From a669ff2e1abf63ac4f60f32e30a416602417940d Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:03:28 +0000 Subject: [PATCH 001/128] Creating branch for staging iSER target git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5228 d57e44dd-8a1f-0410-8b47-8ef2f437770f From e885ce50f3595bbeeec55ce88369b4da90600364 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:11:50 +0000 Subject: [PATCH 002/128] [PATCH 1/9] iscsi: Add iSCSI transport API Need to have transport abstraction in order to be able to add different transport types. In particular TCP and RDMA Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5229 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/include/iscsit_transport.h | 65 +++++++++++++++++++++++++++ iscsi-scst/kernel/Makefile | 3 +- iscsi-scst/kernel/iscsi.h | 3 ++ iscsi-scst/kernel/iscsit_transport.c | 64 ++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 iscsi-scst/include/iscsit_transport.h create mode 100644 iscsi-scst/kernel/iscsit_transport.c diff --git a/iscsi-scst/include/iscsit_transport.h b/iscsi-scst/include/iscsit_transport.h new file mode 100644 index 000000000..e4e5ff870 --- /dev/null +++ b/iscsi-scst/include/iscsit_transport.h @@ -0,0 +1,65 @@ + +#ifndef __ISCSI_TRANSPORT_H__ +#define __ISCSI_TRANSPORT_H__ + +#include +#include + +#ifdef INSIDE_KERNEL_TREE +#include +#else +#include +#endif + +/* forward declarations */ +struct iscsi_session; +struct iscsi_kern_conn_info; +struct iscsi_conn; + +enum iscsit_transport_type { + ISCSI_TCP, + ISCSI_RDMA, +}; + +struct iscsit_transport { + struct iscsi_cmnd* (*iscsit_alloc_cmd)(struct iscsi_conn *conn, + struct iscsi_cmnd *parent); + void (*iscsit_free_cmd)(struct iscsi_cmnd *cmnd); + void (*iscsit_preprocessing_done)(struct iscsi_cmnd *cmnd); + void (*iscsit_send_data_rsp)(struct iscsi_cmnd *req, u8 *sense, + int sense_len, u8 status, + int send_status); + void (*iscsit_make_conn_wr_active)(struct iscsi_conn *conn); + int (*iscsit_conn_alloc)(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, + struct iscsi_conn **new_conn, + struct iscsit_transport *transport); + int (*iscsit_conn_activate)(struct iscsi_conn *conn); + void (*iscsit_conn_free)(struct iscsi_conn *conn); + void (*iscsit_conn_close)(struct iscsi_conn *conn, int flags); + void (*iscsit_mark_conn_closed)(struct iscsi_conn *conn, int flags); + ssize_t (*iscsit_get_initiator_ip)(struct iscsi_conn *conn, char *buf, + int size); + int (*iscsit_send_locally)(struct iscsi_cmnd *cmnd, + unsigned int cmd_count); + void (*iscsit_set_sense_data)(struct iscsi_cmnd *rsp, + const u8 *sense_buf, int sense_len); + void (*iscsit_set_req_data)(struct iscsi_cmnd *req, + struct iscsi_cmnd *rsp); + int (*iscsit_receive_cmnd_data)(struct iscsi_cmnd *cmnd); + void (*iscsit_close_all_portals)(void); + + unsigned int need_alloc_write_buf:1; + + struct module *owner; + const char name[SCST_MAX_NAME]; + enum iscsit_transport_type transport_type; + struct list_head list; +}; + +extern int iscsit_register_transport(struct iscsit_transport *t); +extern void iscsit_unregister_transport(struct iscsit_transport *t); +extern struct iscsit_transport *iscsit_get_transport(enum iscsit_transport_type type); + +#endif /* __ISCSI_TRANSPORT_H__ */ + diff --git a/iscsi-scst/kernel/Makefile b/iscsi-scst/kernel/Makefile index 9d59402c9..d90babd0d 100644 --- a/iscsi-scst/kernel/Makefile +++ b/iscsi-scst/kernel/Makefile @@ -38,5 +38,6 @@ EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions obj-m += iscsi-scst.o iscsi-scst-objs := iscsi.o nthread.o config.o digest.o \ - conn.o session.o target.o event.o param.o + conn.o session.o target.o event.o param.o \ + iscsit_transport.o diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 7b7b05f07..38f975a14 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -33,6 +33,7 @@ #endif #include "iscsi_hdr.h" #include "iscsi_dbg.h" +#include "iscsit_transport.h" #define iscsi_sense_crc_error ABORTED_COMMAND, 0x47, 0x05 #define iscsi_sense_unexpected_unsolicited_data ABORTED_COMMAND, 0x0C, 0x0C @@ -191,6 +192,8 @@ struct iscsi_session { #define ISCSI_CONN_WR_STATE_PROCESSING 3 struct iscsi_conn { + struct iscsit_transport *transport; + struct iscsi_session *session; /* owning session */ /* Both protected by session->sn_lock */ diff --git a/iscsi-scst/kernel/iscsit_transport.c b/iscsi-scst/kernel/iscsit_transport.c new file mode 100644 index 000000000..debf87edb --- /dev/null +++ b/iscsi-scst/kernel/iscsit_transport.c @@ -0,0 +1,64 @@ + +#include +#include "iscsit_transport.h" +#include "iscsi.h" + +static LIST_HEAD(transport_list); +static DEFINE_MUTEX(transport_mutex); + +static struct iscsit_transport *__iscsit_get_transport(enum iscsit_transport_type type) +{ + struct iscsit_transport *t; + + list_for_each_entry(t, &transport_list, list) { + if (t->transport_type == type) + return t; + } + + return NULL; +} + +struct iscsit_transport *iscsit_get_transport(enum iscsit_transport_type type) +{ + struct iscsit_transport *t; + + mutex_lock(&transport_mutex); + t = __iscsit_get_transport(type); + mutex_unlock(&transport_mutex); + + return t; +} + +int iscsit_register_transport(struct iscsit_transport *t) +{ + struct iscsit_transport *tmp; + int ret = 0; + + INIT_LIST_HEAD(&t->list); + + mutex_lock(&transport_mutex); + tmp = __iscsit_get_transport(t->transport_type); + if (tmp) { + PRINT_ERROR("Unable to register transport type %d - Already registered\n", + t->transport_type); + ret = -EEXIST; + } else { + list_add_tail(&t->list, &transport_list); + PRINT_INFO("Registered iSCSI transport: %s\n", t->name); + } + mutex_unlock(&transport_mutex); + + return ret; +} +EXPORT_SYMBOL(iscsit_register_transport); + +void iscsit_unregister_transport(struct iscsit_transport *t) +{ + mutex_lock(&transport_mutex); + list_del(&t->list); + mutex_unlock(&transport_mutex); + + PRINT_INFO("Unregistered iSCSI transport: %s\n", t->name); +} +EXPORT_SYMBOL(iscsit_unregister_transport); + From 52e04267038cb71a693df12b70d0a99f7b149368 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:13:12 +0000 Subject: [PATCH 003/128] [PATCH 2/9] iscsi: Move TCP code over to transport API Replace iscsi-tcp specific calls with transport API calls in order to be able to override them with iser implementation specifics. Make iscsi-tcp specific debug print into a general print Refactor conn close code to work with isert Only allocate RX data in NOP for iscsi-tcp In case of iser, the data is already received Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5230 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/conn.c | 90 +++++------- iscsi-scst/kernel/iscsi.c | 273 +++++++++++++++++++++++++----------- iscsi-scst/kernel/iscsi.h | 9 +- iscsi-scst/kernel/nthread.c | 42 +++--- iscsi-scst/kernel/target.c | 9 ++ 5 files changed, 269 insertions(+), 154 deletions(-) diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 3e14fe82c..e3396ddd5 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -21,6 +21,7 @@ #include "iscsi.h" #include "digest.h" +#include "iscsit_transport.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) #if defined(CONFIG_LOCKDEP) && !defined(CONFIG_SCST_PROC) @@ -158,42 +159,7 @@ struct kobj_type iscsi_conn_ktype = { static ssize_t iscsi_get_initiator_ip(struct iscsi_conn *conn, char *buf, int size) { - int pos; - struct sock *sk; - - TRACE_ENTRY(); - - sk = conn->sock->sk; - switch (sk->sk_family) { - case AF_INET: - pos = scnprintf(buf, size, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) - "%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr)); -#else - "%pI4", &inet_sk(sk)->inet_daddr); -#endif - break; - case AF_INET6: -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) - pos = scnprintf(buf, size, - "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", - NIP6(inet6_sk(sk)->daddr)); -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - pos = scnprintf(buf, size, "[%p6]", &inet6_sk(sk)->daddr); -#else - pos = scnprintf(buf, size, "[%p6]", &sk->sk_v6_daddr); -#endif -#endif - break; - default: - pos = scnprintf(buf, size, "Unknown family %d", - sk->sk_family); - break; - } - - TRACE_EXIT_RES(pos); - return pos; + return conn->transport->iscsit_get_initiator_ip(conn, buf, size); } static ssize_t iscsi_conn_ip_show(struct kobject *kobj, @@ -431,7 +397,7 @@ void iscsi_make_conn_wr_active(struct iscsi_conn *conn) return; } -void __mark_conn_closed(struct iscsi_conn *conn, int flags) +void iscsi_tcp_mark_conn_closed(struct iscsi_conn *conn, int flags) { spin_lock_bh(&conn->conn_thr_pool->rd_lock); conn->closing = 1; @@ -444,6 +410,11 @@ void __mark_conn_closed(struct iscsi_conn *conn, int flags) iscsi_make_conn_rd_active(conn); } +void __mark_conn_closed(struct iscsi_conn *conn, int flags) +{ + conn->transport->iscsit_mark_conn_closed(conn, flags); +} + void mark_conn_closed(struct iscsi_conn *conn) { __mark_conn_closed(conn, ISCSI_CONN_ACTIVE_CLOSE); @@ -746,7 +717,7 @@ void conn_reinst_finished(struct iscsi_conn *conn) return; } -static void conn_activate(struct iscsi_conn *conn) +int conn_activate(struct iscsi_conn *conn) { TRACE_MGMT_DBG("Enabling conn %p", conn); @@ -772,7 +743,7 @@ static void conn_activate(struct iscsi_conn *conn) */ __iscsi_state_change(conn->sock->sk); - return; + return 0; } /* @@ -814,8 +785,19 @@ out: return res; } +void iscsi_tcp_conn_free(struct iscsi_conn *conn) +{ + fput(conn->file); + conn->file = NULL; + conn->sock = NULL; + + free_page((unsigned long)conn->read_iov); + + kmem_cache_free(iscsi_conn_cache, conn); +} + /* target_mutex supposed to be locked */ -int conn_free(struct iscsi_conn *conn) +void conn_free(struct iscsi_conn *conn) { struct iscsi_session *session = conn->session; @@ -856,25 +838,18 @@ int conn_free(struct iscsi_conn *conn) list_del(&conn->conn_list_entry); - fput(conn->file); - conn->file = NULL; - conn->sock = NULL; - - free_page((unsigned long)conn->read_iov); - - kmem_cache_free(iscsi_conn_cache, conn); + conn->transport->iscsit_conn_free(conn); if (list_empty(&session->conn_list)) { sBUG_ON(session->sess_reinst_successor != NULL); session_free(session, true); } - - return 0; } /* target_mutex supposed to be locked */ -static int iscsi_conn_alloc(struct iscsi_session *session, - struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn) +int iscsi_conn_alloc(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn, + struct iscsit_transport *t) { struct iscsi_conn *conn; int res = 0; @@ -892,6 +867,8 @@ static int iscsi_conn_alloc(struct iscsi_session *session, TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn, (long long unsigned int)session->sid, info->cid); + conn->transport = t; + /* Changing it, change ISCSI_CONN_IOV_MAX as well !! */ conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL); if (conn->read_iov == NULL) { @@ -986,6 +963,7 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info) struct iscsi_conn *conn, *new_conn = NULL; int err; bool reinstatement = false; + struct iscsit_transport *t; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); @@ -1001,7 +979,13 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info) goto out; } - err = iscsi_conn_alloc(session, info, &new_conn); + t = iscsit_get_transport(ISCSI_TCP); + if (!t) { + err = -ENOENT; + goto out; + } + + err = t->iscsit_conn_alloc(session, info, &new_conn, t); if (err != 0) goto out; @@ -1013,7 +997,7 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info) __mark_conn_closed(conn, 0); } - conn_activate(new_conn); + err = t->iscsit_conn_activate(new_conn); out: return err; diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index b85a215db..10d876715 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -27,6 +27,7 @@ #include "iscsi.h" #include "digest.h" +#include "iscsit_transport.h" #ifndef GENERATING_UPSTREAM_PATCH #if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) @@ -242,7 +243,7 @@ static struct iscsi_cmnd *iscsi_create_tm_clone(struct iscsi_cmnd *cmnd) TRACE_ENTRY(); - tm_clone = cmnd_alloc(cmnd->conn, NULL); + tm_clone = cmnd->conn->transport->iscsit_alloc_cmd(cmnd->conn, NULL); if (tm_clone != NULL) { set_bit(ISCSI_CMD_ABORTED, &tm_clone->prelim_compl_flags); tm_clone->pdu = cmnd->pdu; @@ -496,10 +497,10 @@ void cmnd_done(struct iscsi_cmnd *cmnd) list_for_each_entry_safe(rsp, t, &cmnd->rsp_cmd_list, rsp_cmd_list_entry) { - cmnd_free(rsp); + cmnd->conn->transport->iscsit_free_cmd(rsp); } - cmnd_free(cmnd); + cmnd->conn->transport->iscsit_free_cmd(cmnd); } else { struct iscsi_cmnd *parent = cmnd->parent_req; @@ -735,7 +736,7 @@ static struct iscsi_cmnd *iscsi_alloc_rsp(struct iscsi_cmnd *parent) TRACE_ENTRY(); - rsp = cmnd_alloc(parent->conn, parent); + rsp = parent->conn->transport->iscsit_alloc_cmd(parent->conn, parent); TRACE_DBG("Adding rsp %p to parent %p", rsp, parent); list_add_tail(&rsp->rsp_cmd_list_entry, &parent->rsp_cmd_list); @@ -797,7 +798,7 @@ static void iscsi_cmnds_init_write(struct list_head *send, int flags) spin_unlock_bh(&conn->write_list_lock); if (flags & ISCSI_INIT_WRITE_WAKE) - iscsi_make_conn_wr_active(conn); + conn->transport->iscsit_make_conn_wr_active(conn); return; } @@ -992,12 +993,25 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) return; } +static void iscsi_tcp_set_sense_data(struct iscsi_cmnd *rsp, + const u8 *sense_buf, int sense_len) +{ + struct scatterlist *sg; + + sg = rsp->sg = rsp->rsp_sg; + rsp->sg_cnt = 2; + rsp->own_sg = 1; + + sg_init_table(sg, 2); + sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr)); + sg_set_buf(&sg[1], sense_buf, sense_len); +} + static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp, int status, const u8 *sense_buf, int sense_len) { struct iscsi_cmnd *req = rsp->parent_req; struct iscsi_scsi_rsp_hdr *rsp_hdr; - struct scatterlist *sg; TRACE_ENTRY(); @@ -1011,16 +1025,11 @@ static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp, if (scst_sense_valid(sense_buf)) { TRACE_DBG("%s", "SENSE VALID"); - sg = rsp->sg = rsp->rsp_sg; - rsp->sg_cnt = 2; - rsp->own_sg = 1; - - sg_init_table(sg, 2); - sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr)); - sg_set_buf(&sg[1], sense_buf, sense_len); - rsp->sense_hdr.length = cpu_to_be16(sense_len); + rsp->conn->transport->iscsit_set_sense_data(rsp, sense_buf, + sense_len); + rsp->pdu.datasize = sizeof(rsp->sense_hdr) + sense_len; rsp->bufflen = rsp->pdu.datasize; } else { @@ -1049,6 +1058,25 @@ static inline struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, return rsp; } +static void iscsi_tcp_send_data_rsp(struct iscsi_cmnd *req, u8 *sense, + int sense_len, u8 status, + int is_send_status) +{ + if ((status != SAM_STAT_CHECK_CONDITION) && + ((cmnd_hdr(req)->flags & (ISCSI_CMD_WRITE|ISCSI_CMD_READ)) != + (ISCSI_CMD_WRITE|ISCSI_CMD_READ))) { + send_data_rsp(req, status, is_send_status); + } else { + struct iscsi_cmnd *rsp; + send_data_rsp(req, 0, 0); + if (is_send_status) { + rsp = create_status_rsp(req, status, sense, + sense_len); + iscsi_cmnd_init_write(rsp, 0); + } + } +} + /* * Initializes data receive fields. Can be called only when they have not been * initialized yet. @@ -1737,7 +1765,7 @@ static int nop_out_start(struct iscsi_cmnd *cmnd) size = cmnd->pdu.datasize; - if (size) { + if (size && !conn->session->sess_params.rdma_extensions) { conn->read_msg.msg_iov = conn->read_iov; if (cmnd->pdu.bhs.itt != ISCSI_RESERVED_TAG) { struct scatterlist *sg; @@ -1978,7 +2006,8 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) scst_cmd_set_expected_out_transfer_len(scst_cmd, be32_to_cpu(req_hdr->data_length)); #if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) - scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd); + if (conn->transport->need_alloc_write_buf) + scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd); #endif } } else if (req_hdr->flags & ISCSI_CMD_READ) { @@ -1986,7 +2015,8 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) scst_cmd_set_expected(scst_cmd, dir, be32_to_cpu(req_hdr->data_length)); #if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) - scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd); + if (conn->transport->need_alloc_write_buf) + scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd); #endif } else if (req_hdr->flags & ISCSI_CMD_WRITE) { dir = SCST_DATA_WRITE; @@ -2053,7 +2083,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) scst_cmd_init_stage1_done(scst_cmd, SCST_CONTEXT_DIRECT, 0); if (req->scst_state != ISCSI_CMD_STATE_RX_CMD) - res = cmnd_rx_continue(req); + res = req->conn->transport->iscsit_receive_cmnd_data(req); else { TRACE_DBG("Delaying req %p post processing (scst_state %d)", req, req->scst_state); @@ -2664,6 +2694,14 @@ reject: return; } +static void iscsi_tcp_set_req_data(struct iscsi_cmnd *req, + struct iscsi_cmnd *rsp) +{ + rsp->sg = req->sg; + rsp->sg_cnt = req->sg_cnt; + rsp->bufflen = req->bufflen; +} + static void nop_out_exec(struct iscsi_cmnd *req) { struct iscsi_cmnd *rsp; @@ -2687,11 +2725,8 @@ static void nop_out_exec(struct iscsi_cmnd *req) else sBUG_ON(req->sg != NULL); - if (req->sg) { - rsp->sg = req->sg; - rsp->sg_cnt = req->sg_cnt; - rsp->bufflen = req->bufflen; - } + if (req->bufflen) + req->conn->transport->iscsit_set_req_data(req, rsp); /* We already checked it in check_segment_length() */ sBUG_ON(get_pgcnt(req->pdu.datasize, 0) > ISCSI_CONN_IOV_MAX); @@ -3176,6 +3211,47 @@ out: return; } +static ssize_t iscsi_tcp_get_initiator_ip(struct iscsi_conn *conn, + char *buf, int size) +{ + int pos; + struct sock *sk; + + TRACE_ENTRY(); + + sk = conn->sock->sk; + switch (sk->sk_family) { + case AF_INET: + pos = scnprintf(buf, size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + "%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr)); +#else + "%pI4", &inet_sk(sk)->inet_daddr); +#endif + break; + case AF_INET6: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + pos = scnprintf(buf, size, + "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", + NIP6(inet6_sk(sk)->daddr)); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + pos = scnprintf(buf, size, "[%p6]", &inet6_sk(sk)->daddr); +#else + pos = scnprintf(buf, size, "[%p6]", &sk->sk_v6_daddr); +#endif +#endif + break; + default: + pos = scnprintf(buf, size, "Unknown family %d", + sk->sk_family); + break; + } + + TRACE_EXIT_RES(pos); + return pos; +} + #if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) static int iscsi_alloc_data_buf(struct scst_cmd *cmd) { @@ -3192,11 +3268,8 @@ static int iscsi_alloc_data_buf(struct scst_cmd *cmd) } #endif -static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd) +static void iscsi_tcp_preprocessing_done(struct iscsi_cmnd *req) { - struct iscsi_cmnd *req = (struct iscsi_cmnd *) - scst_cmd_get_tgt_priv(scst_cmd); - TRACE_DBG("req %p", req); if (req->conn->rx_task == current) @@ -3223,8 +3296,14 @@ static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd) } cmnd_put(req); } +} - return; +static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd) +{ + struct iscsi_cmnd *req = (struct iscsi_cmnd *) + scst_cmd_get_tgt_priv(scst_cmd); + + req->conn->transport->iscsit_preprocessing_done(req); } /* No locks */ @@ -3285,6 +3364,52 @@ static void iscsi_try_local_processing(struct iscsi_cmnd *req) return; } +static int iscsi_tcp_send_locally(struct iscsi_cmnd *req, + unsigned int cmd_count) +{ + struct iscsi_conn *conn = req->conn; + struct iscsi_cmnd *wr_rsp, *our_rsp; + int ret = 0; + + /* + * There's no need for protection, since we are not going to + * dereference them. + */ + wr_rsp = list_first_entry(&conn->write_list, struct iscsi_cmnd, + write_list_entry); + our_rsp = list_first_entry(&req->rsp_cmd_list, struct iscsi_cmnd, + rsp_cmd_list_entry); + if (wr_rsp == our_rsp) { + /* + * This is our rsp, so let's try to process it locally to + * decrease latency. We need to call pre_release before + * processing to handle some error recovery cases. + */ + if (cmd_count <= 2) { + req_cmnd_pre_release(req); + iscsi_try_local_processing(req); + cmnd_put(req); + } else { + /* + * There's too much backend activity, so it could be + * better to push it to the write thread. + */ + ret = 1; + } + } else + ret = 1; + + return ret; +} + +static void iscsi_tcp_conn_close(struct iscsi_conn *conn, int flags) +{ + if (!flags) + conn->sock->sk->sk_prot->disconnect(conn->sock->sk, 0); + else + conn->sock->ops->shutdown(conn->sock, flags); +} + static int iscsi_xmit_response(struct scst_cmd *scst_cmd) { int is_send_status = scst_cmd_get_is_send_status(scst_cmd); @@ -3294,7 +3419,6 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd) int status = scst_cmd_get_status(scst_cmd); u8 *sense = scst_cmd_get_sense_buffer(scst_cmd); int sense_len = scst_cmd_get_sense_buffer_len(scst_cmd); - struct iscsi_cmnd *wr_rsp, *our_rsp; EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd)); @@ -3369,19 +3493,9 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd) * so status is valid here, but in future that could change. * ToDo */ - if ((status != SAM_STAT_CHECK_CONDITION) && - ((cmnd_hdr(req)->flags & (ISCSI_CMD_WRITE|ISCSI_CMD_READ)) != - (ISCSI_CMD_WRITE|ISCSI_CMD_READ))) { - send_data_rsp(req, status, is_send_status); - } else { - struct iscsi_cmnd *rsp; - send_data_rsp(req, 0, 0); - if (is_send_status) { - rsp = create_status_rsp(req, status, sense, - sense_len); - iscsi_cmnd_init_write(rsp, 0); - } - } + req->conn->transport->iscsit_send_data_rsp(req, sense, + sense_len, status, + is_send_status); } else if (is_send_status) { struct iscsi_cmnd *rsp; rsp = create_status_rsp(req, status, sense, sense_len); @@ -3392,32 +3506,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd) sBUG(); #endif - /* - * There's no need for protection, since we are not going to - * dereference them. - */ - wr_rsp = list_first_entry(&conn->write_list, struct iscsi_cmnd, - write_list_entry); - our_rsp = list_first_entry(&req->rsp_cmd_list, struct iscsi_cmnd, - rsp_cmd_list_entry); - if (wr_rsp == our_rsp) { - /* - * This is our rsp, so let's try to process it locally to - * decrease latency. We need to call pre_release before - * processing to handle some error recovery cases. - */ - if (scst_get_active_cmd_count(scst_cmd) <= 2) { - req_cmnd_pre_release(req); - iscsi_try_local_processing(req); - cmnd_put(req); - } else { - /* - * There's too much backend activity, so it could be - * better to push it to the write thread. - */ - goto out_push_to_wr_thread; - } - } else + if (conn->transport->iscsit_send_locally(req, scst_get_active_cmd_count(scst_cmd))) goto out_push_to_wr_thread; out: @@ -3426,7 +3515,7 @@ out: out_push_to_wr_thread: TRACE_DBG("Waking up write thread (conn %p)", conn); req_cmnd_release(req); - iscsi_make_conn_wr_active(conn); + conn->transport->iscsit_make_conn_wr_active(conn); goto out; } @@ -3610,7 +3699,6 @@ static int iscsi_scsi_aen(struct scst_aen *aen) bool found; struct iscsi_cmnd *fake_req, *rsp; struct iscsi_async_msg_hdr *rsp_hdr; - struct scatterlist *sg; TRACE_ENTRY(); @@ -3633,7 +3721,7 @@ static int iscsi_scsi_aen(struct scst_aen *aen) } /* Create a fake request */ - fake_req = cmnd_alloc(conn, NULL); + fake_req = conn->transport->iscsit_alloc_cmd(conn, NULL); if (fake_req == NULL) { PRINT_ERROR("%s", "Unable to alloc fake AEN request"); goto out_err_unlock; @@ -3658,15 +3746,10 @@ static int iscsi_scsi_aen(struct scst_aen *aen) rsp_hdr->ffffffff = cpu_to_be32(0xffffffff); rsp_hdr->async_event = ISCSI_ASYNC_SCSI; - sg = rsp->sg = rsp->rsp_sg; - rsp->sg_cnt = 2; - rsp->own_sg = 1; - - sg_init_table(sg, 2); - sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr)); - sg_set_buf(&sg[1], sense, sense_len); - rsp->sense_hdr.length = cpu_to_be16(sense_len); + + rsp->conn->transport->iscsit_set_sense_data(rsp, sense, sense_len); + rsp->pdu.datasize = sizeof(rsp->sense_hdr) + sense_len; rsp->bufflen = rsp->pdu.datasize; @@ -3788,7 +3871,7 @@ void iscsi_send_nop_in(struct iscsi_conn *conn) TRACE_ENTRY(); - req = cmnd_alloc(conn, NULL); + req = conn->transport->iscsit_alloc_cmd(conn, NULL); if (req == NULL) { PRINT_ERROR("%s", "Unable to alloc fake Nop-In request"); goto out_err; @@ -3930,6 +4013,30 @@ struct scst_tgt_template iscsi_template = { .get_scsi_transport_version = iscsi_get_scsi_transport_version, }; +static struct iscsit_transport iscsi_tcp_transport = { + .owner = THIS_MODULE, + .name = "iSCSI-TCP", + .transport_type = ISCSI_TCP, +#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) + .need_alloc_write_buf = 1, +#endif + .iscsit_conn_alloc = iscsi_conn_alloc, + .iscsit_conn_activate = conn_activate, + .iscsit_conn_free = iscsi_tcp_conn_free, + .iscsit_alloc_cmd = cmnd_alloc, + .iscsit_free_cmd = cmnd_free, + .iscsit_preprocessing_done = iscsi_tcp_preprocessing_done, + .iscsit_send_data_rsp = iscsi_tcp_send_data_rsp, + .iscsit_make_conn_wr_active = iscsi_make_conn_wr_active, + .iscsit_mark_conn_closed = iscsi_tcp_mark_conn_closed, + .iscsit_conn_close = iscsi_tcp_conn_close, + .iscsit_get_initiator_ip = iscsi_tcp_get_initiator_ip, + .iscsit_send_locally = iscsi_tcp_send_locally, + .iscsit_set_sense_data = iscsi_tcp_set_sense_data, + .iscsit_set_req_data = iscsi_tcp_set_req_data, + .iscsit_receive_cmnd_data = cmnd_rx_continue, +}; + static void __iscsi_threads_pool_put(struct iscsi_thread_pool *p) { struct iscsi_thread *t, *tt; @@ -4110,6 +4217,10 @@ static int __init iscsi_init(void) PRINT_INFO("iSCSI SCST Target - version %s", ISCSI_VERSION_STRING); + err = iscsit_register_transport(&iscsi_tcp_transport); + if (err) + goto out; + dummy_page = alloc_pages(GFP_KERNEL, 0); if (dummy_page == NULL) { PRINT_ERROR("%s", "Dummy page allocation failed"); @@ -4260,6 +4371,8 @@ static void __exit iscsi_exit(void) scst_unregister_target_template(&iscsi_template); + iscsit_unregister_transport(&iscsi_tcp_transport); + #if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) net_set_get_put_page_callbacks(NULL, NULL); #endif diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 38f975a14..4ca1c988f 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -555,7 +555,7 @@ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16); extern void conn_reinst_finished(struct iscsi_conn *); extern int __add_conn(struct iscsi_session *, struct iscsi_kern_conn_info *); extern int __del_conn(struct iscsi_session *, struct iscsi_kern_conn_info *); -extern int conn_free(struct iscsi_conn *); +extern void conn_free(struct iscsi_conn *); extern void iscsi_make_conn_rd_active(struct iscsi_conn *conn); #define ISCSI_CONN_ACTIVE_CLOSE 1 #define ISCSI_CONN_DELETING 2 @@ -829,4 +829,11 @@ static inline void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn) {} static inline void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn) {} #endif +extern int iscsi_conn_alloc(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn, + struct iscsit_transport *t); + +extern int conn_activate(struct iscsi_conn *conn); +extern void iscsi_tcp_mark_conn_closed(struct iscsi_conn *conn, int flags); +extern void iscsi_tcp_conn_free(struct iscsi_conn *conn); #endif /* __ISCSI_H__ */ diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 2d4314b6b..404de998e 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -24,6 +24,7 @@ #include "iscsi.h" #include "digest.h" +#include "iscsit_transport.h" /* Read data states */ enum rx_state { @@ -408,10 +409,10 @@ static void close_conn(struct iscsi_conn *conn) if (conn->active_close) { /* We want all our already send operations to complete */ - conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN); + conn->transport->iscsit_conn_close(conn, RCV_SHUTDOWN); } else { - conn->sock->ops->shutdown(conn->sock, - RCV_SHUTDOWN|SEND_SHUTDOWN); + conn->transport->iscsit_conn_close(conn, + RCV_SHUTDOWN|SEND_SHUTDOWN); } mutex_lock(&session->target->target_mutex); @@ -487,15 +488,13 @@ static void close_conn(struct iscsi_conn *conn) } } - iscsi_make_conn_wr_active(conn); + conn->transport->iscsit_make_conn_wr_active(conn); /* That's for active close only, actually */ if (time_after(jiffies, start_waiting + CONN_WAIT_TIMEOUT) && !wait_expired) { - TRACE_CONN_CLOSE("Wait time expired (conn %p, " - "sk_state %d)", - conn, conn->sock->sk->sk_state); - conn->sock->ops->shutdown(conn->sock, SEND_SHUTDOWN); + TRACE_CONN_CLOSE("Wait time expired (conn %p)", conn); + conn->transport->iscsit_conn_close(conn, SEND_SHUTDOWN); wait_expired = 1; shut_start_waiting = jiffies; } @@ -505,9 +504,8 @@ static void close_conn(struct iscsi_conn *conn) conn->deleting ? CONN_DEL_SHUT_TIMEOUT : CONN_REG_SHUT_TIMEOUT)) { TRACE_CONN_CLOSE("Wait time after shutdown expired " - "(conn %p, sk_state %d)", conn, - conn->sock->sk->sk_state); - conn->sock->sk->sk_prot->disconnect(conn->sock->sk, 0); + "(conn %p)", conn); + conn->transport->iscsit_conn_close(conn, 0); shut_expired = 1; } @@ -523,17 +521,21 @@ static void close_conn(struct iscsi_conn *conn) trace_conn_close(conn); - /* It might never be called for being closed conn */ - __iscsi_write_space_ready(conn); + if (!conn->session->sess_params.rdma_extensions) { + /* It might never be called for being closed conn */ + __iscsi_write_space_ready(conn); - iscsi_check_closewait(conn); + iscsi_check_closewait(conn); + } } - write_lock_bh(&conn->sock->sk->sk_callback_lock); - conn->sock->sk->sk_state_change = conn->old_state_change; - conn->sock->sk->sk_data_ready = conn->old_data_ready; - conn->sock->sk->sk_write_space = conn->old_write_space; - write_unlock_bh(&conn->sock->sk->sk_callback_lock); + if (!conn->session->sess_params.rdma_extensions) { + write_lock_bh(&conn->sock->sk->sk_callback_lock); + conn->sock->sk->sk_state_change = conn->old_state_change; + conn->sock->sk->sk_data_ready = conn->old_data_ready; + conn->sock->sk->sk_write_space = conn->old_write_space; + write_unlock_bh(&conn->sock->sk->sk_callback_lock); + } while (1) { bool t; @@ -832,7 +834,7 @@ static int process_read_io(struct iscsi_conn *conn, int *closed) switch (conn->read_state) { case RX_INIT_BHS: EXTRACHECKS_BUG_ON(conn->read_cmnd != NULL); - cmnd = cmnd_alloc(conn, NULL); + cmnd = conn->transport->iscsit_alloc_cmd(conn, NULL); conn->read_cmnd = cmnd; iscsi_conn_init_read(cmnd->conn, (void __force __user *)&cmnd->pdu.bhs, diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index 848c4c981..bef604c3d 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -349,6 +349,7 @@ void target_del_all_sess(struct iscsi_target *target, int flags) void target_del_all(void) { + struct iscsit_transport *transport; struct iscsi_target *target, *t; bool first = true; @@ -356,6 +357,14 @@ void target_del_all(void) TRACE_MGMT_DBG("%s", "Deleting all targets"); + transport = iscsit_get_transport(ISCSI_TCP); + if (transport && transport->iscsit_close_all_portals) + transport->iscsit_close_all_portals(); + + transport = iscsit_get_transport(ISCSI_RDMA); + if (transport && transport->iscsit_close_all_portals) + transport->iscsit_close_all_portals(); + /* Not the best, ToDo */ while (1) { mutex_lock(&target_mgmt_mutex); From de70fa73d9f9c9c209a2e21fc7ce0f92cc19c6b8 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:15:16 +0000 Subject: [PATCH 004/128] [PATCH 3/9] iscsi: Export some functions needed by isert and refactor code for reuse Refactor code to reuse iscsi command init in isert Refactor conn allocation code to be able to reuse it in isert Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5231 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/conn.c | 77 +++++++++------ iscsi-scst/kernel/iscsi.c | 184 ++++++++++++++++++++++-------------- iscsi-scst/kernel/iscsi.h | 14 +++ iscsi-scst/kernel/nthread.c | 6 +- iscsi-scst/kernel/target.c | 1 + 5 files changed, 176 insertions(+), 106 deletions(-) diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index e3396ddd5..42cce27ff 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -237,7 +237,7 @@ static void conn_sysfs_del(struct iscsi_conn *conn) return; } -static int conn_sysfs_add(struct iscsi_conn *conn) +int conn_sysfs_add(struct iscsi_conn *conn) { int res; struct iscsi_session *session = conn->session; @@ -311,7 +311,7 @@ out_err: conn_sysfs_del(conn); goto out; } - +EXPORT_SYMBOL(conn_sysfs_add); #endif /* CONFIG_SCST_PROC */ /* target_mutex supposed to be locked */ @@ -419,6 +419,7 @@ void mark_conn_closed(struct iscsi_conn *conn) { __mark_conn_closed(conn, ISCSI_CONN_ACTIVE_CLOSE); } +EXPORT_SYMBOL(mark_conn_closed); static void __iscsi_state_change(struct sock *sk) { @@ -846,35 +847,11 @@ void conn_free(struct iscsi_conn *conn) } } -/* target_mutex supposed to be locked */ -int iscsi_conn_alloc(struct iscsi_session *session, - struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn, - struct iscsit_transport *t) +int iscsi_init_conn(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, + struct iscsi_conn *conn) { - struct iscsi_conn *conn; - int res = 0; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - lockdep_assert_held(&session->target->target_mutex); -#endif - - conn = kmem_cache_zalloc(iscsi_conn_cache, GFP_KERNEL); - if (!conn) { - res = -ENOMEM; - goto out_err; - } - - TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn, - (long long unsigned int)session->sid, info->cid); - - conn->transport = t; - - /* Changing it, change ISCSI_CONN_IOV_MAX as well !! */ - conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL); - if (conn->read_iov == NULL) { - res = -ENOMEM; - goto out_err_free_conn; - } + int res; atomic_set(&conn->conn_ref_cnt, 0); conn->session = session; @@ -890,7 +867,7 @@ int iscsi_conn_alloc(struct iscsi_session *session, conn->ddigest_type = session->sess_params.data_digest; res = digest_init(conn); if (res != 0) - goto out_free_iov; + return res; conn->target = session->target; spin_lock_init(&conn->cmd_list_lock); @@ -925,6 +902,44 @@ int iscsi_conn_alloc(struct iscsi_session *session, conn->nop_in_interval + ISCSI_ADD_SCHED_TIME); } + return 0; +} +EXPORT_SYMBOL(iscsi_init_conn); + +/* target_mutex supposed to be locked */ +int iscsi_conn_alloc(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn, + struct iscsit_transport *t) +{ + struct iscsi_conn *conn; + int res = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&session->target->target_mutex); +#endif + + conn = kmem_cache_zalloc(iscsi_conn_cache, GFP_KERNEL); + if (!conn) { + res = -ENOMEM; + goto out_err; + } + + TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn, + (long long unsigned int)session->sid, info->cid); + + conn->transport = t; + + /* Changing it, change ISCSI_CONN_IOV_MAX as well !! */ + conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL); + if (conn->read_iov == NULL) { + res = -ENOMEM; + goto out_err_free_conn; + } + + res = iscsi_init_conn(session, info, conn); + if (res != 0) + goto out_free_iov; + conn->file = fget(info->fd); res = conn_setup_sock(conn); diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 10d876715..1add43d72 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -65,11 +65,9 @@ static struct scatterlist dummy_sg; static void cmnd_remove_data_wait_hash(struct iscsi_cmnd *cmnd); static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status); static void iscsi_check_send_delayed_tm_resp(struct iscsi_session *sess); -static void req_cmnd_release(struct iscsi_cmnd *req); static int cmnd_insert_data_wait_hash(struct iscsi_cmnd *cmnd); static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags); static void iscsi_set_resid_no_scst_cmd(struct iscsi_cmnd *rsp); -static void iscsi_set_resid(struct iscsi_cmnd *rsp); static void iscsi_set_not_received_data_len(struct iscsi_cmnd *req, unsigned int not_received) @@ -310,14 +308,9 @@ void iscsi_fail_data_waiting_cmnd(struct iscsi_cmnd *cmnd) return; } -struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, - struct iscsi_cmnd *parent) +void iscsi_cmnd_init(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd, + struct iscsi_cmnd *parent) { - struct iscsi_cmnd *cmnd; - - /* ToDo: __GFP_NOFAIL?? */ - cmnd = kmem_cache_zalloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL); - atomic_set(&cmnd->ref_cnt, 1); cmnd->scst_state = ISCSI_CMD_STATE_NEW; cmnd->conn = conn; @@ -326,9 +319,6 @@ struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, if (parent == NULL) { conn_get(conn); -#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) - atomic_set(&cmnd->net_ref_cnt, 0); -#endif INIT_LIST_HEAD(&cmnd->rsp_cmd_list); INIT_LIST_HEAD(&cmnd->rx_ddigest_cmd_list); cmnd->target_task_tag = ISCSI_RESERVED_TAG_CPU32; @@ -337,6 +327,24 @@ struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, list_add_tail(&cmnd->cmd_list_entry, &conn->cmd_list); spin_unlock_bh(&conn->cmd_list_lock); } +} +EXPORT_SYMBOL(iscsi_cmnd_init); + +struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, + struct iscsi_cmnd *parent) +{ + struct iscsi_cmnd *cmnd; + + /* ToDo: __GFP_NOFAIL?? */ + cmnd = kmem_cache_zalloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL); + + iscsi_cmnd_init(conn, cmnd, parent); + + if (parent == NULL) { +#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) + atomic_set(&cmnd->net_ref_cnt, 0); +#endif + } TRACE_DBG("conn %p, parent %p, cmnd %p", conn, parent, cmnd); return cmnd; @@ -533,6 +541,7 @@ void cmnd_done(struct iscsi_cmnd *cmnd) TRACE_EXIT(); return; } +EXPORT_SYMBOL(cmnd_done); /* * Corresponding conn may also get destroyed after this function, except only @@ -635,7 +644,7 @@ out: return; } -static void req_cmnd_pre_release(struct iscsi_cmnd *req) +void req_cmnd_pre_release(struct iscsi_cmnd *req) { struct iscsi_cmnd *c, *t; @@ -695,12 +704,13 @@ static void req_cmnd_pre_release(struct iscsi_cmnd *req) TRACE_EXIT(); return; } +EXPORT_SYMBOL(req_cmnd_pre_release); /* * Corresponding conn may also get destroyed after this function, except only * if it's called from the read thread! */ -static void req_cmnd_release(struct iscsi_cmnd *req) +void req_cmnd_release(struct iscsi_cmnd *req) { TRACE_ENTRY(); @@ -710,6 +720,7 @@ static void req_cmnd_release(struct iscsi_cmnd *req) TRACE_EXIT(); return; } +EXPORT_SYMBOL(req_cmnd_release); /* * Corresponding conn may also get destroyed after this function, except only @@ -729,6 +740,7 @@ void rsp_cmnd_release(struct iscsi_cmnd *cmnd) cmnd_put(cmnd); return; } +EXPORT_SYMBOL(rsp_cmnd_release); static struct iscsi_cmnd *iscsi_alloc_rsp(struct iscsi_cmnd *parent) { @@ -877,7 +889,7 @@ static void iscsi_set_resid_no_scst_cmd(struct iscsi_cmnd *rsp) return; } -static void iscsi_set_resid(struct iscsi_cmnd *rsp) +void iscsi_set_resid(struct iscsi_cmnd *rsp) { struct iscsi_cmnd *req = rsp->parent_req; struct scst_cmd *scst_cmd = req->scst_cmd; @@ -931,6 +943,7 @@ out: TRACE_EXIT(); return; } +EXPORT_SYMBOL(iscsi_set_resid); static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status) { @@ -1041,7 +1054,7 @@ static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp, return; } -static inline struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, +struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, int status, const u8 *sense_buf, int sense_len) { struct iscsi_cmnd *rsp; @@ -1057,6 +1070,7 @@ static inline struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, TRACE_EXIT_HRES((unsigned long)rsp); return rsp; } +EXPORT_SYMBOL(create_status_rsp); static void iscsi_tcp_send_data_rsp(struct iscsi_cmnd *req, u8 *sense, int sense_len, u8 status, @@ -1252,7 +1266,7 @@ static inline int iscsi_get_allowed_cmds(struct iscsi_session *sess) return res; } -static __be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn) +__be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn) { struct iscsi_conn *conn = cmnd->conn; struct iscsi_session *sess = conn->session; @@ -1271,6 +1285,7 @@ static __be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn) spin_unlock(&sess->sn_lock); return res; } +EXPORT_SYMBOL(cmnd_set_sn); /* Called under sn_lock */ static void update_stat_sn(struct iscsi_cmnd *cmnd) @@ -1822,16 +1837,90 @@ out: return err; } -int cmnd_rx_continue(struct iscsi_cmnd *req) +int iscsi_cmnd_set_write_buf(struct iscsi_cmnd *req) { struct iscsi_conn *conn = req->conn; struct iscsi_session *session = conn->session; struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); struct scst_cmd *scst_cmd = req->scst_cmd; - scst_data_direction dir; bool unsolicited_data_expected = false; int res = 0; + req->bufflen = scst_cmd_get_write_fields(scst_cmd, &req->sg, + &req->sg_cnt); + unsolicited_data_expected = !(req_hdr->flags & ISCSI_CMD_FINAL); + + if (unlikely(session->sess_params.initial_r2t && + unsolicited_data_expected)) { + PRINT_ERROR("Initiator %s violated negotiated " + "parameters: initial R2T is required (ITT %x, " + "op %x)", session->initiator_name, + req->pdu.bhs.itt, req_hdr->scb[0]); + res = -EINVAL; + goto out_close; + } + + if (unlikely(!session->sess_params.immediate_data && + req->pdu.datasize)) { + PRINT_ERROR("Initiator %s violated negotiated " + "parameters: forbidden immediate data sent " + "(ITT %x, op %x)", session->initiator_name, + req->pdu.bhs.itt, req_hdr->scb[0]); + res = -EINVAL; + goto out_close; + } + + if (unlikely(session->sess_params.first_burst_length < req->pdu.datasize)) { + PRINT_ERROR("Initiator %s violated negotiated " + "parameters: immediate data len (%d) > " + "first_burst_length (%d) (ITT %x, op %x)", + session->initiator_name, + req->pdu.datasize, + session->sess_params.first_burst_length, + req->pdu.bhs.itt, req_hdr->scb[0]); + res = -EINVAL; + goto out_close; + } + + req->r2t_len_to_receive = be32_to_cpu(req_hdr->data_length) - + req->pdu.datasize; + + /* + * In case of residual overflow req->r2t_len_to_receive and + * req->pdu.datasize might be > req->bufflen + */ + + res = cmnd_insert_data_wait_hash(req); + + if (unsolicited_data_expected) { + req->outstanding_r2t = 1; + req->r2t_len_to_send = req->r2t_len_to_receive - + min_t(unsigned int, + session->sess_params.first_burst_length - + req->pdu.datasize, + req->r2t_len_to_receive); + } else + req->r2t_len_to_send = req->r2t_len_to_receive; + + if (likely(res == 0)) + req_add_to_write_timeout_list(req); + +out_close: + return res; +} +EXPORT_SYMBOL(iscsi_cmnd_set_write_buf); + +int cmnd_rx_continue(struct iscsi_cmnd *req) +{ + struct iscsi_conn *conn = req->conn; + struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); + struct scst_cmd *scst_cmd = req->scst_cmd; + scst_data_direction dir; +#ifdef CONFIG_SCST_DEBUG + bool unsolicited_data_expected = false; +#endif + int res = 0; + TRACE_ENTRY(); TRACE_DBG("scsi command: %x", req_hdr->scb[0]); @@ -1857,48 +1946,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req) /* For prelim completed commands sg & K can be already set! */ if (dir & SCST_DATA_WRITE) { - req->bufflen = scst_cmd_get_write_fields(scst_cmd, &req->sg, - &req->sg_cnt); - unsolicited_data_expected = !(req_hdr->flags & ISCSI_CMD_FINAL); - - if (unlikely(session->sess_params.initial_r2t && - unsolicited_data_expected)) { - PRINT_ERROR("Initiator %s violated negotiated " - "parameters: initial R2T is required (ITT %x, " - "op %x)", session->initiator_name, - req->pdu.bhs.itt, req_hdr->scb[0]); - goto out_close; - } - - if (unlikely(!session->sess_params.immediate_data && - req->pdu.datasize)) { - PRINT_ERROR("Initiator %s violated negotiated " - "parameters: forbidden immediate data sent " - "(ITT %x, op %x)", session->initiator_name, - req->pdu.bhs.itt, req_hdr->scb[0]); - goto out_close; - } - - if (unlikely(session->sess_params.first_burst_length < req->pdu.datasize)) { - PRINT_ERROR("Initiator %s violated negotiated " - "parameters: immediate data len (%d) > " - "first_burst_length (%d) (ITT %x, op %x)", - session->initiator_name, - req->pdu.datasize, - session->sess_params.first_burst_length, - req->pdu.bhs.itt, req_hdr->scb[0]); - goto out_close; - } - - req->r2t_len_to_receive = be32_to_cpu(req_hdr->data_length) - - req->pdu.datasize; - - /* - * In case of residual overflow req->r2t_len_to_receive and - * req->pdu.datasize might be > req->bufflen - */ - - res = cmnd_insert_data_wait_hash(req); + res = iscsi_cmnd_set_write_buf(req); if (unlikely(res != 0)) { /* * We have to close connection, because otherwise a data @@ -1909,18 +1957,6 @@ int cmnd_rx_continue(struct iscsi_cmnd *req) goto out_close; } - if (unsolicited_data_expected) { - req->outstanding_r2t = 1; - req->r2t_len_to_send = req->r2t_len_to_receive - - min_t(unsigned int, - session->sess_params.first_burst_length - - req->pdu.datasize, - req->r2t_len_to_receive); - } else - req->r2t_len_to_send = req->r2t_len_to_receive; - - req_add_to_write_timeout_list(req); - if (req->pdu.datasize) { res = cmnd_prepare_recv_pdu(conn, req, 0, req->pdu.datasize); /* For performance better to send R2Ts ASAP */ @@ -3179,6 +3215,7 @@ out: TRACE_EXIT_RES(res); return res; } +EXPORT_SYMBOL(cmnd_rx_start); void cmnd_rx_end(struct iscsi_cmnd *cmnd) { @@ -3210,6 +3247,7 @@ out: TRACE_EXIT(); return; } +EXPORT_SYMBOL(cmnd_rx_end); static ssize_t iscsi_tcp_get_initiator_ip(struct iscsi_conn *conn, char *buf, int size) diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 4ca1c988f..f5593320c 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -607,6 +607,7 @@ extern void target_del_all(void); extern int iscsi_procfs_init(void); extern void iscsi_procfs_exit(void); #else +extern int conn_sysfs_add(struct iscsi_conn *conn); extern const struct attribute *iscsi_attrs[]; extern int iscsi_add_attr(struct iscsi_target *target, const struct iscsi_kern_attr *user_info); @@ -836,4 +837,17 @@ extern int iscsi_conn_alloc(struct iscsi_session *session, extern int conn_activate(struct iscsi_conn *conn); extern void iscsi_tcp_mark_conn_closed(struct iscsi_conn *conn, int flags); extern void iscsi_tcp_conn_free(struct iscsi_conn *conn); +extern void iscsi_cmnd_init(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd, + struct iscsi_cmnd *parent); +extern struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn); +extern void start_close_conn(struct iscsi_conn *conn); +extern __be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn); +extern void iscsi_set_resid(struct iscsi_cmnd *rsp); +extern int iscsi_init_conn(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, struct iscsi_conn *conn); +extern void req_cmnd_pre_release(struct iscsi_cmnd *req); +extern void req_cmnd_release(struct iscsi_cmnd *req); +extern struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, + int status, const u8 *sense_buf, int sense_len); +extern int iscsi_cmnd_set_write_buf(struct iscsi_cmnd *req); #endif /* __ISCSI_H__ */ diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 404de998e..665ee0da6 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -598,7 +598,7 @@ static int close_conn_thr(void *arg) } /* No locks */ -static void start_close_conn(struct iscsi_conn *conn) +void start_close_conn(struct iscsi_conn *conn) { struct task_struct *t; @@ -614,6 +614,7 @@ static void start_close_conn(struct iscsi_conn *conn) TRACE_EXIT(); return; } +EXPORT_SYMBOL(start_close_conn); static inline void iscsi_conn_init_read(struct iscsi_conn *conn, void __user *data, size_t len) @@ -638,7 +639,7 @@ static void iscsi_conn_prepare_read_ahs(struct iscsi_conn *conn, return; } -static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn) +struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn) { struct iscsi_cmnd *cmnd = NULL; @@ -677,6 +678,7 @@ static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn) out: return cmnd; } +EXPORT_SYMBOL(iscsi_get_send_cmnd); /* Returns number of bytes left to receive or <0 for error */ static int do_recv(struct iscsi_conn *conn) diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index bef604c3d..17b83388f 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -346,6 +346,7 @@ void target_del_all_sess(struct iscsi_target *target, int flags) TRACE_EXIT(); return; } +EXPORT_SYMBOL(target_del_all_sess); void target_del_all(void) { From 5357b4c37d5ce05780fdc0e5a88a24b8d55d2058 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:16:47 +0000 Subject: [PATCH 005/128] [PATCH 4/9] isert: Add initial isert code Initial iSER code which also supports MLNX_OFED on RedHat and Ubuntu as well as regular OFED Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5232 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/Makefile | 76 +- iscsi-scst/README.iser_ofed | 117 ++ iscsi-scst/include/isert_scst.h | 24 + iscsi-scst/kernel/conn.c | 5 +- iscsi-scst/kernel/iscsi_dbg.h | 6 + iscsi-scst/kernel/isert-scst/Kconfig | 8 + iscsi-scst/kernel/isert-scst/Makefile | 40 + .../kernel/isert-scst/Makefile.in-kernel | 4 + iscsi-scst/kernel/isert-scst/TODO | 10 + iscsi-scst/kernel/isert-scst/iser.h | 315 ++++ iscsi-scst/kernel/isert-scst/iser_buf.c | 303 ++++ iscsi-scst/kernel/isert-scst/iser_datamover.c | 296 ++++ iscsi-scst/kernel/isert-scst/iser_datamover.h | 60 + iscsi-scst/kernel/isert-scst/iser_global.c | 161 ++ iscsi-scst/kernel/isert-scst/iser_hdr.h | 27 + iscsi-scst/kernel/isert-scst/iser_pdu.c | 568 ++++++ iscsi-scst/kernel/isert-scst/iser_rdma.c | 1544 +++++++++++++++++ iscsi-scst/kernel/isert-scst/isert.c | 536 ++++++ iscsi-scst/kernel/isert-scst/isert.h | 137 ++ iscsi-scst/kernel/isert-scst/isert_dbg.h | 50 + iscsi-scst/kernel/isert-scst/isert_login.c | 940 ++++++++++ 21 files changed, 5225 insertions(+), 2 deletions(-) create mode 100644 iscsi-scst/README.iser_ofed create mode 100644 iscsi-scst/include/isert_scst.h create mode 100644 iscsi-scst/kernel/isert-scst/Kconfig create mode 100644 iscsi-scst/kernel/isert-scst/Makefile create mode 100644 iscsi-scst/kernel/isert-scst/Makefile.in-kernel create mode 100644 iscsi-scst/kernel/isert-scst/TODO create mode 100644 iscsi-scst/kernel/isert-scst/iser.h create mode 100644 iscsi-scst/kernel/isert-scst/iser_buf.c create mode 100644 iscsi-scst/kernel/isert-scst/iser_datamover.c create mode 100644 iscsi-scst/kernel/isert-scst/iser_datamover.h create mode 100644 iscsi-scst/kernel/isert-scst/iser_global.c create mode 100644 iscsi-scst/kernel/isert-scst/iser_hdr.h create mode 100644 iscsi-scst/kernel/isert-scst/iser_pdu.c create mode 100644 iscsi-scst/kernel/isert-scst/iser_rdma.c create mode 100644 iscsi-scst/kernel/isert-scst/isert.c create mode 100644 iscsi-scst/kernel/isert-scst/isert.h create mode 100644 iscsi-scst/kernel/isert-scst/isert_dbg.h create mode 100644 iscsi-scst/kernel/isert-scst/isert_login.c diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index 25cc62e0b..ed288354c 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -24,6 +24,7 @@ RCDIR := /etc/rc.d MANDIR ?= $(PREFIX)/man KMOD := $(shell pwd)/kernel INCDIR := $(shell pwd)/include +ISERTMOD := $(KMOD)/isert-scst ifeq ($(KVER),) ifeq ($(KDIR),) @@ -38,8 +39,49 @@ endif all: include/iscsi_scst_itf_ver.h progs mods +ISER_SYMVERS:=$(KMOD)/Module.symvers +OFED_CFLAGS:= + +MLNX_OFED:=$(shell if $( ofed_info | grep MLNX_OFED $) >/dev/null 2>/dev/null; then echo true; else echo false; fi) + +ifeq ($(MLNX_OFED),true) + # Whether MLNX_OFED for ubuntu has been installed + MLNX_OFED_IB_UBUNTU_INSTALLED:=$(shell if dpkg -s mlnx-ofed-kernel-dkms >/dev/null 2>/dev/null; then echo true; else echo false; fi) + + # Whether MLNX_OFED for RedHat has been installed + MLNX_OFED_IB_RH_INSTALLED:=$(shell if rpm -q mlnx-ofa_kernel-devel >&/dev/null; then echo true; else echo false; fi) + + # Check if we have custom compiled kernel modules + ifeq ($(MLNX_OFED_IB_RH_INSTALLED),false) + MLNX_OFED_IB_RH_INSTALLED:=$(shell if rpm -q kernel-ib-devel >&/dev/null; then echo true; else echo false; fi) + endif + + ifeq ($(MLNX_OFED_IB_UBUNTU_INSTALLED),true) + OFED_VERS=$(shell dpkg -s mlnx-ofed-kernel-dkms | awk -F\- '/Version/ {print $$1}' | awk '{print $$2}') + OFED_CFLAGS:=-I/var/lib/dkms/mlnx-ofed-kernel/$(OFED_VERS)/build/include -include /var/lib/dkms/mlnx-ofed-kernel/$(OFED_VERS)/build/include/linux/compat-2.6.h + ISER_SYMVERS:="$(ISER_SYMVERS) /var/lib/dkms/mlnx-ofed-kernel/$(OFED_VERS)/build/Module.symvers" + endif + + ifeq ($(MLNX_OFED_IB_RH_INSTALLED),true) + OFED_CFLAGS:=-I/usr/src/ofa_kernel/default/include -include /usr/src/ofa_kernel/default/include/linux/compat-2.6.h + ISER_SYMVERS:="$(ISER_SYMVERS) /usr/src/ofa_kernel/default/Module.symvers" + endif +else + # Whether or not the OFED kernel-ib-devel RPM has been installed. + OFED_KERNEL_IB_DEVEL_RPM_INSTALLED:=$(shell if rpm -q kernel-ib-devel 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi) + + ifeq ($(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED),true) + # Read OFED's config.mk, which contains the definition of the variable + # BACKPORT_INCLUDES. + include /usr/src/ofa_kernel/config.mk + OFED_CFLAGS:=$(shell echo $(BACKPORT_INCLUDES) -I/usr/src/ofa_kernel/include) + ISER_SYMVERS:="$(ISER_SYMVERS) /usr/src/ofa_kernel/Module.symvers" + endif +endif + mods: Modules.symvers Module.symvers $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(KMOD) modules + $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) PRE_CFLAGS="$(OFED_CFLAGS)" KBUILD_EXTRA_SYMBOLS=$(ISER_SYMVERS) modules progs: $(MAKE) -C usr SCST_INC_DIR=$(SCST_INC_DIR) @@ -60,6 +102,8 @@ install: all @eval `sed -n 's/#define UTS_RELEASE /KERNELRELEASE=/p' $(KDIR)/include/linux/version.h $(KDIR)/include/linux/utsrelease.h 2>/dev/null`; \ install -vD -m 644 kernel/iscsi-scst.ko \ $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/iscsi-scst.ko + install -vD -m 644 kernel/isert-scst/isert-scst.ko \ + $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/isert-scst.ko -/sbin/depmod -aq $(KVER) SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) @@ -67,6 +111,7 @@ ifneq ($(SCST_MOD_VERS),) Modules.symvers: $(SCST_DIR)/Modules.symvers echo $(SCST_MOD_VERS) cp $(SCST_DIR)/Modules.symvers kernel/ + cp $(SCST_DIR)/Modules.symvers kernel/isert-scst else .PHONY: Modules.symvers endif @@ -76,6 +121,7 @@ SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Module.symvers 2>/dev/null) ifneq ($(SCST_MOD_VERS),) Module.symvers: $(SCST_DIR)/Module.symvers cp $(SCST_DIR)/Module.symvers kernel/ + cp $(SCST_DIR)/Module.symvers kernel/isert-scst else .PHONY: Module.symvers endif @@ -83,17 +129,24 @@ endif clean: $(MAKE) -C usr $@ $(MAKE) -C $(KDIR) SUBDIRS=$(KMOD) $@ + $(MAKE) -C $(KDIR) SUBDIRS=$(ISERTMOD) $@ rm -f kernel/Modules.symvers kernel/Module.symvers \ kernel/Module.markers kernel/modules.order \ + kernel/isert-scst/Modules.symvers kernel/isert-scst/Module.symvers \ + kernel/isert-scst/Module.markers kernel/isert-scst/modules.order \ include/iscsi_scst_itf_ver.h extraclean: $(MAKE) -C usr $@ $(MAKE) -C $(KDIR) SUBDIRS=$(KMOD) clean + $(MAKE) -C $(KDIR) SUBDIRS=$(ISERTMOD) clean rm -f kernel/Modules.symvers kernel/Module.symvers \ kernel/Module.markers kernel/modules.order \ + kernel/isert-scst/Modules.symvers kernel/isert-scst/Module.symvers \ + kernel/isert-scst/Module.markers kernel/isert-scst/modules.order \ include/iscsi_scst_itf_ver.h \ - kernel/*.orig kernel/*.rej + kernel/*.orig kernel/*.rej \ + kernel/isert-scst/*.orig kernel/isert-scst/*.rej 2release: sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile @@ -103,6 +156,13 @@ extraclean: sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(KMOD)/Makefile grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(KMOD)/Makefile >/dev/null rm $(KMOD)/Makefile.aa + sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(ISERTMOD)/Makefile + grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS" $(ISERTMOD)/Makefile >/dev/null + sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(ISERTMOD)/Makefile + grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING" $(ISERTMOD)/Makefile >/dev/null + sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(ISERTMOD)/Makefile + grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(ISERTMOD)/Makefile >/dev/null + rm $(ISERTMOD)/Makefile.aa 2debug: sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile @@ -112,6 +172,13 @@ extraclean: sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(KMOD)/Makefile grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(KMOD)/Makefile >/dev/null rm $(KMOD)/Makefile.aa + sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(ISERTMOD)/Makefile + grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS" $(ISERTMOD)/Makefile >/dev/null + sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(ISERTMOD)/Makefile + grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING" $(ISERTMOD)/Makefile >/dev/null + sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(ISERTMOD)/Makefile + grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(ISERTMOD)/Makefile >/dev/null + rm $(ISERTMOD)/Makefile.aa 2perf: sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile @@ -121,6 +188,13 @@ extraclean: sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(KMOD)/Makefile grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(KMOD)/Makefile >/dev/null rm $(KMOD)/Makefile.aa + sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(ISERTMOD)/Makefile + grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS" $(ISERTMOD)/Makefile >/dev/null + sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(ISERTMOD)/Makefile + grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING" $(ISERTMOD)/Makefile >/dev/null + sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(ISERTMOD)/Makefile + grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(ISERTMOD)/Makefile >/dev/null + rm $(ISERTMOD)/Makefile.aa disable_proc: sed -i.aa s/"^#\?define CONFIG_SCST_PROC"/"\/* #define CONFIG_SCST_PROC *\/"/ $(INCDIR)/iscsi_scst_ver.h diff --git a/iscsi-scst/README.iser_ofed b/iscsi-scst/README.iser_ofed new file mode 100644 index 000000000..c93150b36 --- /dev/null +++ b/iscsi-scst/README.iser_ofed @@ -0,0 +1,117 @@ +iSCSI Extensins for RDMA (iSER) Target driver for Linux +================================================= + +Introduction +------------ + +The iSER target driver has been designed to work on top of the Linux +InfiniBand kernel drivers. While all recent Linux distributions +include recent versions of the InfiniBand drivers, the only way to +obtain the latest available InfiniBand drivers is by installing the +OFED or MLNX_OFED (for Mellanox drivers) software stack. + +The OFED stack is distributed by the OpenFabrics Alliance (OFA). The +mission of the OpenFabrics Alliance is to is to develop, distribute +and promote a unified, transport-independent, open-source software +stack for RDMA-capable fabrics and networks, including InfiniBand and +Ethernet. + +The MLNX_OFED is distributed by Mellanox and can be obtained from +http://www.mellanox.com/page/products_dyn?product_family=26 + +Note: because during OFED installation the distro-provided InfiniBand +kernel drivers are replaced, doing so voids the support contract +offered by your Linux distributor. + +Please follow the instructions below carefully. Skipping a step may +result in kernel modules that fail to load, a kernel oops or even a +system that does no longer boot. + + +Verifying the kernel version +---------------------------- + +Before installing the OFED distribution, it is very important to check +the OFED release notes. Each OFED distribution has been tested +carefully, but only against the kernel versions specified in +docs/OFED_release_notes.txt (you can find this document in the OFED +distribution). Make sure that you are using a supported kernel / OFED +combination. As an example, if you want to use OFED 1.5.1 on an Ubuntu +system, you will have to start with replacing the Ubuntu kernel by a +kernel from kernel.org since OFED 1.5.1 has not been tested on any +Ubuntu kernel. + + +Compiling iSER against OFED +-------------------------- + +Make sure that all necessary packages needed for kernel compilation +have been installed (kernel headers, gcc, binutils, ...). + +Unload any loaded InfiniBand drivers: + + /etc/init.d/opensmd stop + /etc/init.d/openibd stop + +Remove any distro-provided InfiniBand drivers: + + rm -rf /lib/modules/$(uname -r)/kernel/drivers/infiniband + rm -rf /lib/modules/$(uname -r)/kernel/drivers/net/mlx4 + +Now locate the file Makefile.lib and patch it such that it supports +the variable PRE_CFLAGS: + + if [ -e /lib/modules/$(uname -r)/build/scripts/Makefile.lib ]; then + cd /lib/modules/$(uname -r)/build + else + cd /usr/src/linux-$(uname -r) + fi + patch -p1 < ${SCST_DIR}/srpt/patches/kernel-${KV}-pre-cflags.patch + +Next, download and install an OFED pacakge. + +For MLNX_OFED, just run the mlnxofedinstall script inside the MLNX_OFED directory. + +For the OFED package.Make sure to enable +at least the kernel-ib and kernel-ib-devel packages. An example: + + wget http://www.openfabrics.org/downloads/OFED/ofed-1.5.1/OFED-1.5.1.tgz + tar xzf OFED-1.5.1.tgz + cd OFED-1.5.1 + cat <ofed.conf + libibverbs=y + libibverbs-utils=y + libmthca=y + libmlx4=y + libcxgb3=y + libnes=y + libipathverbs=y + librdmacm=y + librdmacm-utils=y + mstflint=y + ofed-docs=y + ofed-scripts=y + kernel-ib=y + kernel-ib-devel=y + ibvexdmtools=y + qlgc_vnic_daemon=y + core=y + mthca=y + mlx4=y + mlx4_en=y + cxgb3=y + nes=y + ipath=y + ipoib=y + opensm=y + opensm-libs=y + srpt=n + srptools=y + perftest=y + EOF + ./install.pl -c ofed.conf + +Now continue with the installation instructions you can find in the +ISCSI-SCST README file. The Makefile included with ISCSI-SCST detects +whether OFED has been installed, and if so, compiles ISCSIS-SCST with +the OFED kernel headers instead of with the regular kernel headers. diff --git a/iscsi-scst/include/isert_scst.h b/iscsi-scst/include/isert_scst.h new file mode 100644 index 000000000..1477cfdfd --- /dev/null +++ b/iscsi-scst/include/isert_scst.h @@ -0,0 +1,24 @@ +#ifndef _ISERT_SCST_U_H +#define _ISERT_SCST_U_H + +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif + +struct isert_addr_info { + struct sockaddr_storage addr; + size_t addr_len; +}; + +#define ISERT_MAX_PORTALS 32 + +#define SET_LISTEN_ADDR _IOW('y', 0, struct isert_addr_info) +#define RDMA_CORK _IOW('y', 1, int) +#define GET_PORTAL_ADDR _IOW('y', 2, struct isert_addr_info) +#define DISCOVERY_SESSION _IOW('y', 3, int) + +#endif diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 42cce27ff..35d778ab6 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -994,7 +994,10 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info) goto out; } - t = iscsit_get_transport(ISCSI_TCP); + if (session->sess_params.rdma_extensions) + t = iscsit_get_transport(ISCSI_RDMA); + else + t = iscsit_get_transport(ISCSI_TCP); if (!t) { err = -ENOENT; goto out; diff --git a/iscsi-scst/kernel/iscsi_dbg.h b/iscsi-scst/kernel/iscsi_dbg.h index c7fe8fcba..518600696 100644 --- a/iscsi-scst/kernel/iscsi_dbg.h +++ b/iscsi-scst/kernel/iscsi_dbg.h @@ -17,6 +17,10 @@ #ifndef ISCSI_DBG_H #define ISCSI_DBG_H +#ifdef LOG_PREFIX +#undef LOG_PREFIX +#endif + #define LOG_PREFIX "iscsi-scst" #ifdef INSIDE_KERNEL_TREE @@ -54,8 +58,10 @@ extern unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag( #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) extern unsigned long iscsi_trace_flag; +#ifndef trace_flag #define trace_flag iscsi_trace_flag #endif +#endif #define TRACE_CONN_CLOSE(args...) TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_CONN_OC, args) #define TRACE_CONN_CLOSE_DBG(args...) TRACE(TRACE_CONN_OC_DBG, args) diff --git a/iscsi-scst/kernel/isert-scst/Kconfig b/iscsi-scst/kernel/isert-scst/Kconfig new file mode 100644 index 000000000..99ff7a97f --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/Kconfig @@ -0,0 +1,8 @@ +config SCST_ISER + tristate "ISCSI Target" + depends on SCST && SCST_ISCSI + default SCST + help + ISER target driver for SCST framework. The iSCSI iSER extension + has been defined in RFC 5046. + diff --git a/iscsi-scst/kernel/isert-scst/Makefile b/iscsi-scst/kernel/isert-scst/Makefile new file mode 100644 index 000000000..b1245a105 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/Makefile @@ -0,0 +1,40 @@ +# +# Makefile for the kernel part of iSER-SCST. +# +# Copyright (C) 2007 - 2013 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2010 ID7 Ltd. +# Copyright (C) 2010 - 2013 SCST Ltd. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, version 2 +# of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Note! Dependencies are done automatically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile. + +cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \ + > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) +enable-Wextra = $(shell uname_r="$$(uname -r)"; if [ "$${uname_r%.el5}" = "$${uname_r}" ]; then echo "$(1)"; fi) + +EXTRA_CFLAGS += -I$(src)/../../include -I$(src)/../ -I$(SCST_INC_DIR) +EXTRA_CFLAGS += $(call enable-Wextra,-Wextra \ + $(call cc-option,-Wno-old-style-declaration) \ + -Wno-unused-parameter -Wno-missing-field-initializers) + +EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS +#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING +EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions + +obj-m += isert-scst.o +isert-scst-objs := isert.o isert_login.o \ + iser_datamover.o iser_rdma.o iser_buf.o iser_pdu.o iser_global.o + diff --git a/iscsi-scst/kernel/isert-scst/Makefile.in-kernel b/iscsi-scst/kernel/isert-scst/Makefile.in-kernel new file mode 100644 index 000000000..e65970072 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/Makefile.in-kernel @@ -0,0 +1,4 @@ +isert-scst-y := isert.o isert_login.o \ + iser_datamover.o iser_rdma.o iser_buf.o iser_pdu.o iser_global.o + +obj-$(CONFIG_SCST_ISER) += isert-scst.o diff --git a/iscsi-scst/kernel/isert-scst/TODO b/iscsi-scst/kernel/isert-scst/TODO new file mode 100644 index 000000000..a86e3cc1a --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/TODO @@ -0,0 +1,10 @@ +* In login, handle declarative statements correctly: + use text_key_add() to add target declarative keys. +* Add suppport for immediate data in iSER +* Add suppport for data-out in iSER +* Look into allocating wr and sg entries dynamically from kmem_cache instead of embedding them into iser_cmnd +* Look into seperating between RX pdu and TX pdu +* Do not signal every "response sent" notification +* Make the code NUMA aware +* Add support for AHS + diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h new file mode 100644 index 000000000..727ea4116 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -0,0 +1,315 @@ +#ifndef __ISER_H__ +#define __ISER_H__ + +#include +#include +#include +#include +#include + +#include "iser_hdr.h" + +struct isert_portal { + struct rdma_cm_id *cm_id; + struct sockaddr_storage addr; + struct list_head list_node; /* in portals list */ + /* protected by dev_list_mutex */ + struct list_head conn_list; /* head of conns list */ +}; + +struct isert_buf { + int sg_cnt ____cacheline_aligned; + struct scatterlist *sg; + u8 *addr; + size_t size; + enum dma_data_direction dma_dir; + unsigned int is_alloced:1; + unsigned int is_pgalloced:1; + unsigned int is_malloced:1; +}; + +enum isert_wr_op { + ISER_WR_RECV, + ISER_WR_SEND, + ISER_WR_RDMA_WRITE, + ISER_WR_RDMA_READ, +}; + +struct isert_device; +struct isert_connection; + +struct isert_wr { + enum isert_wr_op wr_op; + struct isert_buf *buf; + + struct isert_connection *conn; + struct isert_cmnd *pdu; + + struct isert_device *isert_dev; + + struct ib_sge *sge_list; + union { + struct ib_recv_wr recv_wr; + struct ib_send_wr send_wr; + }; +} ____cacheline_aligned; + +#define ISER_MAX_SGE 128 +#define ISER_MAX_RDMAS 5 + +#define ISER_SQ_SIZE 128 + +struct isert_cmnd { + struct iscsi_cmnd iscsi ____cacheline_aligned; + + struct isert_buf buf; + struct isert_buf rdma_buf; + struct isert_wr wr[ISER_MAX_RDMAS]; + struct ib_sge sg_pool[ISER_MAX_SGE]; + + struct isert_hdr *isert_hdr ____cacheline_aligned; + struct iscsi_hdr *bhs; + void *ahs; + void *data; + + u8 isert_opcode; + u8 iscsi_opcode; + u8 is_rstag_valid; + u8 is_wstag_valid; + + u32 rem_write_stag; /* write rkey */ + u64 rem_write_va; + u32 rem_read_stag; /* read rkey */ + u64 rem_read_va; + + int is_fake_rx; + struct list_head pool_node; /* pool list */ +}; + +enum isert_conn_state { + ISER_CONN_INIT = 0, + ISER_CONN_HANDSHAKE, + ISER_CONN_ACTIVE, + ISER_CONN_CLOSING, +}; + +struct isert_cq { + struct ib_cq *cq ____cacheline_aligned; + struct ib_wc wc[ISER_SQ_SIZE]; + struct isert_device *dev; + struct workqueue_struct *cq_workqueue; + struct work_struct cq_comp_work; + int idx; +}; + +#define ISERT_TIMEWAIT_RECEIVED 0 +#define ISERT_CONNECTION_ABORTED 1 + +struct isert_connection { + struct iscsi_conn iscsi ____cacheline_aligned; + + int repost_threshold ____cacheline_aligned; + /* access to the following 3 fields is guarded by post_recv_lock */ + int to_post_recv; + struct isert_wr *post_recv_first; + struct isert_wr *post_recv_curr; + + spinlock_t post_recv_lock; + + + spinlock_t tx_lock ____cacheline_aligned; + + /* Following two protected by tx_lock */ + struct list_head tx_free_list; + struct list_head tx_busy_list; + + struct rdma_cm_id *cm_id; + struct isert_device *isert_dev; + struct ib_qp *qp; + struct isert_cq *cq_desc; + + enum isert_conn_state state; + + u32 responder_resources; + u32 initiator_depth; + u32 max_sge; + + /* + * Unprotected. Accessed only before login response is sent and when + * freeing connection + */ + struct list_head rx_buf_list; + + struct isert_cmnd *login_req_pdu; + struct isert_cmnd *login_rsp_pdu; + struct isert_wr *saved_wr; + + int queue_depth; + int immediate_data; + unsigned int target_recv_data_length; + int initiator_recv_data_length; + int initial_r2t; + unsigned int first_burst_length; + struct sockaddr_storage peer_addr; + size_t peer_addrsz; + struct sockaddr_storage self_addr; + + struct list_head dev_node; + struct list_head portal_node; + + wait_queue_head_t waitQ; + unsigned long flags; + struct work_struct close_work; + struct kref kref; + + void *priv_data; /* for connection tracking */ +}; + +struct isert_device { + struct ib_device *ib_dev; + struct ib_pd *pd; + struct ib_mr *mr; + + struct list_head devs_node; + /* conn_list and refcnt protected by dev_list_mutex */ + struct list_head conn_list; + int refcnt; + struct ib_device_attr device_attr; + + int num_cqs; + int *cq_qps; + struct isert_cq *cq_desc; +}; + +struct isert_global { + spinlock_t portal_lock; + /* protected by portal_lock */ + struct list_head portal_list; + /* protected by dev_list_mutex */ + struct list_head dev_list; + struct workqueue_struct *conn_wq; +}; + +#define _ptr_to_u64(p) (u64)(unsigned long)(p) +#define _u64_to_ptr(v) (void *)(unsigned long)(v) + +/* global iser scope */ +int isert_global_init(void); +int isert_datamover_cleanup(void); + +void isert_portal_list_add(struct isert_portal *portal); +void isert_portal_list_remove(struct isert_portal *portal); + +void isert_dev_list_add(struct isert_device *isert_dev); +void isert_dev_list_remove(struct isert_device *isert_dev); +struct isert_device *isert_device_find(struct ib_device *ib_dev); + +void isert_conn_queue_work(struct work_struct *w); + +extern struct kmem_cache *isert_cmnd_cache; +extern struct kmem_cache *isert_conn_cache; + +/* iser portal */ +struct isert_portal *isert_portal_create(void); +int isert_portal_listen(struct isert_portal *portal, + struct sockaddr *sa, + size_t addr_len); +void isert_portal_release(struct isert_portal *portal); +void isert_portal_list_release_all(void); +struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len); +struct isert_portal *isert_portal_add_addr_any(u16 port); + +/* iser connection */ +int isert_post_recv(struct isert_connection *isert_conn, + struct isert_wr *first_wr, int num_wr); +int isert_post_send(struct isert_connection *isert_conn, + struct isert_wr *first_wr, int num_wr); + +int isert_alloc_conn_resources(struct isert_connection *isert_conn); +void isert_free_conn_resources(struct isert_connection *isert_conn); +void isert_conn_close(struct isert_connection *isert_conn, int do_flush); +void isert_conn_free(struct isert_connection *isert_conn); + +static inline struct isert_connection *isert_conn_alloc(void) +{ + return kmem_cache_zalloc(isert_conn_cache, GFP_KERNEL); +} + +static inline void isert_conn_kfree(struct isert_connection *isert_conn) +{ + kmem_cache_free(isert_conn_cache, isert_conn); +} + +/* iser buf */ +int isert_buf_alloc_data_buf(struct ib_device *ib_dev, + struct isert_buf *isert_buf, size_t size, + enum dma_data_direction dma_dir); +void isert_wr_set_fields(struct isert_wr *wr, + struct isert_connection *isert_conn, + struct isert_cmnd *pdu); +int isert_wr_init(struct isert_wr *wr, + enum isert_wr_op wr_op, + struct isert_buf *isert_buf, + struct isert_connection *isert_conn, + struct isert_cmnd *pdu, + struct ib_sge *sge, + int sg_offset, + int sg_cnt, + int buff_offset); +void isert_wr_release(struct isert_wr *wr); + +void isert_buf_release(struct isert_buf *isert_buf); + +static inline void isert_buf_init_sg(struct isert_buf *isert_buf, + struct scatterlist *sg, + int sg_cnt, size_t size) +{ + isert_buf->sg_cnt = sg_cnt; + isert_buf->sg = sg; + isert_buf->size = size; +} + +/* iser pdu */ +static inline struct isert_cmnd *isert_pdu_alloc(void) +{ + return kmem_cache_zalloc(isert_cmnd_cache, GFP_KERNEL); +} + +static inline void isert_pdu_kfree(struct isert_cmnd *cmnd) +{ + kmem_cache_free(isert_cmnd_cache, cmnd); +} + +struct isert_cmnd *isert_rx_pdu_alloc(struct isert_connection *isert_conn, + size_t size); +struct isert_cmnd *isert_tx_pdu_alloc(struct isert_connection *isert_conn, + size_t size); +void isert_tx_pdu_init(struct isert_cmnd *isert_pdu, + struct isert_connection *isert_conn); +int isert_pdu_send(struct isert_connection *isert_conn, + struct isert_cmnd *tx_pdu); + +int isert_prepare_rdma(struct isert_cmnd *isert_pdu, + struct isert_connection *isert_conn, + enum isert_wr_op op); +int isert_pdu_post_rdma_write(struct isert_connection *isert_conn, + struct isert_cmnd *isert_cmd, + struct isert_cmnd *isert_rsp, + int wr_cnt); +int isert_pdu_post_rdma_read(struct isert_connection *isert_conn, + struct isert_cmnd *isert_cmd, + int wr_cnt); + +void isert_pdu_free(struct isert_cmnd *pdu); +int isert_rx_pdu_done(struct isert_cmnd *pdu); + +void isert_tx_pdu_convert_from_iscsi(struct isert_cmnd *isert_cmnd, + struct iscsi_cmnd *iscsi_cmnd); + +void isert_tx_pdu_init_iscsi(struct isert_cmnd *isert_pdu); + +/* global */ +void isert_global_cleanup(void); +int isert_get_addr_size(struct sockaddr *sa, size_t *size); + +#endif diff --git a/iscsi-scst/kernel/isert-scst/iser_buf.c b/iscsi-scst/kernel/isert-scst/iser_buf.c new file mode 100644 index 000000000..de07f862a --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser_buf.c @@ -0,0 +1,303 @@ +/* +* isert_buf.c +* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include + +#include "iser.h" + +static int isert_buf_alloc_pg(struct ib_device *ib_dev, + struct isert_buf *isert_buf, size_t size, + enum dma_data_direction dma_dir) +{ + int res = 0; + int i; + struct page *page; + + isert_buf->sg_cnt = DIV_ROUND_UP(size, PAGE_SIZE); + isert_buf->sg = kmalloc(sizeof(*isert_buf->sg) * isert_buf->sg_cnt, + GFP_KERNEL); + if (unlikely(!isert_buf->sg)) { + pr_err("Failed to allocate buffer SG\n"); + res = -ENOMEM; + goto out; + } + + sg_init_table(isert_buf->sg, isert_buf->sg_cnt); + for (i = 0; i < isert_buf->sg_cnt; ++i) { + size_t page_len = min_t(size_t, size, PAGE_SIZE); + + page = alloc_page(GFP_KERNEL); + if (!page) { + pr_err("Failed to allocate page\n"); + res = -ENOMEM; + goto out_map_failed; + } + sg_set_page(&isert_buf->sg[i], page, page_len, 0); + size -= page_len; + } + + res = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, dma_dir); + if (unlikely(!res)) { + pr_err("Failed to DMA map iser sg:%p len:%d\n", + isert_buf->sg, isert_buf->sg_cnt); + res = -ENOMEM; + goto out_map_failed; + } + + isert_buf->addr = sg_virt(&isert_buf->sg[0]); + + res = 0; + goto out; + +out_map_failed: + for (; i > 0; --i) + __free_page(sg_page(&isert_buf->sg[i])); + kfree(isert_buf->sg); + isert_buf->sg = NULL; +out: + return res; +} + +static void isert_buf_release_pg(struct isert_buf *isert_buf) +{ + int i; + + for (i = 0; i < isert_buf->sg_cnt; ++i) + __free_page(sg_page(&isert_buf->sg[i])); +} + +static int isert_buf_malloc(struct ib_device *ib_dev, + struct isert_buf *isert_buf, size_t size, + enum dma_data_direction dma_dir) +{ + int res = 0; + + isert_buf->sg_cnt = 1; + isert_buf->sg = kmalloc(sizeof(isert_buf->sg[0]), GFP_KERNEL); + if (unlikely(!isert_buf->sg)) { + pr_err("Failed to allocate buffer SG\n"); + res = -ENOMEM; + goto out; + } + + isert_buf->addr = kmalloc(size, GFP_KERNEL); + if (!isert_buf->addr) { + pr_err("Failed to allocate data buffer\n"); + res = -ENOMEM; + goto data_malloc_failed; + } + + sg_init_one(&isert_buf->sg[0], isert_buf->addr, size); + + res = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, dma_dir); + if (unlikely(!res)) { + pr_err("Failed to DMA map iser sg:%p len:%d\n", + isert_buf->sg, isert_buf->sg_cnt); + res = -ENOMEM; + goto out_map_failed; + } + + res = 0; + goto out; + +out_map_failed: + kfree(isert_buf->addr); + isert_buf->addr = NULL; +data_malloc_failed: + kfree(isert_buf->addr); + isert_buf->addr = NULL; +out: + return res; +} + +static void isert_buf_release_kmalloc(struct isert_buf *isert_buf) +{ + kfree(isert_buf->addr); + isert_buf->addr = NULL; +} + +int isert_buf_alloc_data_buf(struct ib_device *ib_dev, + struct isert_buf *isert_buf, size_t size, + enum dma_data_direction dma_dir) +{ + int res = 0; + + isert_buf->is_alloced = 0; + if (size >= PAGE_SIZE) { + res = isert_buf_alloc_pg(ib_dev, isert_buf, size, dma_dir); + if (unlikely(res)) + goto out; + isert_buf->is_pgalloced = 1; + isert_buf->is_malloced = 0; + isert_buf->is_alloced = 1; + } else if (size) { + res = isert_buf_malloc(ib_dev, isert_buf, size, dma_dir); + if (unlikely(res)) + goto out; + isert_buf->is_pgalloced = 0; + isert_buf->is_malloced = 1; + isert_buf->is_alloced = 1; + } + + isert_buf->size = size; + isert_buf->dma_dir = dma_dir; +out: + return res; +} + +void isert_buf_release(struct isert_buf *isert_buf) +{ + if (isert_buf->is_alloced) { + if (isert_buf->is_pgalloced) + isert_buf_release_pg(isert_buf); + + if (isert_buf->is_malloced) + isert_buf_release_kmalloc(isert_buf); + + isert_buf->is_alloced = 0; + kfree(isert_buf->sg); + isert_buf->sg = NULL; + } +} + +void isert_wr_set_fields(struct isert_wr *wr, + struct isert_connection *isert_conn, + struct isert_cmnd *pdu) +{ + struct isert_device *isert_dev = isert_conn->isert_dev; + + wr->conn = isert_conn; + wr->pdu = pdu; + wr->isert_dev = isert_dev; +} + +int isert_wr_init(struct isert_wr *wr, + enum isert_wr_op wr_op, + struct isert_buf *isert_buf, + struct isert_connection *isert_conn, + struct isert_cmnd *pdu, + struct ib_sge *sge, + int sg_offset, + int sg_cnt, + int buff_offset) +{ + enum ib_wr_opcode send_wr_op = IB_WR_SEND; + struct scatterlist *sg_tmp; + int i; + + TRACE_ENTRY(); + + switch (wr_op) { + case ISER_WR_RECV: + case ISER_WR_SEND: + break; + case ISER_WR_RDMA_READ: + send_wr_op = IB_WR_RDMA_READ; + if (unlikely(!pdu->is_wstag_valid)) { + pr_err("No write tag/va specified for RDMA op\n"); + isert_buf_release(isert_buf); + buff_offset = -EFAULT; + goto out; + } + wr->send_wr.wr.rdma.remote_addr = pdu->rem_write_va + + buff_offset; + wr->send_wr.wr.rdma.rkey = pdu->rem_write_stag; + break; + case ISER_WR_RDMA_WRITE: + send_wr_op = IB_WR_RDMA_WRITE; + if (unlikely(!pdu->is_rstag_valid)) { + pr_err("No read tag/va specified for RDMA op\n"); + isert_buf_release(isert_buf); + buff_offset = -EFAULT; + goto out; + } + wr->send_wr.wr.rdma.remote_addr = pdu->rem_read_va + + buff_offset; + wr->send_wr.wr.rdma.rkey = pdu->rem_read_stag; + break; + default: + BUG(); + } + + EXTRACHECKS_BUG_ON(isert_buf->sg_cnt == 0); + + wr->wr_op = wr_op; + wr->buf = isert_buf; + + wr->sge_list = sge + sg_offset; + + sg_tmp = &isert_buf->sg[sg_offset]; + for (i = 0; i < sg_cnt; i++, sg_tmp++) { + wr->sge_list[i].addr = sg_dma_address(sg_tmp); + wr->sge_list[i].length = sg_dma_len(sg_tmp); + buff_offset += wr->sge_list[i].length; + } + + if (wr_op == ISER_WR_RECV) { + wr->recv_wr.next = NULL; + wr->recv_wr.wr_id = _ptr_to_u64(wr); + wr->recv_wr.sg_list = wr->sge_list; + wr->recv_wr.num_sge = sg_cnt; + } else { + wr->send_wr.next = NULL; + wr->send_wr.wr_id = _ptr_to_u64(wr); + wr->send_wr.sg_list = wr->sge_list; + wr->send_wr.num_sge = sg_cnt; + wr->send_wr.opcode = send_wr_op; + wr->send_wr.send_flags = IB_SEND_SIGNALED; + } + +out: + TRACE_EXIT_RES(buff_offset); + return buff_offset; +} + +void isert_wr_release(struct isert_wr *wr) +{ + struct isert_buf *isert_buf = wr->buf; + if (isert_buf && isert_buf->is_alloced) { + struct isert_device *isert_dev = wr->isert_dev; + struct ib_device *ib_dev; + + ib_dev = isert_dev->ib_dev; + ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, + isert_buf->dma_dir); + isert_buf_release(isert_buf); + } + memset(wr, 0, sizeof(*wr)); +} + diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.c b/iscsi-scst/kernel/isert-scst/iser_datamover.c new file mode 100644 index 000000000..da94dcc2d --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.c @@ -0,0 +1,296 @@ +/* +* isert_datamover.c +* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include + +#include "iser.h" +#include "iser_datamover.h" + +int isert_datamover_init(void) +{ + int err; + + err = isert_global_init(); + if (err) { + pr_err("iser datamover init failed, err:%d\n", err); + return err; + } + return 0; +} + +int isert_datamover_cleanup(void) +{ + isert_global_cleanup(); + return 0; +} + +int isert_get_peer_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa, + size_t *addr_len) +{ + int ret; + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + struct sockaddr *peer_sa = (struct sockaddr *)&isert_conn->peer_addr; + + ret = isert_get_addr_size(peer_sa, addr_len); + if (unlikely(ret)) + goto out; + + memcpy(sa, peer_sa, *addr_len); +out: + return ret; +} + +int isert_get_target_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa, + size_t *addr_len) +{ + int ret; + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + struct sockaddr *self_sa = (struct sockaddr *)&isert_conn->self_addr; + + ret = isert_get_addr_size(self_sa, addr_len); + if (unlikely(ret)) + goto out; + + memcpy(sa, self_sa, *addr_len); +out: + return ret; +} + +void create_sockaddr_any(struct sockaddr *sa, u16 port, size_t *addr_len) +{ + struct sockaddr_in *sa_any = (struct sockaddr_in *)sa; + + memset(sa_any, 0, sizeof(*sa_any)); + sa_any->sin_family = AF_INET; + sa_any->sin_port = cpu_to_be16(port); + sa_any->sin_addr.s_addr = cpu_to_be32(INADDR_ANY); + *addr_len = sizeof(*sa_any); +} + +void *isert_portal_add(struct sockaddr *saddr, size_t addr_len) +{ + struct isert_portal *portal = isert_portal_start(saddr, addr_len); + + if (IS_ERR(portal)) + portal = NULL; + + return portal; +} + +int isert_portal_remove(void *portal_h) +{ + struct isert_portal *portal = portal_h; + + isert_portal_release(portal); + return 0; +} + +void isert_free_connection(struct iscsi_conn *iscsi_conn) +{ + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + isert_conn_free(isert_conn); +} + +struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn) +{ + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + struct isert_cmnd *isert_pdu = isert_conn->login_rsp_pdu; + + isert_tx_pdu_init(isert_pdu, isert_conn); + return &isert_pdu->iscsi; +} + +static struct iscsi_cmnd *isert_alloc_scsi_pdu(struct iscsi_conn *iscsi_conn, + int fake) +{ + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + struct isert_cmnd *isert_pdu; + + spin_lock(&isert_conn->tx_lock); + isert_pdu = list_first_entry(&isert_conn->tx_free_list, + struct isert_cmnd, pool_node); + list_move(&isert_pdu->pool_node, &isert_conn->tx_busy_list); + spin_unlock(&isert_conn->tx_lock); + + isert_pdu->is_fake_rx = fake; + return &isert_pdu->iscsi; +} + +struct iscsi_cmnd *isert_alloc_scsi_rsp_pdu(struct iscsi_conn *iscsi_conn) +{ + return isert_alloc_scsi_pdu(iscsi_conn, 0); +} + +struct iscsi_cmnd *isert_alloc_scsi_fake_pdu(struct iscsi_conn *iscsi_conn) +{ + return isert_alloc_scsi_pdu(iscsi_conn, 1); +} + +void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu) +{ + struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu; + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_pdu->conn; + + isert_tx_pdu_init_iscsi(isert_pdu); + + spin_lock(&isert_conn->tx_lock); + list_move(&isert_pdu->pool_node, &isert_conn->tx_free_list); + spin_unlock(&isert_conn->tx_lock); +} + +void isert_release_rx_pdu(struct iscsi_cmnd *iscsi_pdu) +{ + struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu; + + if (likely(!isert_pdu->is_fake_rx)) + isert_rx_pdu_done(isert_pdu); +} + +/* if last transition into FF (Fully Featured) state */ +int isert_login_rsp_tx(struct iscsi_cmnd *login_rsp, int last, int discovery) +{ + struct isert_connection *isert_conn = (struct isert_connection *)login_rsp->conn; + + if (last && !discovery) { + int err = isert_alloc_conn_resources(isert_conn); + if (err) { + pr_err("Failed to init conn resources\n"); + return err; + } + isert_pdu_free(isert_conn->login_req_pdu); + isert_conn->login_req_pdu = NULL; + } else { + int err = isert_post_recv(isert_conn, + &isert_conn->login_req_pdu->wr[0], + 1); + if (unlikely(err)) { + pr_err("Failed to post recv login req rx buf, err:%d\n", err); + return err; + } + } + + return isert_pdu_tx(login_rsp); +} + +int isert_set_session_params(struct iscsi_conn *iscsi_conn, + struct iscsi_sess_params *sess_params, + struct iscsi_tgt_params *tgt_params) +{ + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + + isert_conn->queue_depth = tgt_params->queued_cmnds; + + isert_conn->immediate_data = sess_params->immediate_data; + isert_conn->target_recv_data_length = sess_params->target_recv_data_length; + isert_conn->initial_r2t = sess_params->initial_r2t; + isert_conn->first_burst_length = sess_params->first_burst_length; + isert_conn->initiator_recv_data_length = sess_params->initiator_recv_data_length; + + return 0; +} + +int isert_pdu_tx(struct iscsi_cmnd *iscsi_cmnd) +{ + struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd; + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn; + int err; + + isert_tx_pdu_convert_from_iscsi(isert_cmnd, iscsi_cmnd); + err = isert_pdu_send(isert_conn, isert_cmnd); + + return err; +} + +int isert_request_data_out(struct iscsi_cmnd *iscsi_cmnd) +{ + struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd; + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn; + int ret; + + ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_READ); + if (unlikely(ret < 0)) + return ret; + + ret = isert_pdu_post_rdma_read(isert_conn, isert_cmnd, ret); + + return ret; +} + +int isert_send_data_in(struct iscsi_cmnd *iscsi_cmnd, + struct iscsi_cmnd *iscsi_rsp) +{ + struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd; + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn; + struct isert_cmnd *isert_rsp = (struct isert_cmnd *)iscsi_rsp; + int ret; + + ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_WRITE); + if (unlikely(ret < 0)) + return ret; + + isert_tx_pdu_convert_from_iscsi(isert_rsp, iscsi_rsp); + ret = isert_pdu_post_rdma_write(isert_conn, isert_cmnd, isert_rsp, ret); + + return ret; +} + +int isert_close_connection(struct iscsi_conn *iscsi_conn) +{ + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + + isert_conn_close(isert_conn, 1); + return 0; +} + +int isert_task_abort(struct iscsi_cmnd *cmnd) +{ + return 0; +} + +void *isert_get_priv(struct iscsi_conn *iscsi_conn) +{ + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + + return isert_conn->priv_data; +} + +void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv) +{ + struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; + + isert_conn->priv_data = priv; +} diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.h b/iscsi-scst/kernel/isert-scst/iser_datamover.h new file mode 100644 index 000000000..864b3d3ec --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.h @@ -0,0 +1,60 @@ +#ifndef __ISER_DATAMOVER_H__ +#define __ISER_DATAMOVER_H__ + +#include "iscsi.h" + +/* iscsi layer calling iser */ +int isert_datamover_init(void); +int isert_datamover_cleanup(void); + +void create_sockaddr_any(struct sockaddr *sa, u16 port, size_t *addr_len); +void *isert_portal_add(struct sockaddr *sa, size_t addr_len); +int isert_portal_remove(void *portal_h); + +struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn); + +int isert_get_peer_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa, + size_t *addr_len); + +int isert_get_target_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa, + size_t *addr_len); + + /* last: if last transition into FF (Fully Featured) state */ +int isert_login_rsp_tx(struct iscsi_cmnd *login_rsp, + int last, int discovery); +int isert_set_session_params(struct iscsi_conn *iscsi_conn, + struct iscsi_sess_params *sess_params, + struct iscsi_tgt_params *tgt_params); + +struct iscsi_cmnd *isert_alloc_scsi_rsp_pdu(struct iscsi_conn *iscsi_conn); +struct iscsi_cmnd *isert_alloc_scsi_fake_pdu(struct iscsi_conn *iscsi_conn); + +int isert_pdu_tx(struct iscsi_cmnd *pdu); + +int isert_request_data_out(struct iscsi_cmnd *cmd); +int isert_send_data_in(struct iscsi_cmnd *cmd, struct iscsi_cmnd *rsp); +int isert_send_status(struct iscsi_cmnd *rsp); + +int isert_close_connection(struct iscsi_conn *iscsi_conn); +int isert_task_abort(struct iscsi_cmnd *cmnd); +void isert_free_connection(struct iscsi_conn *iscsi_conn); + +void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu); +void isert_release_rx_pdu(struct iscsi_cmnd *cmnd); + +/* iser calling iscsi layer */ +int isert_conn_established(struct iscsi_conn *iscsi_conn, + struct sockaddr *from_addr, int addr_len); +int isert_login_req_rx(struct iscsi_cmnd *login_req); +int isert_pdu_rx(struct iscsi_cmnd *pdu); +int isert_data_out_ready(struct iscsi_cmnd *cmd); +int isert_data_in_sent(struct iscsi_cmnd *cmd); +int isert_pdu_sent(struct iscsi_cmnd *pdu); +void isert_pdu_err(struct iscsi_cmnd *pdu); + +int isert_connection_closed(struct iscsi_conn *iscsi_conn); + +void *isert_get_priv(struct iscsi_conn *iscsi_conn); +void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv); + +#endif diff --git a/iscsi-scst/kernel/isert-scst/iser_global.c b/iscsi-scst/kernel/isert-scst/iser_global.c new file mode 100644 index 000000000..72039e30f --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser_global.c @@ -0,0 +1,161 @@ +/* +* isert_global.c +* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include + +#include "iser.h" + +static struct isert_global isert_glob; + +struct kmem_cache *isert_cmnd_cache; +struct kmem_cache *isert_conn_cache; + +void isert_portal_list_add(struct isert_portal *portal) +{ + spin_lock(&isert_glob.portal_lock); + list_add_tail(&portal->list_node, &isert_glob.portal_list); + spin_unlock(&isert_glob.portal_lock); +} + +void isert_portal_list_remove(struct isert_portal *portal) +{ + spin_lock(&isert_glob.portal_lock); + list_del_init(&portal->list_node); + spin_unlock(&isert_glob.portal_lock); +} + +void isert_dev_list_add(struct isert_device *isert_dev) +{ + list_add_tail(&isert_dev->devs_node, &isert_glob.dev_list); +} + +void isert_dev_list_remove(struct isert_device *isert_dev) +{ + list_del_init(&isert_dev->devs_node); +} + +struct isert_device *isert_device_find(struct ib_device *ib_dev) +{ + struct isert_device *isert_dev; + struct isert_device *res = NULL; + + list_for_each_entry(isert_dev, &isert_glob.dev_list, devs_node) { + if (isert_dev->ib_dev == ib_dev) { + res = isert_dev; + break; + } + } + + return res; +} + +void isert_portal_list_release_all(void) +{ + struct isert_portal *portal, *n; + + list_for_each_entry_safe(portal, n, &isert_glob.portal_list, list_node) + isert_portal_release(portal); +} + +void isert_conn_queue_work(struct work_struct *w) +{ + queue_work(isert_glob.conn_wq, w); +} + +int isert_global_init(void) +{ + INIT_LIST_HEAD(&isert_glob.portal_list); + INIT_LIST_HEAD(&isert_glob.dev_list); + + spin_lock_init(&isert_glob.portal_lock); + + isert_glob.conn_wq = create_workqueue("isert_conn_wq"); + if (!isert_glob.conn_wq) { + pr_err("Failed to alloc iser conn work queue\n"); + return -ENOMEM; + } + + isert_cmnd_cache = KMEM_CACHE(isert_cmnd, + SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN); + if (!isert_cmnd_cache) { + destroy_workqueue(isert_glob.conn_wq); + pr_err("Failed to alloc iser command cache\n"); + return -ENOMEM; + } + + isert_conn_cache = KMEM_CACHE(isert_connection, + SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN); + if (!isert_conn_cache) { + destroy_workqueue(isert_glob.conn_wq); + kmem_cache_destroy(isert_cmnd_cache); + pr_err("Failed to alloc iser connection cache\n"); + return -ENOMEM; + } + + return 0; +} + +void isert_global_cleanup(void) +{ + isert_portal_list_release_all(); + if (isert_glob.conn_wq) + destroy_workqueue(isert_glob.conn_wq); + if (isert_cmnd_cache) + kmem_cache_destroy(isert_cmnd_cache); + if (isert_conn_cache) + kmem_cache_destroy(isert_conn_cache); +} + +int isert_get_addr_size(struct sockaddr *sa, size_t *addr_len) +{ + int ret = 0; + + switch (sa->sa_family) { + case AF_INET: + *addr_len = sizeof(struct sockaddr_in); + break; + case AF_INET6: + *addr_len = sizeof(struct sockaddr_in6); + break; + default: + pr_err("Unknown address family\n"); + ret = -EINVAL; + goto out; + } +out: + return ret; +} diff --git a/iscsi-scst/kernel/isert-scst/iser_hdr.h b/iscsi-scst/kernel/isert-scst/iser_hdr.h new file mode 100644 index 000000000..bcaf64905 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser_hdr.h @@ -0,0 +1,27 @@ +#ifndef __ISER_HDR_H__ +#define __ISER_HDR_H__ + +#include "iscsi.h" + +#define ISCSI_LOGIN_MAX_RDSL (8 * 1024) + +struct isert_hdr { + u8 flags; + u8 rsvd[3]; + __be32 write_stag; /* write rkey */ + __be64 write_va; + __be32 read_stag; /* read rkey */ + __be64 read_va; +} __packed; + +#define ISER_WSV 0x08 +#define ISER_RSV 0x04 + +#define ISER_ISCSI_CTRL 0x10 +#define ISER_HELLO 0x20 +#define ISER_HELLORPLY 0x30 + +#define ISER_HDRS_SZ (sizeof(struct isert_hdr) + sizeof(struct iscsi_hdr)) + +#endif + diff --git a/iscsi-scst/kernel/isert-scst/iser_pdu.c b/iscsi-scst/kernel/isert-scst/iser_pdu.c new file mode 100644 index 000000000..109b3901f --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser_pdu.c @@ -0,0 +1,568 @@ +/* +* isert_pdu.c +* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include + +#include "iser.h" +#include "iscsi.h" +#include "iser_datamover.h" + +static inline int isert_pdu_rx_buf_init(struct isert_cmnd *isert_pdu, + struct isert_connection *isert_conn) +{ + struct isert_buf *isert_buf = &isert_pdu->buf; + + return isert_wr_init(&isert_pdu->wr[0], ISER_WR_RECV, isert_buf, + isert_conn, isert_pdu, isert_pdu->sg_pool, + 0, isert_buf->sg_cnt, 0); +} + +static inline int isert_pdu_tx_buf_init(struct isert_cmnd *isert_pdu, + struct isert_connection *isert_conn) +{ + struct isert_buf *isert_buf = &isert_pdu->buf; + + return isert_wr_init(&isert_pdu->wr[0], ISER_WR_SEND, isert_buf, + isert_conn, isert_pdu, isert_pdu->sg_pool, + 0, isert_buf->sg_cnt, 0); +} + +static inline void isert_pdu_set_hdr_plain(struct isert_cmnd *isert_pdu) +{ + struct isert_hdr *isert_hdr = isert_pdu->isert_hdr; + + isert_hdr->flags = ISER_ISCSI_CTRL; + isert_hdr->write_stag = 0; + isert_hdr->write_va = 0; + isert_hdr->read_stag = 0; + isert_hdr->read_va = 0; +} + +/* rx pdu should be initialized to get the posted buffer and + * the associated pointers right; after a pdu is received + * it should be parsed to setup isert_cmnd + iscsi_cmnd in full + */ +static int isert_rx_pdu_init(struct isert_cmnd *isert_pdu, + struct isert_connection *isert_conn) +{ + struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi; + int err = isert_pdu_rx_buf_init(isert_pdu, isert_conn); + if (unlikely(err < 0)) + return err; + iscsi_cmnd->conn = &isert_conn->iscsi; + return 0; +} + +void isert_tx_pdu_init_iscsi(struct isert_cmnd *isert_pdu) +{ + struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi; + struct isert_buf *isert_buf = &isert_pdu->buf; + + memset(iscsi_cmnd, 0, sizeof(*iscsi_cmnd)); + + iscsi_cmnd->sg_cnt = isert_buf->sg_cnt; + iscsi_cmnd->sg = isert_buf->sg; + iscsi_cmnd->bufflen = isert_buf->size; +} + +/* tx pdu should set most of the pointers to enable filling out + * of the iscsi pdu struct + */ +void isert_tx_pdu_init(struct isert_cmnd *isert_pdu, + struct isert_connection *isert_conn) +{ + struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi; + struct isert_buf *isert_buf = &isert_pdu->buf; + void *addr = isert_buf->addr; + struct iscsi_hdr *bhs = (struct iscsi_hdr *)(addr + sizeof(struct isert_hdr)); + + isert_pdu->isert_hdr = (struct isert_hdr *)addr; + isert_pdu->bhs = bhs; + isert_pdu->ahs = NULL; + + isert_tx_pdu_init_iscsi(isert_pdu); + iscsi_cmnd->conn = &isert_conn->iscsi; +} + +void isert_tx_pdu_convert_from_iscsi(struct isert_cmnd *isert_cmnd, + struct iscsi_cmnd *iscsi_cmnd) +{ + struct iscsi_pdu *iscsi_pdu = &iscsi_cmnd->pdu; + + TRACE_ENTRY(); + + memcpy(isert_cmnd->bhs, &iscsi_pdu->bhs, sizeof(*isert_cmnd->bhs)); + if (unlikely(iscsi_pdu->ahssize)) { + isert_cmnd->ahs = isert_cmnd->bhs + 1; + memcpy(isert_cmnd->ahs, iscsi_pdu->ahs, iscsi_pdu->ahssize); + } + +#ifdef CONFIG_SCST_EXTRACHECKS + if (iscsi_cmnd->bufflen) + EXTRACHECKS_BUG_ON(!iscsi_cmnd->sg); +#endif + + TRACE_EXIT(); + return; +} + +static inline int isert_pdu_prepare_send(struct isert_connection *isert_conn, + struct isert_cmnd *tx_pdu) +{ + struct isert_device *isert_dev = isert_conn->isert_dev; + struct ib_sge *sge = tx_pdu->wr[0].sge_list; + size_t to_sync, size; + int sg_cnt = 0; + + size = ISER_HDRS_SZ + tx_pdu->iscsi.pdu.ahssize + + tx_pdu->iscsi.pdu.datasize; + while (size) { + to_sync = size > PAGE_SIZE ? PAGE_SIZE : size; + ib_dma_sync_single_for_device(isert_dev->ib_dev, sge->addr, + to_sync, + DMA_TO_DEVICE); + + sge->length = to_sync; + size -= to_sync; + ++sge; + ++sg_cnt; + } + + return sg_cnt; +} + +static inline void isert_link_send_wrs(struct isert_wr *from_wr, + struct isert_wr *to_wr) +{ + from_wr->send_wr.next = &to_wr->send_wr; + from_wr->send_wr.send_flags = 0; /* not signaled */ + + to_wr->send_wr.next = NULL; + to_wr->send_wr.send_flags = IB_SEND_SIGNALED; +} + +static inline void isert_link_send_pdu_wrs(struct isert_cmnd *from_pdu, + struct isert_cmnd *to_pdu, + int wr_cnt) +{ + isert_link_send_wrs(&from_pdu->wr[wr_cnt - 1], &to_pdu->wr[0]); +} + +int isert_prepare_rdma(struct isert_cmnd *isert_pdu, + struct isert_connection *isert_conn, + enum isert_wr_op op) +{ + struct isert_buf *isert_buf = &isert_pdu->rdma_buf; + struct isert_device *isert_dev = isert_conn->isert_dev; + struct ib_device *ib_dev = isert_dev->ib_dev; + int err; + int buff_offset; + int sg_offset, sg_cnt; + int wr_cnt, i; + + isert_buf_init_sg(isert_buf, isert_pdu->iscsi.sg, + isert_pdu->iscsi.sg_cnt, + isert_pdu->iscsi.bufflen); + + if (op == ISER_WR_RDMA_WRITE) + isert_buf->dma_dir = DMA_TO_DEVICE; + else + isert_buf->dma_dir = DMA_FROM_DEVICE; + + if (unlikely(isert_buf->sg_cnt > ISER_MAX_SGE)) { + pr_err("Scatterlist too large: %d\n", isert_buf->sg_cnt); + wr_cnt = -EOPNOTSUPP; + goto out; + } + + err = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, + isert_buf->dma_dir); + if (unlikely(!err)) { + pr_err("Failed to DMA map iser sg:%p len:%d\n", + isert_buf->sg, isert_buf->sg_cnt); + wr_cnt = -EFAULT; + goto out; + } + + buff_offset = 0; + sg_cnt = 0; + for (wr_cnt = 0, sg_offset = 0; sg_offset < isert_buf->sg_cnt; ++wr_cnt) { + sg_cnt = min((int)isert_conn->max_sge, + isert_buf->sg_cnt - sg_offset); + err = isert_wr_init(&isert_pdu->wr[wr_cnt], op, isert_buf, + isert_conn, isert_pdu, isert_pdu->sg_pool, + sg_offset, sg_cnt, buff_offset); + if (unlikely(err < 0)) { + wr_cnt = err; + goto out; + } + buff_offset = err; + sg_offset += sg_cnt; + } + + for (i = 1; i < wr_cnt; ++i) + isert_link_send_wrs(&isert_pdu->wr[i - 1], &isert_pdu->wr[i]); + +out: + TRACE_EXIT_RES(wr_cnt); + return wr_cnt; +} + +void isert_pdu_free(struct isert_cmnd *pdu) +{ + unsigned int i; + + list_del(&pdu->pool_node); + for (i = 0; i < ARRAY_SIZE(pdu->wr); ++i) + isert_wr_release(&pdu->wr[i]); + + isert_pdu_kfree(pdu); +} + +struct isert_cmnd *isert_rx_pdu_alloc(struct isert_connection *isert_conn, + size_t size) +{ + struct isert_cmnd *pdu = NULL; + int err; + unsigned int i; + + TRACE_ENTRY(); + + pdu = isert_pdu_alloc(); + if (unlikely(!pdu)) { + pr_err("Failed to alloc pdu\n"); + goto out; + } + + err = isert_buf_alloc_data_buf(isert_conn->isert_dev->ib_dev, + &pdu->buf, size, DMA_FROM_DEVICE); + if (unlikely(err)) { + pr_err("Failed to alloc rx pdu buf sz:%zd\n", size); + goto buf_alloc_failed; + } + + err = isert_rx_pdu_init(pdu, isert_conn); + if (unlikely(err)) { + pr_err("Failed to init rx pdu wr:%p size:%zd err:%d\n", + &pdu->wr, size, err); + goto pdu_init_failed; + } + + for (i = 0; i < ARRAY_SIZE(pdu->wr); ++i) + isert_wr_set_fields(&pdu->wr[i], isert_conn, pdu); + + for (i = 0; i < ARRAY_SIZE(pdu->sg_pool); ++i) + pdu->sg_pool[i].lkey = isert_conn->isert_dev->mr->lkey; + + list_add_tail(&pdu->pool_node, &isert_conn->rx_buf_list); + + goto out; + +pdu_init_failed: + isert_buf_release(&pdu->buf); +buf_alloc_failed: + isert_pdu_kfree(pdu); + pdu = NULL; +out: + TRACE_EXIT(); + return pdu; +} + +struct isert_cmnd *isert_tx_pdu_alloc(struct isert_connection *isert_conn, + size_t size) +{ + struct isert_cmnd *pdu = NULL; + int err; + unsigned int i; + + TRACE_ENTRY(); + + pdu = isert_pdu_alloc(); + if (unlikely(!pdu)) { + pr_err("Failed to alloc pdu\n"); + goto out; + } + + err = isert_buf_alloc_data_buf(isert_conn->isert_dev->ib_dev, + &pdu->buf, size, DMA_TO_DEVICE); + if (unlikely(err)) { + pr_err("Failed to alloc tx pdu buf sz:%zd\n", size); + goto buf_alloc_failed; + } + + err = isert_pdu_tx_buf_init(pdu, isert_conn); + if (unlikely(err < 0)) { + pr_err("Failed to init tx pdu wr:%p size:%zd err:%d\n", + &pdu->wr, size, err); + goto buf_init_failed; + } + isert_tx_pdu_init(pdu, isert_conn); + + for (i = 0; i < ARRAY_SIZE(pdu->wr); ++i) + isert_wr_set_fields(&pdu->wr[i], isert_conn, pdu); + + for (i = 0; i < ARRAY_SIZE(pdu->sg_pool); ++i) + pdu->sg_pool[i].lkey = isert_conn->isert_dev->mr->lkey; + + isert_pdu_set_hdr_plain(pdu); + + list_add_tail(&pdu->pool_node, &isert_conn->tx_free_list); + + goto out; + +buf_init_failed: + isert_buf_release(&pdu->buf); +buf_alloc_failed: + isert_pdu_kfree(pdu); + pdu = NULL; +out: + TRACE_EXIT(); + return pdu; +} + +static inline void isert_link_recv_wrs(struct isert_wr *from_wr, + struct isert_wr *to_wr) +{ + from_wr->recv_wr.next = &to_wr->recv_wr; + + to_wr->recv_wr.next = NULL; +} + +static inline void isert_link_recv_pdu_wrs(struct isert_cmnd *from_pdu, + struct isert_cmnd *to_pdu) +{ + isert_link_recv_wrs(&from_pdu->wr[0], &to_pdu->wr[0]); +} + +int isert_alloc_conn_resources(struct isert_connection *isert_conn) +{ + struct isert_cmnd *pdu, *prev_pdu = NULL, *first_pdu = NULL; + int t_datasz = ISER_HDRS_SZ; + int i_datasz = ISER_HDRS_SZ + SCST_SENSE_BUFFERSIZE; + int i, err = 0; + int to_alloc; + + TRACE_ENTRY(); + + isert_conn->repost_threshold = 32; + to_alloc = isert_conn->queue_depth * 2 + isert_conn->repost_threshold; + + for (i = 0; i < to_alloc; i++) { + pdu = isert_rx_pdu_alloc(isert_conn, t_datasz); + if (unlikely(!pdu)) { + err = -ENOMEM; + goto clean_pdus; + } + + if (unlikely(first_pdu == NULL)) + first_pdu = pdu; + else + isert_link_recv_pdu_wrs(prev_pdu, pdu); + + prev_pdu = pdu; + + pdu = isert_tx_pdu_alloc(isert_conn, i_datasz); + if (unlikely(!pdu)) { + err = -ENOMEM; + goto clean_pdus; + } + } + + err = isert_post_recv(isert_conn, &first_pdu->wr[0], to_alloc); + if (unlikely(err)) { + pr_err("Failed to post recv err:%d\n", err); + goto clean_pdus; + } + +out: + TRACE_EXIT_RES(err); + return err; + +clean_pdus: + isert_free_conn_resources(isert_conn); + goto out; +} + +static int isert_reinit_rx_pdu(struct isert_cmnd *pdu) +{ + struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn; + + pdu->is_rstag_valid = 0; + pdu->is_wstag_valid = 0; + + memset(&pdu->iscsi, 0, sizeof(pdu->iscsi)); + + return isert_rx_pdu_init(pdu, isert_conn); +} + +int isert_rx_pdu_done(struct isert_cmnd *pdu) +{ + int err; + struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn; + + TRACE_ENTRY(); + + err = isert_reinit_rx_pdu(pdu); + if (unlikely(err)) + goto out; + + spin_lock(&isert_conn->post_recv_lock); + if (unlikely(isert_conn->to_post_recv == 0)) + isert_conn->post_recv_first = &pdu->wr[0]; + else + isert_link_recv_wrs(isert_conn->post_recv_curr, &pdu->wr[0]); + + isert_conn->post_recv_curr = &pdu->wr[0]; + + if (++isert_conn->to_post_recv > isert_conn->repost_threshold) { + err = isert_post_recv(isert_conn, isert_conn->post_recv_first, + isert_conn->to_post_recv); + if (unlikely(err)) + pr_err("Failed to post recv err:%d\n", err); + + isert_conn->to_post_recv = 0; + } + spin_unlock(&isert_conn->post_recv_lock); + +out: + TRACE_EXIT_RES(err); + return err; +} + +void isert_free_conn_resources(struct isert_connection *isert_conn) +{ + struct isert_cmnd *pdu; + + TRACE_ENTRY(); + + if (isert_conn->login_rsp_pdu) { + isert_pdu_free(isert_conn->login_rsp_pdu); + isert_conn->login_rsp_pdu = NULL; + } + if (isert_conn->login_req_pdu) { + isert_pdu_free(isert_conn->login_req_pdu); + isert_conn->login_req_pdu = NULL; + } + + while (!list_empty(&isert_conn->rx_buf_list)) { + pdu = list_first_entry(&isert_conn->rx_buf_list, + struct isert_cmnd, pool_node); + isert_pdu_free(pdu); /* releases buffer as well */ + } + + spin_lock(&isert_conn->tx_lock); + while (!list_empty(&isert_conn->tx_free_list)) { + pdu = list_first_entry(&isert_conn->tx_free_list, + struct isert_cmnd, pool_node); + isert_pdu_free(pdu); /* releases buffer as well */ + } + + while (!list_empty(&isert_conn->tx_busy_list)) { + pdu = list_first_entry(&isert_conn->tx_busy_list, + struct isert_cmnd, pool_node); + isert_pdu_free(pdu); /* releases buffer as well */ + } + spin_unlock(&isert_conn->tx_lock); + + TRACE_EXIT(); +} + +int isert_pdu_send(struct isert_connection *isert_conn, + struct isert_cmnd *tx_pdu) +{ + int err; + struct isert_wr *wr; + + TRACE_ENTRY(); + +#ifdef CONFIG_SCST_EXTRACHECKS + EXTRACHECKS_BUG_ON(!isert_conn); + EXTRACHECKS_BUG_ON(!tx_pdu); +#endif + + wr = &tx_pdu->wr[0]; + wr->send_wr.num_sge = isert_pdu_prepare_send(isert_conn, tx_pdu); + + err = isert_post_send(isert_conn, wr, 1); + if (unlikely(err)) { + pr_err("Failed to send pdu conn:%p pdu:%p err:%d\n", + isert_conn, tx_pdu, err); + } + + TRACE_EXIT_RES(err); + return err; +} + +int isert_pdu_post_rdma_write(struct isert_connection *isert_conn, + struct isert_cmnd *isert_cmd, + struct isert_cmnd *isert_rsp, + int wr_cnt) +{ + int err; + + TRACE_ENTRY(); + + isert_rsp->wr[0].send_wr.num_sge = isert_pdu_prepare_send(isert_conn, + isert_rsp); + isert_link_send_pdu_wrs(isert_cmd, isert_rsp, wr_cnt); + err = isert_post_send(isert_conn, &isert_cmd->wr[0], wr_cnt + 1); + if (unlikely(err)) { + pr_err("Failed to send pdu conn:%p pdu:%p err:%d\n", + isert_conn, isert_cmd, err); + } + + TRACE_EXIT_RES(err); + return err; +} + +int isert_pdu_post_rdma_read(struct isert_connection *isert_conn, + struct isert_cmnd *isert_cmd, int wr_cnt) +{ + int err; + + TRACE_ENTRY(); + + err = isert_post_send(isert_conn, &isert_cmd->wr[0], wr_cnt); + if (unlikely(err)) { + pr_err("Failed to send pdu conn:%p pdu:%p err:%d\n", + isert_conn, isert_cmd, err); + } + + TRACE_EXIT_RES(err); + return err; +} + diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c new file mode 100644 index 000000000..b55182e91 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -0,0 +1,1544 @@ +/* +* isert_rdma.c +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include + +#include "iser.h" +#include "iser_datamover.h" + +#define ISER_CQ_ENTRIES (128 * 1024) +#define ISER_MAX_WCE 2048 +#define ISER_LISTEN_BACKLOG 8 + +static DEFINE_MUTEX(dev_list_mutex); + +static int isert_num_recv_posted_on_err(struct ib_recv_wr *first_ib_wr, + struct ib_recv_wr *bad_wr) +{ + struct ib_recv_wr *wr; + int num_posted = 0; + + for (wr = first_ib_wr; wr != NULL && wr != bad_wr; wr = wr->next) + num_posted++; + + return num_posted; +} + +int isert_post_recv(struct isert_connection *isert_conn, + struct isert_wr *first_wr, + int num_wr) +{ + struct ib_recv_wr *first_ib_wr = &first_wr->recv_wr; + struct ib_recv_wr *bad_wr; + int num_posted; + int err; + + TRACE_ENTRY(); + + err = ib_post_recv(isert_conn->qp, first_ib_wr, &bad_wr); + if (unlikely(err)) { + num_posted = isert_num_recv_posted_on_err(first_ib_wr, bad_wr); + + pr_err("conn:%p recv posted:%d/%d 1st wr_id:0x%llx sz:%d err:%d\n", + isert_conn, num_posted, num_wr, first_ib_wr->wr_id, + first_ib_wr->sg_list->length, err); + } + + TRACE_EXIT_RES(err); + return err; +} + +static int isert_num_send_posted_on_err(struct ib_send_wr *first_ib_wr, + struct ib_send_wr *bad_wr) +{ + struct ib_send_wr *wr; + int num_posted = 0; + + for (wr = first_ib_wr; wr != NULL && wr != bad_wr; wr = wr->next) + num_posted++; + + return num_posted; +} + +int isert_post_send(struct isert_connection *isert_conn, + struct isert_wr *first_wr, + int num_wr) +{ + struct ib_send_wr *first_ib_wr = &first_wr->send_wr; + struct ib_send_wr *bad_wr; + int num_posted; + int err; + + TRACE_ENTRY(); + + err = ib_post_send(isert_conn->qp, first_ib_wr, &bad_wr); + if (unlikely(err)) { + num_posted = isert_num_send_posted_on_err(first_ib_wr, bad_wr); + + pr_err("conn:%p send posted:%d/%d bad wr_id:0x%llx sz:%d num_sge: %d err:%d\n", + isert_conn, num_posted, num_wr, bad_wr->wr_id, + bad_wr->sg_list->length, bad_wr->num_sge, err); + } + + TRACE_EXIT_RES(err); + return err; +} + +static void isert_conn_disconnect(struct isert_connection *isert_conn) +{ + int err = rdma_disconnect(isert_conn->cm_id); + if (unlikely(err)) + pr_err("Failed to rdma disconnect, err:%d\n", err); +} + +static int isert_pdu_handle_hello_req(struct isert_cmnd *pdu) +{ + pr_info("iSER Hello not supported\n"); + return -EINVAL; /* meanwhile disconnect immediately */ +} + +static int isert_pdu_handle_login_req(struct isert_cmnd *isert_pdu) +{ + return isert_login_req_rx(&isert_pdu->iscsi); +} + +static int isert_pdu_handle_text(struct isert_cmnd *pdu) +{ + return isert_login_req_rx(&pdu->iscsi); +} + +static int isert_pdu_handle_nop_out(struct isert_cmnd *pdu) +{ + return isert_pdu_rx(&pdu->iscsi); +} + +static int isert_pdu_handle_scsi_cmd(struct isert_cmnd *pdu) +{ + return isert_pdu_rx(&pdu->iscsi); +} + +static int isert_pdu_handle_tm_func(struct isert_cmnd *pdu) +{ + return isert_pdu_rx(&pdu->iscsi); +} + +static int isert_pdu_handle_data_out(struct isert_cmnd *pdu) +{ + pr_info("iser iscsi data out not supported\n"); + return -EINVAL; /* meanwhile disconnect immediately */ +} + +static int isert_pdu_handle_logout(struct isert_cmnd *pdu) +{ + return isert_pdu_rx(&pdu->iscsi); +} + +static int isert_pdu_handle_snack(struct isert_cmnd *pdu) +{ + pr_info("iser iscsi SNACK not supported\n"); + return -EINVAL; /* meanwhile disconnect immediately */ +} + +static void isert_rx_pdu_parse_headers(struct isert_cmnd *isert_pdu) +{ + struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi; + struct isert_buf *isert_buf = &isert_pdu->buf; + u8 *addr = isert_buf->addr; + struct isert_hdr *isert_hdr = (struct isert_hdr *)addr; + struct iscsi_hdr *bhs = (struct iscsi_hdr *)(addr + sizeof(*isert_hdr)); + unsigned int data_offset = ISER_HDRS_SZ; + unsigned int ahssize; + + TRACE_ENTRY(); + + isert_pdu->isert_hdr = isert_hdr; + isert_pdu->isert_opcode = isert_hdr->flags & 0xf0; + isert_pdu->is_rstag_valid = isert_hdr->flags & ISER_RSV ? 1 : 0; + isert_pdu->is_wstag_valid = isert_hdr->flags & ISER_WSV ? 1 : 0; + + if (isert_pdu->is_rstag_valid) { + isert_pdu->rem_read_stag = be32_to_cpu(isert_hdr->read_stag); + isert_pdu->rem_read_va = be64_to_cpu(isert_hdr->read_va); + } + + if (isert_pdu->is_wstag_valid) { + isert_pdu->rem_write_stag = be32_to_cpu(isert_hdr->write_stag); + isert_pdu->rem_write_va = be64_to_cpu(isert_hdr->write_va); + } + + isert_pdu->bhs = bhs; + isert_pdu->iscsi_opcode = bhs->opcode & ISCSI_OPCODE_MASK; + + memcpy(&iscsi_cmnd->pdu.bhs, bhs, sizeof(iscsi_cmnd->pdu.bhs)); + iscsi_cmnd_get_length(&iscsi_cmnd->pdu); /* get ahssize and datasize */ + + ahssize = isert_pdu->iscsi.pdu.ahssize; + if (likely(!ahssize)) { + isert_pdu->ahs = NULL; + } else { + isert_pdu->ahs = addr + ISER_HDRS_SZ; + data_offset += ahssize; + } + iscsi_cmnd->pdu.ahs = isert_pdu->ahs; + + iscsi_cmnd->bufflen = iscsi_cmnd->pdu.datasize; + iscsi_cmnd->bufflen = (iscsi_cmnd->bufflen + 3) & ~3; + if (iscsi_cmnd->bufflen) { + iscsi_cmnd->sg_cnt = isert_pdu->buf.sg_cnt; + iscsi_cmnd->sg = isert_pdu->buf.sg; + } else { + iscsi_cmnd->sg = NULL; + } + + TRACE_EXIT(); +} + +static void isert_dma_sync_data_for_cpu(struct ib_device *ib_dev, + struct ib_sge *sge, size_t size) +{ + size_t to_sync = size > (PAGE_SIZE - ISER_HDRS_SZ) ? + (PAGE_SIZE - ISER_HDRS_SZ) : size; + ib_dma_sync_single_for_cpu(ib_dev, sge->addr + ISER_HDRS_SZ, + to_sync, + DMA_FROM_DEVICE); + + size -= to_sync; + while (size) { + ++sge; + to_sync = size > PAGE_SIZE ? PAGE_SIZE : size; + ib_dma_sync_single_for_cpu(ib_dev, sge->addr, + to_sync, + DMA_FROM_DEVICE); + + size -= to_sync; + } +} + +static void isert_recv_completion_handler(struct isert_wr *wr) +{ + struct isert_cmnd *pdu = wr->pdu; + struct ib_sge *sge = wr->sge_list; + struct ib_device *ib_dev = wr->isert_dev->ib_dev; + int err; + + TRACE_ENTRY(); + + ib_dma_sync_single_for_cpu(ib_dev, sge->addr, + ISER_HDRS_SZ, + DMA_FROM_DEVICE); + isert_rx_pdu_parse_headers(pdu); + isert_dma_sync_data_for_cpu(ib_dev, sge, + pdu->iscsi.pdu.datasize + pdu->iscsi.pdu.ahssize); + + switch (pdu->isert_opcode) { + case ISER_ISCSI_CTRL: + switch (pdu->iscsi_opcode) { + case ISCSI_OP_NOP_OUT: + err = isert_pdu_handle_nop_out(pdu); + break; + case ISCSI_OP_SCSI_CMD: + err = isert_pdu_handle_scsi_cmd(pdu); + break; + case ISCSI_OP_SCSI_TASK_MGT_MSG: + err = isert_pdu_handle_tm_func(pdu); + break; + case ISCSI_OP_LOGIN_CMD: + err = isert_pdu_handle_login_req(pdu); + break; + case ISCSI_OP_TEXT_CMD: + err = isert_pdu_handle_text(pdu); + break; + case ISCSI_OP_SCSI_DATA_OUT: + err = isert_pdu_handle_data_out(pdu); + break; + case ISCSI_OP_LOGOUT_CMD: + err = isert_pdu_handle_logout(pdu); + break; + case ISCSI_OP_SNACK_CMD: + err = isert_pdu_handle_snack(pdu); + break; + default: + pr_err("Unexpected iscsi opcode:0x%x\n", + pdu->iscsi_opcode); + err = -EINVAL; + break; + } + break; + case ISER_HELLO: + err = isert_pdu_handle_hello_req(pdu); + break; + default: + pr_err("malformed isert_hdr, iser op:%x flags 0x%02x\n", + pdu->isert_opcode, pdu->isert_hdr->flags); + err = -EINVAL; + break; + } + + if (unlikely(err)) { + pr_err("err:%d while handling iser pdu\n", err); + isert_conn_close(wr->conn, 0); + } + + TRACE_EXIT(); +} + +static void isert_send_completion_handler(struct isert_wr *wr) +{ + struct isert_cmnd *isert_pdu = wr->pdu; + struct iscsi_cmnd *iscsi_pdu = &isert_pdu->iscsi; + struct iscsi_cmnd *iscsi_req_pdu = iscsi_pdu->parent_req; + struct isert_cmnd *isert_req_pdu = (struct isert_cmnd *)iscsi_req_pdu; + + TRACE_ENTRY(); + + if (iscsi_req_pdu && iscsi_req_pdu->bufflen && + isert_req_pdu->is_rstag_valid) + isert_data_in_sent(iscsi_req_pdu); + + isert_pdu_sent(iscsi_pdu); + + TRACE_EXIT(); +} + +static void isert_rdma_rd_completion_handler(struct isert_wr *wr) +{ + struct isert_buf *isert_buf = wr->buf; + struct isert_device *isert_dev = wr->isert_dev; + struct ib_device *ib_dev = isert_dev->ib_dev; + + ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, + isert_buf->dma_dir); + + isert_data_out_ready(&wr->pdu->iscsi); +} + +static void isert_rdma_wr_completion_handler(struct isert_wr *wr) +{ + struct isert_buf *isert_buf = wr->buf; + struct isert_device *isert_dev = wr->isert_dev; + struct ib_device *ib_dev = isert_dev->ib_dev; + + ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, + isert_buf->dma_dir); + + isert_data_in_sent(&wr->pdu->iscsi); +} + +static void isert_handle_wc(struct ib_wc *wc) +{ + struct isert_wr *wr = _u64_to_ptr(wc->wr_id); + struct isert_connection *isert_conn; + + TRACE_ENTRY(); + + switch (wr->wr_op) { + case ISER_WR_RECV: + isert_conn = wr->conn; + if (unlikely(isert_conn->state == ISER_CONN_HANDSHAKE)) { + isert_conn->state = ISER_CONN_ACTIVE; + isert_conn->saved_wr = wr; + pr_info("iser rx pdu before conn established, pdu saved\n"); + break; + } + isert_recv_completion_handler(wr); + break; + case ISER_WR_SEND: + isert_send_completion_handler(wr); + break; + case ISER_WR_RDMA_WRITE: + isert_rdma_wr_completion_handler(wr); + break; + case ISER_WR_RDMA_READ: + isert_rdma_rd_completion_handler(wr); + break; + default: + isert_conn = wr->conn; + pr_err("unexpected work req op:%d, wc op:%d, wc:%p wr_id:%p conn:%p\n", + wr->wr_op, wc->opcode, wc, wr, isert_conn); + if (isert_conn) + isert_conn_disconnect(isert_conn); + break; + } + + TRACE_EXIT(); +} + +static const char *wr_status_str(enum ib_wc_status status) +{ + switch (status) { + case IB_WC_SUCCESS: + return "WC_SUCCESS"; + + case IB_WC_LOC_LEN_ERR: + return "WC_LOC_LEN_ERR"; + + case IB_WC_LOC_QP_OP_ERR: + return "WC_LOC_QP_OP_ERR"; + + case IB_WC_LOC_EEC_OP_ERR: + return "WC_LOC_EEC_OP_ERR"; + + case IB_WC_LOC_PROT_ERR: + return "WC_LOC_PROT_ERR"; + + case IB_WC_WR_FLUSH_ERR: + return "WC_WR_FLUSH_ERR"; + + case IB_WC_MW_BIND_ERR: + return "WC_MW_BIND_ERR"; + + case IB_WC_BAD_RESP_ERR: + return "WC_BAD_RESP_ERR"; + + case IB_WC_LOC_ACCESS_ERR: + return "WC_LOC_ACCESS_ERR"; + + case IB_WC_REM_INV_REQ_ERR: + return "WC_REM_INV_REQ_ERR"; + + case IB_WC_REM_ACCESS_ERR: + return "WC_REM_ACCESS_ERR"; + + case IB_WC_REM_OP_ERR: + return "WC_REM_OP_ERR"; + + case IB_WC_RETRY_EXC_ERR: + return "WC_RETRY_EXC_ERR"; + + case IB_WC_RNR_RETRY_EXC_ERR: + return "WC_RNR_RETRY_EXC_ERR"; + + case IB_WC_LOC_RDD_VIOL_ERR: + return "WC_LOC_RDD_VIOL_ERR"; + + case IB_WC_REM_INV_RD_REQ_ERR: + return "WC_REM_INV_RD_REQ_ERR"; + + case IB_WC_REM_ABORT_ERR: + return "WC_REM_ABORT_ERR"; + + case IB_WC_INV_EECN_ERR: + return "WC_INV_EECN_ERR"; + + case IB_WC_INV_EEC_STATE_ERR: + return "WC_INV_EEC_STATE_ERR"; + + case IB_WC_FATAL_ERR: + return "WC_FATAL_ERR"; + + case IB_WC_RESP_TIMEOUT_ERR: + return "WC_RESP_TIMEOUT_ERR"; + + case IB_WC_GENERAL_ERR: + return "WC_GENERAL_ERR"; + + default: + return "UNKNOWN"; + } +} + +static void isert_handle_wc_error(struct ib_wc *wc) +{ + struct isert_wr *wr = _u64_to_ptr(wc->wr_id); + struct isert_cmnd *isert_pdu = wr->pdu; + struct isert_connection *isert_conn = wr->conn; + + TRACE_ENTRY(); + + if (wc->status != IB_WC_WR_FLUSH_ERR) + pr_err("conn:%p wr_id:0x%p status:%s vendor_err:0x%0x\n", + isert_conn, wr, wr_status_str(wc->status), + wc->vendor_err); + + switch (wr->wr_op) { + case ISER_WR_SEND: + isert_pdu_err(&isert_pdu->iscsi); + break; + case ISER_WR_RDMA_READ: + isert_pdu_err(&isert_pdu->iscsi); + break; + case ISER_WR_RECV: + /* this should be the Flush, no task has been created yet */ + case ISER_WR_RDMA_WRITE: + /* RDMA-WR and SEND response of a READ task + are sent together, so when receiving RDMA-WR error, + wait until SEND error arrives to complete the task */ + break; + default: + pr_err("unexpected opcode %d, wc:%p wr_id:%p conn:%p\n", + wr->wr_op, wc, wr, isert_conn); + break; + } + + TRACE_EXIT(); +} + +static int isert_poll_cq(struct isert_cq *cq) +{ + int err, i; + + TRACE_ENTRY(); + + do { + err = ib_poll_cq(cq->cq, ARRAY_SIZE(cq->wc), cq->wc); + + for (i = 0; i < err; ++i) { + if (likely(cq->wc[i].status == IB_WC_SUCCESS)) + isert_handle_wc(&cq->wc[i]); + else + isert_handle_wc_error(&cq->wc[i]); + } + + } while (err > 0); + + TRACE_EXIT_RES(err); + return err; +} + +/* callback function for isert_dev->[cq]->cq_comp_work */ +static void isert_cq_comp_work_cb(struct work_struct *work) +{ + struct isert_cq *cq_desc; + struct isert_device *isert_dev; + int ret; + + TRACE_ENTRY(); + + cq_desc = container_of(work, struct isert_cq, cq_comp_work); + isert_dev = cq_desc->dev; + ret = isert_poll_cq(cq_desc); + if (unlikely(ret < 0)) { /* poll error */ + pr_err("ib_poll_cq failed\n"); + goto out; + } + + ib_req_notify_cq(cq_desc->cq, + IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); + /* + * not all HCAs support IB_CQ_REPORT_MISSED_EVENTS, + * so we need to make sure we don't miss any events between + * last call to ib_poll_cq() and ib_req_notify_cq() + */ + isert_poll_cq(cq_desc); + +out: + TRACE_EXIT(); + return; +} + +static void isert_cq_comp_handler(struct ib_cq *cq, void *context) +{ + struct isert_cq *cq_desc = context; + + queue_work_on(smp_processor_id(), cq_desc->cq_workqueue, + &cq_desc->cq_comp_work); +} + +static const char *ib_event_type_str(enum ib_event_type ev_type) +{ + switch (ev_type) { + case IB_EVENT_COMM_EST: + return "COMM_EST"; + case IB_EVENT_QP_FATAL: + return "QP_FATAL"; + case IB_EVENT_QP_REQ_ERR: + return "QP_REQ_ERR"; + case IB_EVENT_QP_ACCESS_ERR: + return "QP_ACCESS_ERR"; + case IB_EVENT_SQ_DRAINED: + return "SQ_DRAINED"; + case IB_EVENT_PATH_MIG: + return "PATH_MIG"; + case IB_EVENT_PATH_MIG_ERR: + return "PATH_MIG_ERR"; + case IB_EVENT_QP_LAST_WQE_REACHED: + return "QP_LAST_WQE_REACHED"; + case IB_EVENT_CQ_ERR: + return "CQ_ERR"; + case IB_EVENT_SRQ_ERR: + return "SRQ_ERR"; + case IB_EVENT_SRQ_LIMIT_REACHED: + return "SRQ_LIMIT_REACHED"; + case IB_EVENT_PORT_ACTIVE: + return "PORT_ACTIVE"; + case IB_EVENT_PORT_ERR: + return "PORT_ERR"; + case IB_EVENT_LID_CHANGE: + return "LID_CHANGE"; + case IB_EVENT_PKEY_CHANGE: + return "PKEY_CHANGE"; + case IB_EVENT_SM_CHANGE: + return "SM_CHANGE"; + case IB_EVENT_CLIENT_REREGISTER: + return "CLIENT_REREGISTER"; + case IB_EVENT_DEVICE_FATAL: + return "DEVICE_FATAL"; + default: + return "UNKNOWN"; + } +} + +static void isert_async_evt_handler(struct ib_event *async_ev, void *context) +{ + struct isert_cq *cq = context; + struct isert_device *isert_dev = cq->dev; + struct ib_device *ib_dev = isert_dev->ib_dev; + char *dev_name = ib_dev->name; + enum ib_event_type ev_type = async_ev->event; + struct isert_connection *isert_conn; + + TRACE_ENTRY(); + + switch (ev_type) { + case IB_EVENT_COMM_EST: + isert_conn = async_ev->element.qp->qp_context; + pr_info("conn:0x%p cm_id:0x%p dev:%s, QP evt: %s\n", + isert_conn, isert_conn->cm_id, dev_name, + ib_event_type_str(IB_EVENT_COMM_EST)); + /* force "connection established" event */ + rdma_notify(isert_conn->cm_id, IB_EVENT_COMM_EST); + break; + + /* rest of QP-related events */ + case IB_EVENT_QP_FATAL: + case IB_EVENT_QP_REQ_ERR: + case IB_EVENT_QP_ACCESS_ERR: + case IB_EVENT_SQ_DRAINED: + case IB_EVENT_PATH_MIG: + case IB_EVENT_PATH_MIG_ERR: + case IB_EVENT_QP_LAST_WQE_REACHED: + isert_conn = async_ev->element.qp->qp_context; + pr_err("conn:0x%p cm_id:0x%p dev:%s, QP evt: %s\n", + isert_conn, isert_conn->cm_id, dev_name, + ib_event_type_str(ev_type)); + break; + + /* CQ-related events */ + case IB_EVENT_CQ_ERR: + pr_err("dev:%s CQ evt: %s\n", dev_name, + ib_event_type_str(ev_type)); + break; + + /* SRQ events */ + case IB_EVENT_SRQ_ERR: + case IB_EVENT_SRQ_LIMIT_REACHED: + pr_err("dev:%s SRQ evt: %s\n", dev_name, + ib_event_type_str(ev_type)); + break; + + /* Port events */ + case IB_EVENT_PORT_ACTIVE: + case IB_EVENT_PORT_ERR: + case IB_EVENT_LID_CHANGE: + case IB_EVENT_PKEY_CHANGE: + case IB_EVENT_SM_CHANGE: + case IB_EVENT_CLIENT_REREGISTER: + pr_err("dev:%s port:%d evt: %s\n", + dev_name, async_ev->element.port_num, + ib_event_type_str(ev_type)); + break; + + /* HCA events */ + case IB_EVENT_DEVICE_FATAL: + pr_err("dev:%s HCA evt: %s\n", dev_name, + ib_event_type_str(ev_type)); + break; + + default: + pr_err("dev:%s evt: %s\n", dev_name, + ib_event_type_str(ev_type)); + break; + } + + TRACE_EXIT(); +} + +static struct isert_device *isert_device_create(struct ib_device *ib_dev) +{ + struct isert_device *isert_dev; + struct ib_device_attr *dev_attr; + int cqe_num, err; + struct ib_pd *pd; + struct ib_mr *mr; + struct ib_cq *cq; + char wq_name[64]; + int i, j; + + TRACE_ENTRY(); + + isert_dev = kzalloc(sizeof(*isert_dev), GFP_KERNEL); + if (isert_dev == NULL) { + pr_err("Failed to allocate iser dev\n"); + err = -ENOMEM; + goto out; + } + + dev_attr = &isert_dev->device_attr; + err = ib_query_device(ib_dev, dev_attr); + if (err) { + pr_err("Failed to query device, err: %d\n", err); + goto fail_query; + } + + isert_dev->num_cqs = min_t(int, num_online_cpus(), + ib_dev->num_comp_vectors); + + isert_dev->cq_qps = kzalloc(sizeof(*isert_dev->cq_qps) * isert_dev->num_cqs, + GFP_KERNEL); + if (isert_dev->cq_qps == NULL) { + pr_err("Failed to allocate iser cq_qps\n"); + err = -ENOMEM; + goto fail_cq_qps; + } + + isert_dev->cq_desc = vmalloc(sizeof(*isert_dev->cq_desc) * isert_dev->num_cqs); + if (isert_dev->cq_desc == NULL) { + pr_err("Failed to allocate %ld bytes for iser cq_desc\n", + sizeof(*isert_dev->cq_desc) * isert_dev->num_cqs); + err = -ENOMEM; + goto fail_alloc_cq_desc; + } + + pd = ib_alloc_pd(ib_dev); + if (IS_ERR(pd)) { + err = PTR_ERR(pd); + pr_err("Failed to alloc iser dev pd, err:%d\n", err); + goto fail_pd; + } + + mr = ib_get_dma_mr(pd, IB_ACCESS_LOCAL_WRITE); + if (IS_ERR(mr)) { + err = PTR_ERR(mr); + pr_err("Failed to get dma mr, err: %d\n", err); + goto fail_mr; + } + + cqe_num = min(isert_dev->device_attr.max_cqe, ISER_CQ_ENTRIES); + cqe_num = cqe_num / isert_dev->num_cqs; + + for (i = 0; i < isert_dev->num_cqs; ++i) { + struct isert_cq *cq_desc = &isert_dev->cq_desc[i]; + + cq_desc->dev = isert_dev; + cq_desc->idx = i; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&cq_desc->cq_comp_work, isert_cq_comp_work_cb, NULL); +#else + INIT_WORK(&cq_desc->cq_comp_work, isert_cq_comp_work_cb); +#endif + + snprintf(wq_name, sizeof(wq_name), "isert_cq_%p", cq_desc); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + cq_desc->cq_workqueue = create_singlethread_workqueue(wq_name); +#else + cq_desc->cq_workqueue = alloc_workqueue(wq_name, + WQ_CPU_INTENSIVE| +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) + WQ_RESCUER +#else + WQ_MEM_RECLAIM +#endif + + , 1); +#endif + if (!cq_desc->cq_workqueue) { + pr_err("Failed to alloc iser cq work queue for dev:%s\n", + ib_dev->name); + err = -ENOMEM; + goto fail_cq; + } + + cq = ib_create_cq(ib_dev, + isert_cq_comp_handler, + isert_async_evt_handler, + cq_desc, /* context */ + cqe_num, + i); /* completion vector */ + if (IS_ERR(cq)) { + err = PTR_ERR(cq); + pr_err("Failed to create iser dev cq, err:%d\n", err); + goto fail_cq; + } + + cq_desc->cq = cq; + err = ib_req_notify_cq(cq, IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); + if (err) { + pr_err("Failed to request notify cq, err: %d\n", err); + goto fail_cq; + } + } + + isert_dev->ib_dev = ib_dev; + isert_dev->pd = pd; + isert_dev->mr = mr; + + INIT_LIST_HEAD(&isert_dev->conn_list); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&dev_list_mutex); +#endif + isert_dev_list_add(isert_dev); + + pr_info("iser created device:%p\n", isert_dev); + return isert_dev; + +fail_cq: + for (j = 0; j < i; ++j) { + if (isert_dev->cq_desc[j].cq) + ib_destroy_cq(isert_dev->cq_desc[j].cq); + if (isert_dev->cq_desc[j].cq_workqueue) + destroy_workqueue(isert_dev->cq_desc[j].cq_workqueue); + } + ib_dereg_mr(mr); +fail_mr: + ib_dealloc_pd(pd); +fail_pd: + vfree(isert_dev->cq_desc); +fail_alloc_cq_desc: + kfree(isert_dev->cq_qps); +fail_cq_qps: +fail_query: + kfree(isert_dev); +out: + TRACE_EXIT_RES(err); + return ERR_PTR(err); +} + +static void isert_device_release(struct isert_device *isert_dev) +{ + int err, i; + + TRACE_ENTRY(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&dev_list_mutex); +#endif + isert_dev_list_remove(isert_dev); /* remove from global list */ + + for (i = 0; i < isert_dev->num_cqs; ++i) { + struct isert_cq *cq_desc = &isert_dev->cq_desc[i]; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + /* + * cancel_work_sync() was introduced in 2.6.22. We can + * only wait until all scheduled work is done. + */ + flush_workqueue(cq_desc->cq_workqueue); +#else + cancel_work_sync(&cq_desc->cq_comp_work); +#endif + + err = ib_destroy_cq(cq_desc->cq); + if (err) + pr_err("Failed to destroy cq, err:%d\n", err); + + destroy_workqueue(cq_desc->cq_workqueue); + } + + err = ib_dereg_mr(isert_dev->mr); + if (err) + pr_err("Failed to destroy mr, err:%d\n", err); + err = ib_dealloc_pd(isert_dev->pd); + if (err) + pr_err("Failed to destroy pd, err:%d\n", err); + + vfree(isert_dev->cq_desc); + isert_dev->cq_desc = NULL; + + kfree(isert_dev->cq_qps); + isert_dev->cq_qps = NULL; + + kfree(isert_dev); + + TRACE_EXIT(); +} + +static int isert_get_cq_idx(struct isert_device *isert_dev) +{ + int i, min_idx; + + min_idx = 0; + mutex_lock(&dev_list_mutex); + for (i = 0; i < isert_dev->num_cqs; ++i) + if (isert_dev->cq_qps[i] < isert_dev->cq_qps[min_idx]) + min_idx = i; + isert_dev->cq_qps[min_idx]++; + mutex_unlock(&dev_list_mutex); + + return min_idx; +} + +static int isert_conn_qp_create(struct isert_connection *isert_conn) +{ + struct rdma_cm_id *cm_id = isert_conn->cm_id; + struct isert_device *isert_dev = isert_conn->isert_dev; + struct ib_qp_init_attr qp_attr; + int err; + int cq_idx; + + TRACE_ENTRY(); + + cq_idx = isert_get_cq_idx(isert_dev); + + memset(&qp_attr, 0, sizeof(qp_attr)); + + qp_attr.event_handler = isert_async_evt_handler; + qp_attr.qp_context = isert_conn; + qp_attr.send_cq = isert_dev->cq_desc[cq_idx].cq; + qp_attr.recv_cq = isert_dev->cq_desc[cq_idx].cq; + qp_attr.cap.max_send_wr = ISER_MAX_WCE; + qp_attr.cap.max_recv_wr = ISER_MAX_WCE; + + isert_conn->cq_desc = &isert_dev->cq_desc[cq_idx]; + + /* + * A quote from the OFED 1.5.3.1 release notes + * (docs/release_notes/mthca_release_notes.txt), section "Known Issues": + * In mem-free devices, RC QPs can be created with a maximum of + * (max_sge - 1) entries only; UD QPs can be created with a maximum of + * (max_sge - 3) entries. + * A quote from the OFED 1.2.5 release notes + * (docs/mthca_release_notes.txt), section "Known Issues": + * In mem-free devices, RC QPs can be created with a maximum of + * (max_sge - 3) entries only. + */ + isert_conn->max_sge = isert_dev->device_attr.max_sge - 3; + + WARN_ON(isert_conn->max_sge < 1); + + qp_attr.cap.max_send_sge = isert_conn->max_sge; + qp_attr.cap.max_recv_sge = 2; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + + err = rdma_create_qp(cm_id, isert_dev->pd, &qp_attr); + if (unlikely(err)) { + pr_err("Failed to create qp, err:%d\n", err); + goto out; + } + isert_conn->qp = cm_id->qp; + + pr_info("iser created cm_id:%p qp:%p\n", cm_id, cm_id->qp); + +out: + TRACE_EXIT_RES(err); + return err; +} + +static void isert_conn_qp_destroy(struct isert_connection *isert_conn) +{ + rdma_destroy_qp(isert_conn->cm_id); + isert_conn->qp = NULL; +} + +static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id, + struct isert_device *isert_dev) +{ + struct isert_connection *isert_conn; + int err; + + TRACE_ENTRY(); + + if (!try_module_get(THIS_MODULE)) { + err = -EINVAL; + goto fail_get; + } + + isert_conn = isert_conn_alloc(); + if (unlikely(!isert_conn)) { + pr_err("Unable to allocate iser conn, cm_id:%p\n", cm_id); + err = -ENOMEM; + goto fail_alloc; + } + isert_conn->state = ISER_CONN_INIT; + isert_conn->cm_id = cm_id; + isert_conn->isert_dev = isert_dev; + + INIT_LIST_HEAD(&isert_conn->rx_buf_list); + INIT_LIST_HEAD(&isert_conn->tx_free_list); + INIT_LIST_HEAD(&isert_conn->tx_busy_list); + spin_lock_init(&isert_conn->tx_lock); + spin_lock_init(&isert_conn->post_recv_lock); + + isert_conn->login_req_pdu = isert_rx_pdu_alloc(isert_conn, + ISCSI_LOGIN_MAX_RDSL); + if (unlikely(!isert_conn->login_req_pdu)) { + pr_err("Failed to init login req rx pdu\n"); + err = -ENOMEM; + goto fail_login_req_pdu; + } + + isert_conn->login_rsp_pdu = isert_tx_pdu_alloc(isert_conn, + ISCSI_LOGIN_MAX_RDSL); + if (unlikely(!isert_conn->login_rsp_pdu)) { + pr_err("Failed to init login rsp tx pdu\n"); + err = -ENOMEM; + goto fail_login_rsp_pdu; + } + + err = isert_conn_qp_create(isert_conn); + if (unlikely(err)) + goto fail_qp; + + err = isert_post_recv(isert_conn, &isert_conn->login_req_pdu->wr[0], 1); + if (unlikely(err)) { + pr_err("Failed to post recv login req rx buf, err:%d\n", err); + goto fail_post_recv; + } + + kref_init(&isert_conn->kref); + + init_waitqueue_head(&isert_conn->waitQ); + + pr_info("iser created connection cm_id:%p\n", cm_id); + TRACE_EXIT(); + return isert_conn; + +fail_post_recv: + isert_conn_qp_destroy(isert_conn); +fail_qp: + isert_pdu_free(isert_conn->login_rsp_pdu); +fail_login_rsp_pdu: + isert_pdu_free(isert_conn->login_req_pdu); +fail_login_req_pdu: + isert_conn_kfree(isert_conn); +fail_alloc: + module_put(THIS_MODULE); +fail_get: + TRACE_EXIT_RES(err); + return ERR_PTR(err); +} + +/* start closing process; + * only when all buffers released, can free */ +void isert_conn_close(struct isert_connection *isert_conn, int do_flush) +{ + isert_conn_disconnect(isert_conn); + if (do_flush) { + wait_event_interruptible(isert_conn->waitQ, + test_bit(ISERT_TIMEWAIT_RECEIVED, + &isert_conn->flags)); + flush_workqueue(isert_conn->cq_desc->cq_workqueue); + } +} + +static void isert_kref_free(struct kref *kref) +{ + struct isert_connection *isert_conn = container_of(kref, + struct isert_connection, + kref); + struct isert_device *isert_dev = isert_conn->isert_dev; + struct isert_cq *cq = isert_conn->qp->recv_cq->cq_context; + + TRACE_ENTRY(); + + pr_info("isert_conn_free conn:%p\n", isert_conn); + + isert_free_conn_resources(isert_conn); + + rdma_destroy_qp(isert_conn->cm_id); + + mutex_lock(&dev_list_mutex); + isert_dev->cq_qps[cq->idx]--; + list_del(&isert_conn->portal_node); + list_del(&isert_conn->dev_node); + isert_dev->refcnt--; + if (isert_dev->refcnt == 0) + isert_device_release(isert_dev); + mutex_unlock(&dev_list_mutex); + + rdma_destroy_id(isert_conn->cm_id); + + isert_conn_kfree(isert_conn); + + module_put(THIS_MODULE); + + TRACE_EXIT(); +} + +void isert_conn_free(struct isert_connection *isert_conn) +{ + kref_put(&isert_conn->kref, isert_kref_free); +} + +static void isert_conn_closed_do_work(struct work_struct *work) +{ + struct isert_connection *isert_conn = + container_of(work, struct isert_connection, close_work); + + set_bit(ISERT_TIMEWAIT_RECEIVED, &isert_conn->flags); + wake_up_interruptible(&isert_conn->waitQ); + + /* notify upper layer */ + if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) + isert_connection_closed(&isert_conn->iscsi); + + isert_conn_free(isert_conn); +} + +static void isert_sched_conn_closed(struct isert_connection *isert_conn) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work, NULL); +#else + INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work); +#endif + isert_conn_queue_work(&isert_conn->close_work); +} + +static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + /* passed in rdma_create_id */ + struct isert_portal *portal = cm_id->context; + struct ib_device *ib_dev = cm_id->device; + struct isert_device *new_isert_dev = NULL; + struct isert_device *isert_dev; + struct isert_connection *isert_conn; + struct rdma_conn_param *ini_conn_param; + struct rdma_conn_param tgt_conn_param; + int err; + + TRACE_ENTRY(); + + mutex_lock(&dev_list_mutex); + isert_dev = isert_device_find(ib_dev); + if (!isert_dev) { + new_isert_dev = isert_device_create(ib_dev); + if (unlikely(IS_ERR(new_isert_dev))) { + err = PTR_ERR(new_isert_dev); + mutex_unlock(&dev_list_mutex); + goto fail_dev_create; + } + isert_dev = new_isert_dev; + } + isert_dev->refcnt++; + mutex_unlock(&dev_list_mutex); + + isert_conn = isert_conn_create(cm_id, isert_dev); + if (unlikely(IS_ERR(isert_conn))) { + err = PTR_ERR(isert_conn); + goto fail_conn_create; + } + + isert_conn->state = ISER_CONN_HANDSHAKE; + + /* initiator is dst, target is src */ + memcpy(&isert_conn->peer_addr, &cm_id->route.addr.dst_addr, + sizeof(isert_conn->peer_addr)); + memcpy(&isert_conn->self_addr, &cm_id->route.addr.src_addr, + sizeof(isert_conn->self_addr)); + + ini_conn_param = &event->param.conn; + memset(&tgt_conn_param, 0, sizeof(tgt_conn_param)); + tgt_conn_param.responder_resources = + ini_conn_param->responder_resources; + tgt_conn_param.initiator_depth = + ini_conn_param->initiator_depth; + tgt_conn_param.flow_control = + ini_conn_param->flow_control; + tgt_conn_param.rnr_retry_count = + ini_conn_param->rnr_retry_count; + + err = rdma_accept(cm_id, &tgt_conn_param); + if (unlikely(err)) { + module_put(THIS_MODULE); + pr_err("Failed to accept conn request, err:%d\n", err); + goto fail_accept; + } + + mutex_lock(&dev_list_mutex); + list_add_tail(&isert_conn->portal_node, &portal->conn_list); + list_add_tail(&isert_conn->dev_node, &isert_dev->conn_list); + mutex_unlock(&dev_list_mutex); + + pr_info("iser accepted connection cm_id:%p\n", cm_id); +out: + TRACE_EXIT_RES(err); + return err; + +fail_accept: + isert_conn_free(isert_conn); + mutex_lock(&dev_list_mutex); + list_del(&isert_conn->portal_node); + list_del(&isert_conn->dev_node); + mutex_unlock(&dev_list_mutex); + isert_conn_qp_destroy(isert_conn); + +fail_conn_create: + if (new_isert_dev) { + mutex_lock(&dev_list_mutex); + new_isert_dev->refcnt--; + if (new_isert_dev->refcnt == 0) + isert_device_release(new_isert_dev); + mutex_unlock(&dev_list_mutex); + } +fail_dev_create: + rdma_reject(cm_id, NULL, 0); + goto out; +} + +static int isert_cm_connect_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct isert_connection *isert_conn = cm_id->qp->qp_context; + int push_saved_pdu = 0; + int ret; + + TRACE_ENTRY(); + + if (isert_conn->state == ISER_CONN_HANDSHAKE) + isert_conn->state = ISER_CONN_ACTIVE; + else if (isert_conn->state == ISER_CONN_ACTIVE) + push_saved_pdu = 1; + + ret = isert_get_addr_size((struct sockaddr *)&isert_conn->peer_addr, + &isert_conn->peer_addrsz); + if (unlikely(ret)) + goto out; + + kref_get(&isert_conn->kref); + /* notify upper layer */ + ret = isert_conn_established(&isert_conn->iscsi, + (struct sockaddr *)&isert_conn->peer_addr, + isert_conn->peer_addrsz); + if (unlikely(ret)) { + set_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags); + isert_conn_free(isert_conn); + goto out; + } + + if (push_saved_pdu) { + pr_info("iser push saved rx pdu\n"); + isert_recv_completion_handler(isert_conn->saved_wr); + isert_conn->saved_wr = NULL; + } + +out: + TRACE_EXIT_RES(ret); + return ret; +} + +static int isert_cm_disconnect_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct isert_connection *isert_conn = cm_id->qp->qp_context; + + isert_conn_disconnect(isert_conn); + + return 0; +} + +static int isert_cm_timewait_exit_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct isert_connection *isert_conn = cm_id->qp->qp_context; + + isert_sched_conn_closed(isert_conn); + return 0; +} + +static const char *cm_event_type_str(enum rdma_cm_event_type ev_type) +{ + switch (ev_type) { + case RDMA_CM_EVENT_ADDR_RESOLVED: + return "ADDRESS_RESOLVED"; + case RDMA_CM_EVENT_ADDR_ERROR: + return "ADDESS_ERROR"; + case RDMA_CM_EVENT_ROUTE_RESOLVED: + return "ROUTE_RESOLVED"; + case RDMA_CM_EVENT_ROUTE_ERROR: + return "ROUTE_ERROR"; + case RDMA_CM_EVENT_CONNECT_REQUEST: + return "CONNECT_REQUEST"; + case RDMA_CM_EVENT_CONNECT_RESPONSE: + return "CONNECT_RESPONSE"; + case RDMA_CM_EVENT_CONNECT_ERROR: + return "CONNECT_ERROR"; + case RDMA_CM_EVENT_UNREACHABLE: + return "UNREACHABLE"; + case RDMA_CM_EVENT_REJECTED: + return "REJECTED"; + case RDMA_CM_EVENT_ESTABLISHED: + return "ESTABLISHED"; + case RDMA_CM_EVENT_DISCONNECTED: + return "DISCONNECTED"; + case RDMA_CM_EVENT_DEVICE_REMOVAL: + return "DEVICE_REMOVAL"; + case RDMA_CM_EVENT_MULTICAST_JOIN: + return "MULTICAST_JOIN"; + case RDMA_CM_EVENT_MULTICAST_ERROR: + return "MULTICAST_ERROR"; + case RDMA_CM_EVENT_ADDR_CHANGE: + return "ADDR_CHANGE"; + case RDMA_CM_EVENT_TIMEWAIT_EXIT: + return "TIMEWAIT_EXIT"; + default: + return "UNKNOWN"; + } +} + +static int isert_handle_failure(struct isert_connection *conn) +{ + isert_conn_disconnect(conn); + return 0; +} + +static int isert_cm_evt_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *cm_ev) +{ + enum rdma_cm_event_type ev_type; + struct isert_portal *portal; + int err = -EINVAL; + + TRACE_ENTRY(); + + if (unlikely(IS_ERR(cm_id))) { + pr_err("isert_cm_evt invalid cm_id:%p\n", cm_id); + goto out; + } + ev_type = cm_ev->event; + portal = cm_id->context; + pr_info("isert_cm_evt:%s(%d) status:%d portal:%p cm_id:%p\n", + cm_event_type_str(ev_type), ev_type, cm_ev->status, + portal, cm_id); + + switch (ev_type) { + case RDMA_CM_EVENT_CONNECT_REQUEST: + err = isert_cm_conn_req_handler(cm_id, cm_ev); + break; + + case RDMA_CM_EVENT_ESTABLISHED: + err = isert_cm_connect_handler(cm_id, cm_ev); + if (unlikely(err)) + err = isert_handle_failure(cm_id->qp->qp_context); + break; + + case RDMA_CM_EVENT_CONNECT_ERROR: + case RDMA_CM_EVENT_REJECTED: + case RDMA_CM_EVENT_ADDR_CHANGE: + case RDMA_CM_EVENT_DISCONNECTED: + err = isert_cm_disconnect_handler(cm_id, cm_ev); + break; + + case RDMA_CM_EVENT_DEVICE_REMOVAL: + isert_cm_disconnect_handler(cm_id, cm_ev); + + case RDMA_CM_EVENT_TIMEWAIT_EXIT: /* fall through */ + err = isert_cm_timewait_exit_handler(cm_id, cm_ev); + break; + + case RDMA_CM_EVENT_MULTICAST_JOIN: + case RDMA_CM_EVENT_MULTICAST_ERROR: + pr_err("UD-related event:%d, ignored\n", ev_type); + break; + + case RDMA_CM_EVENT_ADDR_RESOLVED: + case RDMA_CM_EVENT_ADDR_ERROR: + case RDMA_CM_EVENT_ROUTE_RESOLVED: + case RDMA_CM_EVENT_ROUTE_ERROR: + case RDMA_CM_EVENT_CONNECT_RESPONSE: + pr_err("Active side event:%d, ignored\n", ev_type); + break; + + /* We can receive this instead of RDMA_CM_EVENT_ESTABLISHED */ + case RDMA_CM_EVENT_UNREACHABLE: + { + struct isert_connection *isert_conn; + + isert_conn = cm_id->qp->qp_context; + set_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags); + isert_sched_conn_closed(isert_conn); + err = 0; + } + break; + + default: + pr_err("Illegal event:%d, ignored\n", ev_type); + break; + } + + if (unlikely(err)) + pr_err("Failed to handle rdma cm evt:%d, err:%d\n", + ev_type, err); + +out: + TRACE_EXIT_RES(err); + return err; +} + +/* create a portal, after listening starts all events + * are received in isert_cm_evt_handler() + */ +struct isert_portal *isert_portal_create(void) +{ + struct isert_portal *portal; + struct rdma_cm_id *cm_id; + int err; + + portal = kzalloc(sizeof(*portal), GFP_KERNEL); + if (!portal) { + pr_err("Unable to allocate struct portal\n"); + return ERR_PTR(-ENOMEM); + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) && !defined(RHEL_MAJOR) + cm_id = rdma_create_id(isert_cm_evt_handler, portal, RDMA_PS_TCP); +#else + cm_id = rdma_create_id(isert_cm_evt_handler, portal, RDMA_PS_TCP, + IB_QPT_RC); +#endif + if (IS_ERR(cm_id)) { + err = PTR_ERR(cm_id); + pr_err("Failed to create rdma id, err:%d\n", err); + return ERR_PTR(err); + } + portal->cm_id = cm_id; + + INIT_LIST_HEAD(&portal->conn_list); + isert_portal_list_add(portal); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + rdma_set_afonly(cm_id, 1); +#endif + + pr_info("Created iser portal cm_id:%p\n", cm_id); + return portal; +} + +int isert_portal_listen(struct isert_portal *portal, + struct sockaddr *sa, + size_t addr_len) +{ + int err; + + TRACE_ENTRY(); + err = rdma_bind_addr(portal->cm_id, sa); + if (err) { + pr_warn("Failed to bind rdma addr, err:%d\n", err); + goto out; + } + err = rdma_listen(portal->cm_id, ISER_LISTEN_BACKLOG); + if (err) { + pr_err("Failed rdma listen, err:%d\n", err); + goto out; + } + memcpy(&portal->addr, sa, addr_len); + + switch (sa->sa_family) { + case AF_INET: + pr_info("iser portal cm_id:%p listens on: " +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + NIPQUAD_FMT ":%d\n", portal->cm_id, + NIPQUAD(((struct sockaddr_in *)sa)->sin_addr.s_addr), +#else + "%pI4:%d\n", portal->cm_id, + &((struct sockaddr_in *)sa)->sin_addr.s_addr, +#endif + (int)ntohs(((struct sockaddr_in *)sa)->sin_port)); + + break; + case AF_INET6: + pr_info("iser portal cm_id:%p listens on: " +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + NIP6_FMT " %d\n", + portal->cm_id, + NIP6(((struct sockaddr_in6 *)sa)->sin6_addr.s_addr), +#else + "%pI6 %d\n", portal->cm_id, + &((struct sockaddr_in6 *)sa)->sin6_addr, +#endif + (int)ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); + break; + default: + pr_err("Unknown address family\n"); + err = -EINVAL; + goto out; + } + +out: + TRACE_EXIT_RES(err); + return err; +} + +void isert_portal_release(struct isert_portal *portal) +{ + struct isert_connection *conn; + + pr_info("iser portal cm_id:%p releasing\n", portal->cm_id); + + rdma_destroy_id(portal->cm_id); + + mutex_lock(&dev_list_mutex); + list_for_each_entry(conn, &portal->conn_list, portal_node) + isert_conn_disconnect(conn); + mutex_unlock(&dev_list_mutex); + + isert_portal_list_remove(portal); +} + +struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len) +{ + struct isert_portal *portal; + int err; + + portal = isert_portal_create(); + if (IS_ERR(portal)) + return portal; + + err = isert_portal_listen(portal, sa, addr_len); + if (err) { + isert_portal_release(portal); + portal = ERR_PTR(err); + } + return portal; +} + +struct isert_portal *isert_portal_add_addr_any(u16 port) +{ + struct sockaddr_storage sa_any; + size_t addr_len; + struct isert_portal *portal; + + create_sockaddr_any((struct sockaddr *)&sa_any, port, &addr_len); + + portal = isert_portal_start((struct sockaddr *)&sa_any, addr_len); + if (IS_ERR(portal)) + portal = NULL; + + return portal; +} diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c new file mode 100644 index 000000000..0f0ea73db --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -0,0 +1,536 @@ +/* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include +#include + +#include "isert.h" +#include "isert_dbg.h" +#include "iscsit_transport.h" +#include "iser_datamover.h" + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +unsigned long isert_trace_flag = ISERT_DEFAULT_LOG_FLAGS; +unsigned long iscsi_trace_flag = ISERT_DEFAULT_LOG_FLAGS; +#endif + +static unsigned int isert_nr_devs = ISERT_NR_DEVS; +module_param(isert_nr_devs, uint, S_IRUGO); +MODULE_PARM_DESC(isert_nr_devs, + "Maximum concurrent number of connection requests to handle."); + +static void isert_do_close_conn(struct iscsi_conn *conn) +{ + isert_close_connection(conn); + start_close_conn(conn); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void isert_close_conn_fn(void *ctx) +#else +static void isert_close_conn_fn(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct isert_close_conn_work *conn_work = ctx; +#else + struct isert_close_conn_work *conn_work = container_of(work, + struct isert_close_conn_work, close_work); +#endif + struct iscsi_conn *conn = conn_work->conn; + + /* Take care of case where our connection is being closed + * without being connected to a session - if connection allocation + * failed for some reason */ + if (unlikely(!conn->session)) + isert_free_connection(conn); + else + isert_do_close_conn(conn); + + kfree(conn_work); +} + +static void isert_mark_conn_closed(struct iscsi_conn *conn, int flags) +{ + struct isert_close_conn_work *conn_work; + + TRACE_ENTRY(); + if (flags & ISCSI_CONN_ACTIVE_CLOSE) + conn->active_close = 1; + if (flags & ISCSI_CONN_DELETING) + conn->deleting = 1; + + conn->read_state = 0; + + if (!conn->closing) { + conn->closing = 1; + + conn_work = kmalloc(sizeof(*conn_work), GFP_ATOMIC); + if (unlikely(!conn_work)) { + PRINT_CRIT_ERROR("Unable to allocate isert_close_conn_work for conn %p\n", + conn); + goto out; + } + + conn_work->conn = conn; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&conn_work->close_work, isert_close_conn_fn, + conn_work); +#else + INIT_WORK(&conn_work->close_work, isert_close_conn_fn); +#endif + schedule_work(&conn_work->close_work); + } + +out: + TRACE_EXIT(); +} + +static void isert_close_conn(struct iscsi_conn *conn, int flags) +{ +} + +static int isert_receive_cmnd_data(struct iscsi_cmnd *cmnd) +{ +#ifdef CONFIG_SCST_EXTRACHECKS + if (cmnd->scst_state == ISCSI_CMD_STATE_RX_CMD) + TRACE_DBG("cmnd %p is still in RX_CMD state", + cmnd); +#endif + EXTRACHECKS_BUG_ON(cmnd->scst_state != ISCSI_CMD_STATE_AFTER_PREPROC); + return 0; +} + +static void isert_update_len_sn(struct iscsi_cmnd *cmnd) +{ + TRACE_ENTRY(); + + iscsi_cmnd_set_length(&cmnd->pdu); + switch (cmnd_opcode(cmnd)) { + case ISCSI_OP_NOP_IN: + if (cmnd->pdu.bhs.itt == ISCSI_RESERVED_TAG) + cmnd->pdu.bhs.sn = (__force u32)cmnd_set_sn(cmnd, 0); + else + cmnd_set_sn(cmnd, 1); + break; + case ISCSI_OP_SCSI_RSP: + cmnd_set_sn(cmnd, 1); + break; + case ISCSI_OP_SCSI_TASK_MGT_RSP: + cmnd_set_sn(cmnd, 1); + break; + case ISCSI_OP_TEXT_RSP: + cmnd_set_sn(cmnd, 1); + break; + case ISCSI_OP_SCSI_DATA_IN: + { + struct iscsi_data_in_hdr *rsp = + (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs; + + cmnd_set_sn(cmnd, (rsp->flags & ISCSI_FLG_FINAL) ? 1 : 0); + break; + } + case ISCSI_OP_LOGOUT_RSP: + cmnd_set_sn(cmnd, 1); + break; + case ISCSI_OP_R2T: + cmnd->pdu.bhs.sn = (__force u32)cmnd_set_sn(cmnd, 0); + break; + case ISCSI_OP_ASYNC_MSG: + cmnd_set_sn(cmnd, 1); + break; + case ISCSI_OP_REJECT: + cmnd_set_sn(cmnd, 1); + break; + default: + PRINT_ERROR("Unexpected cmnd op %x", cmnd_opcode(cmnd)); + break; + } + + TRACE_EXIT(); +} + +static int isert_process_all_writes(struct iscsi_conn *conn) +{ + struct iscsi_cmnd *cmnd; + int res = 0; + + TRACE_ENTRY(); + + while ((cmnd = iscsi_get_send_cmnd(conn)) != NULL) { + isert_update_len_sn(cmnd); + conn_get(conn); + isert_pdu_tx(cmnd); + } + + TRACE_EXIT_RES(res); + return res; +} + +static int isert_send_locally(struct iscsi_cmnd *req, unsigned int cmd_count) +{ + int res = 0; + + TRACE_ENTRY(); + + req_cmnd_pre_release(req); + res = isert_process_all_writes(req->conn); + cmnd_put(req); + + TRACE_EXIT_RES(res); + return res; +} + +static struct iscsi_cmnd *isert_cmnd_alloc(struct iscsi_conn *conn, + struct iscsi_cmnd *parent) +{ + struct iscsi_cmnd *cmnd; + + TRACE_ENTRY(); + + if (likely(parent)) + cmnd = isert_alloc_scsi_rsp_pdu(conn); + else + cmnd = isert_alloc_scsi_fake_pdu(conn); + + iscsi_cmnd_init(conn, cmnd, parent); + + TRACE_EXIT(); + return cmnd; +} + +static void isert_cmnd_free(struct iscsi_cmnd *cmnd) +{ + TRACE_ENTRY(); + +#ifdef CONFIG_SCST_EXTRACHECKS + if (unlikely(cmnd->on_write_list || cmnd->on_write_timeout_list)) { + struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); + + PRINT_CRIT_ERROR("cmnd %p still on some list?, %x, %x, %x, " + "%x, %x, %x, %x", cmnd, req->opcode, req->scb[0], + req->flags, req->itt, be32_to_cpu(req->data_length), + req->cmd_sn, + be32_to_cpu((__force __be32)(cmnd->pdu.datasize))); + + if (unlikely(cmnd->parent_req)) { + struct iscsi_scsi_cmd_hdr *preq = + cmnd_hdr(cmnd->parent_req); + PRINT_CRIT_ERROR("%p %x %u", preq, preq->opcode, + preq->scb[0]); + } + sBUG(); + } +#endif + if (cmnd->parent_req) + isert_release_tx_pdu(cmnd); + else + isert_release_rx_pdu(cmnd); + + TRACE_EXIT(); +} + +static void isert_preprocessing_done(struct iscsi_cmnd *req) +{ + req->scst_state = ISCSI_CMD_STATE_AFTER_PREPROC; +} + +static void isert_set_sense_data(struct iscsi_cmnd *rsp, + const u8 *sense_buf, int sense_len) +{ + u8 *buf; + + buf = sg_virt(rsp->sg) + ISER_HDRS_SZ; + + memcpy(buf, &rsp->sense_hdr, sizeof(rsp->sense_hdr)); + memcpy(&buf[sizeof(rsp->sense_hdr)], sense_buf, sense_len); +} + +static void isert_set_req_data(struct iscsi_cmnd *req, struct iscsi_cmnd *rsp) +{ + memcpy(sg_virt(rsp->sg) + ISER_HDRS_SZ, + sg_virt(req->sg) + ISER_HDRS_SZ, req->bufflen); + rsp->bufflen = req->bufflen; +} + +static void isert_send_data_rsp(struct iscsi_cmnd *req, u8 *sense, + int sense_len, u8 status, int is_send_status) +{ + struct iscsi_cmnd *rsp; + + TRACE_ENTRY(); + + sBUG_ON(!is_send_status); + + rsp = create_status_rsp(req, status, sense, sense_len); + + isert_update_len_sn(rsp); + + conn_get(rsp->conn); + if (status != SAM_STAT_CHECK_CONDITION) + isert_send_data_in(req, rsp); + else + isert_pdu_tx(rsp); + + TRACE_EXIT(); +} + +static void isert_make_conn_wr_active(struct iscsi_conn *conn) +{ + isert_process_all_writes(conn); +} + +static int isert_conn_activate(struct iscsi_conn *conn) +{ + return 0; +} + +static void isert_conn_free(struct iscsi_conn *conn) +{ + isert_free_connection(conn); +} + +int isert_handle_close_connection(struct iscsi_conn *conn) +{ + isert_mark_conn_closed(conn, 0); + return 0; +} + +int isert_pdu_rx(struct iscsi_cmnd *cmnd) +{ + int res = 0; + scst_data_direction dir; + + TRACE_ENTRY(); + +#ifdef CONFIG_SCST_EXTRACHECKS + cmnd->conn->rd_task = current; +#endif + iscsi_cmnd_init(cmnd->conn, cmnd, NULL); + cmnd_rx_start(cmnd); + + if (unlikely(!cmnd->scst_cmd)) { + cmnd_rx_end(cmnd); + goto out; + } + + if (unlikely(scst_cmd_prelim_completed(cmnd->scst_cmd) || + unlikely(cmnd->prelim_compl_flags != 0))) { + set_bit(ISCSI_CMD_PRELIM_COMPLETED, &cmnd->prelim_compl_flags); + cmnd_rx_end(cmnd); + goto out; + } + + dir = scst_cmd_get_data_direction(cmnd->scst_cmd); + + if (dir & SCST_DATA_WRITE) { + res = iscsi_cmnd_set_write_buf(cmnd); + if (unlikely(res)) + goto out; + res = isert_request_data_out(cmnd); + cmnd->r2t_len_to_receive = 0; + cmnd->r2t_len_to_send = 0; + cmnd->outstanding_r2t = 0; + } else { + cmnd_rx_end(cmnd); + } + +out: + TRACE_EXIT_RES(res); + return res; +} + +int isert_data_out_ready(struct iscsi_cmnd *cmnd) +{ + int res = 0; + + TRACE_ENTRY(); +#ifdef CONFIG_SCST_EXTRACHECKS + cmnd->conn->rd_task = current; +#endif + cmnd_rx_end(cmnd); + + TRACE_EXIT_RES(res); + return res; +} + +int isert_data_in_sent(struct iscsi_cmnd *din) +{ + return 0; +} + +void isert_pdu_err(struct iscsi_cmnd *pdu) +{ + struct iscsi_conn *conn = pdu->conn; + + if (!conn->session) /* we are still in login phase */ + return; + + if (pdu->parent_req) { + rsp_cmnd_release(pdu); + conn_put(conn); + } else { + /* + * we will get multiple pdu errors + * for same PDU with multiple RDMAs case + */ + if (pdu->on_write_timeout_list) + req_cmnd_release(pdu); + } +} + +int isert_pdu_sent(struct iscsi_cmnd *pdu) +{ + struct iscsi_conn *conn = pdu->conn; + int res = 0; + + TRACE_ENTRY(); + + if (unlikely(pdu->should_close_conn)) { + if (pdu->should_close_all_conn) { + struct iscsi_target *target = pdu->conn->session->target; + + PRINT_INFO("Closing all connections for target %x at " + "initiator's %s request", target->tid, + conn->session->initiator_name); + mutex_lock(&target->target_mutex); + target_del_all_sess(target, 0); + mutex_unlock(&target->target_mutex); + } else { + PRINT_INFO("Closing connection at initiator's %s " + "request", conn->session->initiator_name); + mark_conn_closed(conn); + } + } + + /* we may get NULL parent req for login response */ + if (likely(pdu->parent_req)) { + rsp_cmnd_release(pdu); + conn_put(conn); + } + + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t isert_get_initiator_ip(struct iscsi_conn *conn, + char *buf, int size) +{ + int pos; + struct sockaddr_storage ss; + size_t addr_len; + + TRACE_ENTRY(); + + isert_get_peer_addr(conn, (struct sockaddr *)&ss, &addr_len); + + switch (ss.ss_family) { + case AF_INET: + pos = scnprintf(buf, size, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + "%u.%u.%u.%u", + NIPQUAD(((struct sockaddr_in *)&ss)->sin_addr.s_addr)); +#else + "%pI4", &((struct sockaddr_in *)&ss)->sin_addr.s_addr); +#endif + break; + case AF_INET6: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + pos = scnprintf(buf, size, + "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", + NIP6(((struct sockaddr_in6 *)&ss)->sin6_addr.s_addr)); +#else + pos = scnprintf(buf, size, "[%p6]", + &((struct sockaddr_in6 *)&ss)->sin6_addr); +#endif + break; + default: + pos = scnprintf(buf, size, "Unknown family %d", + ss.ss_family); + break; + } + + TRACE_EXIT_RES(pos); + return pos; +} + +static struct iscsit_transport isert_transport = { + .owner = THIS_MODULE, + .name = "iSER", + .transport_type = ISCSI_RDMA, + .iscsit_conn_alloc = isert_conn_alloc, + .iscsit_conn_activate = isert_conn_activate, + .iscsit_conn_free = isert_conn_free, + .iscsit_alloc_cmd = isert_cmnd_alloc, + .iscsit_free_cmd = isert_cmnd_free, + .iscsit_preprocessing_done = isert_preprocessing_done, + .iscsit_send_data_rsp = isert_send_data_rsp, + .iscsit_make_conn_wr_active = isert_make_conn_wr_active, + .iscsit_get_initiator_ip = isert_get_initiator_ip, + .iscsit_send_locally = isert_send_locally, + .iscsit_mark_conn_closed = isert_mark_conn_closed, + .iscsit_conn_close = isert_close_conn, + .iscsit_set_sense_data = isert_set_sense_data, + .iscsit_set_req_data = isert_set_req_data, + .iscsit_receive_cmnd_data = isert_receive_cmnd_data, + .iscsit_close_all_portals = isert_close_all_portals, +}; + +static void isert_cleanup_module(void) +{ + iscsit_unregister_transport(&isert_transport); + isert_cleanup_login_devs(); +} + +static int __init isert_init_module(void) +{ + int ret; + + ret = iscsit_register_transport(&isert_transport); + if (ret) + return ret; + + ret = isert_init_login_devs(isert_nr_devs); + + return ret; +} + +MODULE_AUTHOR("Yan Burman"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("iSER target transport driver"); + +module_init(isert_init_module); +module_exit(isert_cleanup_module); diff --git a/iscsi-scst/kernel/isert-scst/isert.h b/iscsi-scst/kernel/isert-scst/isert.h new file mode 100644 index 000000000..56a7d3b09 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/isert.h @@ -0,0 +1,137 @@ +/* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef __ISERT_H__ +#define __ISERT_H__ + +#include +#include +#include +#include /* size_t, dev_t */ +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +#include +#else +#include +#endif + +#ifdef INSIDE_KERNEL_TREE +#include +#include +#include +#else +#include "isert_scst.h" +#include "iscsi_scst.h" +#include "iscsi.h" +#endif + +#include "iser_hdr.h" + +struct iscsi_conn; + +#define ISERT_NR_DEVS 64 + +struct isert_listener_dev { + struct device *dev; + struct cdev cdev; + dev_t devno; + wait_queue_head_t waitqueue; + spinlock_t conn_lock; + struct list_head new_conn_list; + struct list_head curr_conn_list; + struct isert_addr_info info; + atomic_t available; + void *portal_h[ISERT_MAX_PORTALS]; + int free_portal_idx; +}; + +struct isert_close_conn_work { + struct work_struct close_work; + struct iscsi_conn *conn; +}; + +enum isert_conn_dev_state { + CS_INIT, + CS_REQ_BHS, + CS_REQ_DATA, + CS_REQ_FINISHED, + CS_RSP_BHS, + CS_RSP_DATA, + CS_RSP_FINISHED, + CS_DISCONNECTED, +}; + +struct isert_conn_dev { + struct device *dev; + struct cdev cdev; + dev_t devno; + wait_queue_head_t waitqueue; + struct list_head conn_list_entry; + struct iscsi_conn *conn; + unsigned int idx; + int occupied; + spinlock_t pdu_lock; + struct iscsi_cmnd *login_req; + struct iscsi_cmnd *login_rsp; + atomic_t available; + size_t read_len; + char *read_buf; + size_t write_len; + char *write_buf; + void *sg_virt; + struct page *pages[DIV_ROUND_UP(ISCSI_LOGIN_MAX_RDSL, PAGE_SIZE)]; + enum isert_conn_dev_state state; + int is_discovery; + struct timer_list tmo_timer; + int timer_active; +}; + +#define ISER_CONN_DEV_PREFIX "isert/conn" + +/* isert_login.c */ +int __init isert_init_login_devs(unsigned int ndevs); +void isert_cleanup_login_devs(void); +int isert_conn_alloc(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, + struct iscsi_conn **new_conn, + struct iscsit_transport *t); +int isert_handle_close_connection(struct iscsi_conn *conn); +void isert_close_all_portals(void); + +#endif /* __ISERT_H__ */ diff --git a/iscsi-scst/kernel/isert-scst/isert_dbg.h b/iscsi-scst/kernel/isert-scst/isert_dbg.h new file mode 100644 index 000000000..37472fe60 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/isert_dbg.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2010 ID7 Ltd. + * Copyright (C) 2010 - 2013 SCST Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ISERT_DBG_H +#define ISERT_DBG_H + +#include + +#ifdef LOG_PREFIX +#undef LOG_PREFIX +#endif + +#define LOG_PREFIX "isert" /* Prefix for SCST tracing macros. */ +#ifdef INSIDE_KERNEL_TREE +#include +#else +#include +#endif + +#ifdef CONFIG_SCST_DEBUG +#define ISERT_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_LINE | TRACE_PID | \ + TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MGMT_DEBUG | \ + TRACE_MINOR | TRACE_SPECIAL) +#else +#define ISERT_DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \ + TRACE_SPECIAL) +#endif + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +extern unsigned long isert_trace_flag; +#ifdef trace_flag +#undef trace_flag +#endif +#define trace_flag isert_trace_flag +#endif + +#endif diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c new file mode 100644 index 000000000..5beee62a2 --- /dev/null +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -0,0 +1,940 @@ +/* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include +#include +#include + +#ifdef INSIDE_KERNEL_TREE +#include +#else +#include "iscsi.h" +#endif + +#include "isert.h" +#include "isert_dbg.h" +#include "iser_datamover.h" + +static unsigned int n_devs; + +static int isert_major; + +static struct isert_conn_dev *isert_conn_devices; + +static struct isert_listener_dev isert_listen_dev; + +static struct class *isert_class; + +static struct isert_conn_dev *get_available_dev(struct isert_listener_dev *dev, + struct iscsi_conn *conn) +{ + unsigned int i; + struct isert_conn_dev *res = NULL; + + spin_lock(&dev->conn_lock); + for (i = 0; i < n_devs; ++i) { + if (!isert_conn_devices[i].occupied) { + res = &isert_conn_devices[i]; + res->occupied = 1; + res->conn = conn; + isert_set_priv(conn, res); + list_add(&res->conn_list_entry, &dev->new_conn_list); + break; + } + } + spin_unlock(&dev->conn_lock); + + return res; +} + +static void isert_del_timer(struct isert_conn_dev *dev) +{ + if (dev->timer_active) { + del_timer_sync(&dev->tmo_timer); + dev->timer_active = 0; + } +} + +static void release_dev(struct isert_conn_dev *dev) +{ + isert_del_timer(dev); + + spin_lock(&isert_listen_dev.conn_lock); + dev->occupied = 0; + list_del_init(&dev->conn_list_entry); + dev->state = CS_INIT; + spin_unlock(&isert_listen_dev.conn_lock); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void isert_login_close_conn_fn(void *ctx) +#else +static void isert_login_close_conn_fn(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct isert_close_conn_work *conn_work = ctx; +#else + struct isert_close_conn_work *conn_work = container_of(work, + struct isert_close_conn_work, close_work); +#endif + struct iscsi_conn *conn = conn_work->conn; + + isert_close_connection(conn); + + kfree(conn_work); +} + +static void isert_conn_timer_fn(unsigned long arg) +{ + struct isert_conn_dev *conn_dev = (struct isert_conn_dev *)arg; + struct isert_close_conn_work *conn_work; + + TRACE_ENTRY(); + + conn_dev->timer_active = 0; + + PRINT_ERROR("Timeout on connection %p\n", conn_dev->conn); + + conn_work = kmalloc(sizeof(*conn_work), GFP_ATOMIC); + if (unlikely(!conn_work)) { + PRINT_CRIT_ERROR("Unable to allocate isert_close_conn_work for conn %p\n", + conn_dev->conn); + goto out; + } + + conn_work->conn = conn_dev->conn; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&conn_work->close_work, isert_login_close_conn_fn, + conn_work); +#else + INIT_WORK(&conn_work->close_work, isert_login_close_conn_fn); +#endif + schedule_work(&conn_work->close_work); + +out: + TRACE_EXIT(); +} + +static int add_new_connection(struct isert_listener_dev *dev, + struct iscsi_conn *conn) +{ + struct isert_conn_dev *conn_dev = get_available_dev(dev, conn); + int res = 0; + + TRACE_ENTRY(); + + if (!conn_dev) { + res = -ENOSPC; + goto out; + } + + init_timer(&conn_dev->tmo_timer); + conn_dev->tmo_timer.function = isert_conn_timer_fn; + conn_dev->tmo_timer.expires = jiffies + 120 * HZ; + conn_dev->tmo_timer.data = (unsigned long)conn_dev; + add_timer(&conn_dev->tmo_timer); + conn_dev->timer_active = 1; + wake_up(&dev->waitqueue); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static bool have_new_connection(struct isert_listener_dev *dev) +{ + bool ret; + + spin_lock(&dev->conn_lock); + ret = !list_empty(&dev->new_conn_list); + spin_unlock(&dev->conn_lock); + + return ret; +} + +int isert_conn_alloc(struct iscsi_session *session, + struct iscsi_kern_conn_info *info, + struct iscsi_conn **new_conn, + struct iscsit_transport *t) +{ + int res = 0; + struct isert_conn_dev *dev; + struct iscsi_conn *conn; + struct iscsi_cmnd *cmnd; + struct file *filp = fget(info->fd); + + TRACE_ENTRY(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&session->target->target_mutex); +#endif + + if (unlikely(!filp)) { + res = -EBADF; + goto out; + } + + dev = filp->private_data; + + cmnd = dev->login_rsp; + + sBUG_ON(cmnd == NULL); + dev->login_rsp = NULL; + + *new_conn = dev->conn; + res = isert_set_session_params(dev->conn, &session->sess_params, + &session->tgt_params); + + if (!res) + dev->conn = NULL; + + fput(filp); + + conn = *new_conn; + + if (unlikely(res)) + goto cleanup_conn; + + conn->transport = t; + + res = iscsi_init_conn(session, info, conn); + if (unlikely(res)) + goto cleanup_conn; + +#ifndef CONFIG_SCST_PROC + res = conn_sysfs_add(conn); + if (unlikely(res)) + goto cleanup_iscsi_conn; +#endif + + list_add_tail(&conn->conn_list_entry, &session->conn_list); + + conn->rd_state = 1; + res = isert_login_rsp_tx(cmnd, true, false); + vunmap(dev->sg_virt); + dev->sg_virt = NULL; + + if (unlikely(res)) + goto cleanup_iscsi_conn; + + goto out; + +cleanup_iscsi_conn: + if (conn->nop_in_interval > 0) + cancel_delayed_work_sync(&conn->nop_in_delayed_work); + list_del(&conn->conn_list_entry); +cleanup_conn: + conn->session = NULL; + isert_close_connection(conn); +out: + TRACE_EXIT_RES(res); + return res; +} + +static unsigned int isert_listen_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct isert_listener_dev *dev = filp->private_data; + unsigned int mask = 0; + + poll_wait(filp, &dev->waitqueue, wait); + + if (have_new_connection(dev)) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static int isert_listen_open(struct inode *inode, struct file *filp) +{ + struct isert_listener_dev *dev; + + dev = container_of(inode->i_cdev, struct isert_listener_dev, cdev); + + if (!atomic_dec_and_test(&dev->available)) { + atomic_inc(&dev->available); + return -EBUSY; /* already open */ + } + + filp->private_data = dev; /* for other methods */ + + return 0; +} + +static int isert_listen_release(struct inode *inode, struct file *filp) +{ + struct isert_listener_dev *dev = filp->private_data; + struct isert_conn_dev *conn_dev; + + /* No need for locking here, since the chardev is being closed */ + while (!list_empty(&dev->new_conn_list)) { + conn_dev = list_first_entry(&dev->new_conn_list, + struct isert_conn_dev, + conn_list_entry); + + isert_del_timer(conn_dev); + if (conn_dev->conn) { + isert_close_connection(conn_dev->conn); + conn_dev->conn = NULL; + } + list_del(&conn_dev->conn_list_entry); + } + + atomic_inc(&dev->available); + return 0; +} + +static ssize_t isert_listen_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct isert_listener_dev *dev = filp->private_data; + struct isert_conn_dev *conn_dev; + int res = 0; + char k_buff[sizeof("/dev/") + sizeof(ISER_CONN_DEV_PREFIX) + 3 + 1]; + + TRACE_ENTRY(); + + if (!have_new_connection(dev)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + res = wait_event_freezable(dev->waitqueue, + !have_new_connection(dev)); + if (res < 0) + goto out; + } + + sBUG_ON(list_empty(&dev->new_conn_list)); + + spin_lock(&dev->conn_lock); + conn_dev = list_first_entry(&dev->new_conn_list, struct isert_conn_dev, + conn_list_entry); + list_move(&conn_dev->conn_list_entry, &dev->curr_conn_list); + spin_unlock(&dev->conn_lock); + + res = snprintf(k_buff, sizeof(k_buff), "/dev/"ISER_CONN_DEV_PREFIX"%d", + conn_dev->idx); + ++res; /* copy trailing \0 as well */ + + if (copy_to_user(buf, k_buff, res)) + res = -EFAULT; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static long isert_listen_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct isert_listener_dev *dev = filp->private_data; + int res = 0, rc; + void __user *ptr = (void __user *)arg; + void *portal; + + TRACE_ENTRY(); + + switch (cmd) { + case SET_LISTEN_ADDR: + rc = copy_from_user(&dev->info, ptr, sizeof(dev->info)); + if (rc != 0) { + PRINT_ERROR("Failed to copy %d user's bytes\n", rc); + res = -EFAULT; + goto out; + } + + if (dev->free_portal_idx >= ISERT_MAX_PORTALS) { + PRINT_ERROR("Maximum number of portals exceeded: %d\n", + ISERT_MAX_PORTALS); + res = -EINVAL; + goto out; + } + + portal = isert_portal_add((struct sockaddr *)&dev->info.addr, + dev->info.addr_len); + if (!portal) { + PRINT_ERROR("Unable to add portal of size %zu\n", + dev->info.addr_len); + res = -EINVAL; + goto out; + } + dev->portal_h[dev->free_portal_idx++] = portal; + break; + + default: + PRINT_ERROR("Invalid ioctl cmd %x", cmd); + res = -EINVAL; + } + +out: + TRACE_EXIT_RES(res); + return res; +} + +int isert_conn_established(struct iscsi_conn *iscsi_conn, + struct sockaddr *from_addr, int addr_len) +{ + return add_new_connection(&isert_listen_dev, iscsi_conn); +} + +int isert_connection_closed(struct iscsi_conn *iscsi_conn) +{ + int res = 0; + + TRACE_ENTRY(); + + if (iscsi_conn->rd_state) { + res = isert_handle_close_connection(iscsi_conn); + } else { + struct isert_conn_dev *dev = isert_get_priv(iscsi_conn); + + if (dev) { + isert_del_timer(dev); + dev->state = CS_DISCONNECTED; + if (dev->login_req) { + res = isert_task_abort(dev->login_req); + dev->login_req = NULL; + } + + dev->conn = NULL; + wake_up(&dev->waitqueue); + } + + isert_free_connection(iscsi_conn); + } + + TRACE_EXIT_RES(res); + return res; +} + +static bool will_read_block(struct isert_conn_dev *dev) +{ + bool res; + + spin_lock(&dev->pdu_lock); + res = (dev->login_req == NULL) && (dev->state != CS_DISCONNECTED); + spin_unlock(&dev->pdu_lock); + + return res; +} + +static int isert_open(struct inode *inode, struct file *filp) +{ + struct isert_conn_dev *dev; /* device information */ + int res = 0; + + TRACE_ENTRY(); + + dev = container_of(inode->i_cdev, struct isert_conn_dev, cdev); + + if (!atomic_dec_and_test(&dev->available)) { + atomic_inc(&dev->available); + res = -EBUSY; /* already open */ + goto out; + } + + filp->private_data = dev; /* for other methods */ + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int isert_release(struct inode *inode, struct file *filp) +{ + struct isert_conn_dev *dev = filp->private_data; + int res = 0; + + TRACE_ENTRY(); + + vunmap(dev->sg_virt); + dev->is_discovery = 0; + + if (dev->conn) { + isert_close_connection(dev->conn); + dev->conn = NULL; + } + + release_dev(dev); + atomic_inc(&dev->available); + + TRACE_EXIT_RES(res); + return res; +} + +static char *isert_vmap_sg(struct page **pages, struct scatterlist *sgl, + int n_ents) +{ + unsigned int i; + struct scatterlist *sg; + void *vaddr; + + for_each_sg(sgl, sg, n_ents, i) + pages[i] = sg_page(sg); + + vaddr = vmap(pages, n_ents, 0, PAGE_KERNEL); + + return vaddr; +} + +static ssize_t isert_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct isert_conn_dev *dev = filp->private_data; + size_t to_read; + + if (will_read_block(dev)) { + int ret; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_freezable(dev->waitqueue, + !will_read_block(dev)); + if (ret < 0) + return ret; + } + + if (dev->state == CS_DISCONNECTED) + return -EPIPE; + + to_read = min(count, dev->read_len); + if (copy_to_user(buf, dev->read_buf, to_read)) + return -EFAULT; + + dev->read_len -= to_read; + dev->read_buf += to_read; + + switch (dev->state) { + case CS_REQ_BHS: + if (dev->read_len == 0) { + dev->read_len = dev->login_req->bufflen; + dev->sg_virt = isert_vmap_sg(dev->pages, + dev->login_req->sg, + dev->login_req->sg_cnt); + if (!dev->sg_virt) + return -ENOMEM; + dev->read_buf = dev->sg_virt + ISER_HDRS_SZ; + dev->state = CS_REQ_DATA; + } + break; + + case CS_REQ_DATA: + if (dev->read_len == 0) { + vunmap(dev->sg_virt); + dev->sg_virt = NULL; + + spin_lock(&dev->pdu_lock); + dev->login_req = NULL; + dev->state = CS_REQ_FINISHED; + spin_unlock(&dev->pdu_lock); + } + break; + + default: + sBUG(); + } + + return to_read; +} + +static ssize_t isert_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct isert_conn_dev *dev = filp->private_data; + size_t to_write; + + if (dev->state == CS_DISCONNECTED) + return -EPIPE; + + to_write = min(count, dev->write_len); + if (copy_from_user(dev->write_buf, buf, to_write)) + return -EFAULT; + + dev->write_len -= to_write; + dev->write_buf += to_write; + + switch (dev->state) { + case CS_RSP_BHS: + if (dev->write_len == 0) { + dev->state = CS_RSP_DATA; + dev->sg_virt = isert_vmap_sg(dev->pages, + dev->login_rsp->sg, + dev->login_rsp->sg_cnt); + if (!dev->sg_virt) + return -ENOMEM; + dev->write_buf = dev->sg_virt + ISER_HDRS_SZ; + dev->write_len = dev->login_rsp->bufflen - + sizeof(dev->login_rsp->pdu.bhs); + iscsi_cmnd_get_length(&dev->login_rsp->pdu); + } + break; + + case CS_RSP_DATA: + break; + + default: + sBUG(); + } + + return to_write; +} + +static bool is_last_login_rsp(struct iscsi_login_rsp_hdr *rsp) +{ + return (rsp->flags & ISCSI_FLG_TRANSIT) && + ((rsp->flags & ISCSI_FLG_NSG_MASK) == ISCSI_FLG_NSG_FULL_FEATURE); +} + +static long isert_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct isert_conn_dev *dev = filp->private_data; + int res = 0, rc; + int val; + void __user *ptr = (void __user *)arg; + struct iscsi_cmnd *cmnd; + + TRACE_ENTRY(); + + if (dev->state == CS_DISCONNECTED) { + res = -EPIPE; + goto out; + } + + switch (cmd) { + case RDMA_CORK: + rc = copy_from_user(&val, ptr, sizeof(val)); + if (unlikely(rc != 0)) { + PRINT_ERROR("Failed to copy %d user's bytes", rc); + res = -EFAULT; + goto out; + } + if (val) { + if (!dev->login_rsp) { + cmnd = isert_alloc_login_rsp_pdu(dev->conn); + if (!cmnd) { + res = -ENOMEM; + goto out; + } + dev->login_rsp = cmnd; + dev->write_buf = (char *)&cmnd->pdu.bhs; + dev->write_len = sizeof(cmnd->pdu.bhs); + dev->state = CS_RSP_BHS; + } + } else { + struct iscsi_login_rsp_hdr *rsp; + bool last; + + if (!dev->login_rsp) { + res = -EINVAL; + goto out; + } + + dev->state = CS_RSP_FINISHED; + rsp = (struct iscsi_login_rsp_hdr *)(&dev->login_rsp->pdu.bhs); + last = is_last_login_rsp(rsp); + + dev->login_rsp->bufflen -= dev->write_len; + + if (!last || dev->is_discovery) { + res = isert_login_rsp_tx(dev->login_rsp, + last, + dev->is_discovery); + vunmap(dev->sg_virt); + dev->sg_virt = NULL; + dev->login_rsp = NULL; + } + } + break; + + case GET_PORTAL_ADDR: + { + struct isert_addr_info addr; + + res = isert_get_target_addr(dev->conn, + (struct sockaddr *)&addr.addr, + &addr.addr_len); + if (unlikely(res)) + goto out; + + rc = copy_to_user(ptr, &addr, sizeof(addr)); + if (rc) + res = -EFAULT; + } + break; + + case DISCOVERY_SESSION: + rc = copy_from_user(&val, ptr, sizeof(val)); + if (unlikely(rc != 0)) { + PRINT_ERROR("Failed to copy %d user's bytes", rc); + res = -EFAULT; + goto out; + } + dev->is_discovery = val; + break; + + default: + PRINT_ERROR("Invalid ioctl cmd %x", cmd); + res = -EINVAL; + } + +out: + TRACE_EXIT_RES(res); + return res; +} + +static unsigned int isert_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct isert_conn_dev *dev = filp->private_data; + unsigned int mask = 0; + + poll_wait(filp, &dev->waitqueue, wait); + + if (!dev->conn) + mask |= POLLHUP | POLLERR; + if (!will_read_block(dev)) + mask |= POLLIN | POLLRDNORM; + + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +int isert_login_req_rx(struct iscsi_cmnd *login_req) +{ + struct isert_conn_dev *dev = isert_get_priv(login_req->conn); + int res = 0; + + TRACE_ENTRY(); + + if (!dev) { + PRINT_ERROR("Received PDU %p on invalid connection\n", + login_req); + res = -EINVAL; + goto out; + } + + sBUG_ON(dev->login_req != NULL); + + spin_lock(&dev->pdu_lock); + dev->login_req = login_req; + dev->read_len = sizeof(login_req->pdu.bhs); + dev->read_buf = (char *)&login_req->pdu.bhs; + dev->state = CS_REQ_BHS; + spin_unlock(&dev->pdu_lock); + + wake_up(&dev->waitqueue); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static dev_t devno; + +static const struct file_operations listener_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = isert_listen_read, + .unlocked_ioctl = isert_listen_ioctl, + .compat_ioctl = isert_listen_ioctl, + .poll = isert_listen_poll, + .open = isert_listen_open, + .release = isert_listen_release, +}; + +static const struct file_operations conn_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = isert_read, + .write = isert_write, + .unlocked_ioctl = isert_ioctl, + .compat_ioctl = isert_ioctl, + .poll = isert_poll, + .open = isert_open, + .release = isert_release, +}; + +static void __init isert_setup_cdev(struct isert_conn_dev *dev, + unsigned int index) +{ + int err; + + TRACE_ENTRY(); + + dev->devno = MKDEV(isert_major, index + 1); + + cdev_init(&dev->cdev, &conn_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &conn_fops; + dev->idx = index; + init_waitqueue_head(&dev->waitqueue); + dev->login_req = NULL; + dev->login_rsp = NULL; + spin_lock_init(&dev->pdu_lock); + atomic_set(&dev->available, 1); + dev->state = CS_INIT; + err = cdev_add(&dev->cdev, dev->devno, 1); + /* Fail gracefully if need be */ + if (err) + PRINT_ERROR("Error %d adding "ISER_CONN_DEV_PREFIX"%d", err, + index); + + dev->dev = device_create(isert_class, NULL, dev->devno, NULL, + ISER_CONN_DEV_PREFIX"%d", index); + + TRACE_EXIT(); +} + +static void __init isert_setup_listener_cdev(struct isert_listener_dev *dev) +{ + int err; + + TRACE_ENTRY(); + + dev->devno = MKDEV(isert_major, 0); + + cdev_init(&dev->cdev, &listener_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &listener_fops; + init_waitqueue_head(&dev->waitqueue); + INIT_LIST_HEAD(&dev->new_conn_list); + INIT_LIST_HEAD(&dev->curr_conn_list); + spin_lock_init(&dev->conn_lock); + atomic_set(&dev->available, 1); + err = cdev_add(&dev->cdev, dev->devno, 1); + /* Fail gracefully if need be */ + if (err) + PRINT_ERROR("Error %d adding isert_scst", err); + + dev->dev = device_create(isert_class, NULL, dev->devno, NULL, + "isert_scst"); + + TRACE_EXIT(); +} + +int __init isert_init_login_devs(unsigned int ndevs) +{ + int res; + unsigned int i; + + TRACE_ENTRY(); + + n_devs = ndevs; + + res = alloc_chrdev_region(&devno, 0, n_devs, + "isert_scst"); + isert_major = MAJOR(devno); + + if (res < 0) { + PRINT_ERROR("isert: can't get major %d\n", isert_major); + goto out; + } + + /* + * allocate the devices -- we can't have them static, as the number + * can be specified at load time + */ + isert_conn_devices = kzalloc(n_devs * sizeof(struct isert_conn_dev), + GFP_KERNEL); + if (!isert_conn_devices) { + res = -ENOMEM; + goto fail; /* Make this more graceful */ + } + + isert_class = class_create(THIS_MODULE, "isert_scst"); + + isert_setup_listener_cdev(&isert_listen_dev); + + /* Initialize each device. */ + for (i = 0; i < n_devs; i++) + isert_setup_cdev(&isert_conn_devices[i], i); + + res = isert_datamover_init(); + if (res) { + PRINT_ERROR("Unable to initialize datamover: %d\n", res); + goto fail; + } + +out: + TRACE_EXIT_RES(res); + return res; +fail: + isert_cleanup_login_devs(); + goto out; +} + +void isert_close_all_portals(void) +{ + int i; + + for (i = 0; i < isert_listen_dev.free_portal_idx; ++i) + isert_portal_remove(isert_listen_dev.portal_h[i]); + isert_listen_dev.free_portal_idx = 0; +} + +void isert_cleanup_login_devs(void) +{ + int i; + + TRACE_ENTRY(); + + isert_close_all_portals(); + + isert_datamover_cleanup(); + + if (isert_conn_devices) { + for (i = 0; i < n_devs; i++) { + device_destroy(isert_class, + isert_conn_devices[i].devno); + cdev_del(&isert_conn_devices[i].cdev); + } + kfree(isert_conn_devices); + } + + device_destroy(isert_class, isert_listen_dev.devno); + cdev_del(&isert_listen_dev.cdev); + + if (isert_class) + class_destroy(isert_class); + + unregister_chrdev_region(devno, n_devs); + + TRACE_EXIT(); +} From 39d0b2983a88fe13e3fd778b560f2065a9afc753 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:17:32 +0000 Subject: [PATCH 006/128] [PATCH 5/9] iscsid: Add start/stop transmit abstraction In order to be able to abstract from socket and iser connection fd's we need to have generic code that can handle both. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5233 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/iscsi_scstd.c | 15 ++++++++++----- iscsi-scst/usr/iscsid.h | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index ede7626b5..5425c4501 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -170,6 +170,12 @@ static void create_listen_socket(struct pollfd *array) exit(1); } +static int transmit_sock(int fd, bool start) +{ + int opt = start; + return setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); +} + static void accept_connection(int listen) { union { @@ -261,6 +267,7 @@ static void accept_connection(int listen) } incoming[i] = conn; + conn->transmit = transmit_sock; conn_read_pdu(conn); set_non_blocking(fd); @@ -297,7 +304,7 @@ void isns_set_fd(int isns, int scn_listen, int scn) static void event_conn(struct connection *conn, struct pollfd *pollfd) { - int res, opt; + int res; again: switch (conn->iostate) { @@ -368,8 +375,7 @@ again: case IOSTATE_WRITE_AHS: case IOSTATE_WRITE_DATA: write_again: - opt = 1; - setsockopt(pollfd->fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); + conn->transmit(pollfd->fd, true); res = write(pollfd->fd, conn->buffer, conn->rwsize); if (res < 0) { if (errno != EINTR && errno != EAGAIN) { @@ -409,8 +415,7 @@ again: goto write_again; } case IOSTATE_WRITE_DATA: - opt = 0; - setsockopt(pollfd->fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); + conn->transmit(pollfd->fd, false); cmnd_finish(conn); switch (conn->state) { diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 489ec9397..2a3b4490e 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -127,6 +127,8 @@ struct connection { } auth; struct __qelem clist; + + int (*transmit)(int fd, bool start); }; #define IOSTATE_FREE 0 From bded9b5943e2f78b43fe38900c9cd50c85430d70 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:18:35 +0000 Subject: [PATCH 007/128] [PATCH 6/9] iscsid: Refactor code for iser reuse Refactor character device handling code as well as connection allocation code in order to be able to reuse that in iser later on. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5234 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/ctldev.c | 51 +++------------------------- iscsi-scst/usr/iscsi_scstd.c | 56 ++++++++++++++++++++----------- iscsi-scst/usr/misc.c | 65 ++++++++++++++++++++++++++++++++++++ iscsi-scst/usr/misc.h | 1 + 4 files changed, 106 insertions(+), 67 deletions(-) diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index 303cca650..8fed2e285 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -28,59 +28,17 @@ #include "iscsid.h" -#define CTL_DEVICE "/dev/iscsi-scst-ctl" +#define CTL_DEVICE "iscsi-scst-ctl" int kernel_open(void) { - FILE *f; - char devname[256]; - char buf[256]; - int devn; int ctlfd = -1; int err; struct iscsi_kern_register_info reg; - if (!(f = fopen("/proc/devices", "r"))) { - err = -errno; - perror("Cannot open control path to the driver"); - goto out_err; - } - - devn = 0; - while (!feof(f)) { - if (!fgets(buf, sizeof(buf), f)) { - break; - } - if (sscanf(buf, "%d %s", &devn, devname) != 2) { - continue; - } - if (!strcmp(devname, "iscsi-scst-ctl")) { - break; - } - devn = 0; - } - - fclose(f); - if (!devn) { - err = -ENOENT; - printf("cannot find iscsictl in /proc/devices - " - "make sure the module is loaded\n"); - goto out_err; - } - - unlink(CTL_DEVICE); - if (mknod(CTL_DEVICE, (S_IFCHR | 0600), (devn << 8))) { - err = -errno; - printf("cannot create %s %s\n", CTL_DEVICE, strerror(errno)); - goto out_err; - } - - ctlfd = open(CTL_DEVICE, O_RDWR); - if (ctlfd < 0) { - err = -errno; - printf("cannot open %s %s\n", CTL_DEVICE, strerror(errno)); - goto out_err; - } + ctlfd = create_and_open_dev(CTL_DEVICE, 0); + if (ctlfd < 0) + goto out; memset(®, 0, sizeof(reg)); reg.version = (uintptr_t)ISCSI_SCST_INTERFACE_VERSION; @@ -104,7 +62,6 @@ out: out_close: close(ctlfd); -out_err: ctlfd = err; goto out; } diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 5425c4501..00a03355c 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -170,6 +170,39 @@ static void create_listen_socket(struct pollfd *array) exit(1); } +static struct connection *alloc_and_init_conn(int fd) +{ + struct pollfd *pollfd; + struct connection *conn = NULL; + int i; + + for (i = 0; i < INCOMING_MAX; i++) { + if (!incoming[i]) + break; + } + if (i >= INCOMING_MAX) { + log_error("Unable to find incoming slot? %d\n", i); + goto out; + } + + conn = conn_alloc(); + if (!conn) { + log_error("Fail to allocate %s", "conn\n"); + goto out; + } + + conn->fd = fd; + incoming[i] = conn; + + pollfd = &poll_array[POLL_INCOMING + i]; + pollfd->fd = fd; + pollfd->events = POLLIN; + pollfd->revents = 0; + +out: + return conn; +} + static int transmit_sock(int fd, bool start) { int opt = start; @@ -184,9 +217,8 @@ static void accept_connection(int listen) struct sockaddr_in6 sin6; } from, to; socklen_t namesize; - struct pollfd *pollfd; struct connection *conn; - int fd, i, rc; + int fd, rc; char initiator_addr[ISCSI_PORTAL_LEN], initiator_port[NI_MAXSERV]; char target_portal[ISCSI_PORTAL_LEN], target_portal_port[NI_MAXSERV]; @@ -245,36 +277,20 @@ static void accept_connection(int listen) goto out_close; } - for (i = 0; i < INCOMING_MAX; i++) { - if (!incoming[i]) - break; - } - if (i >= INCOMING_MAX) { - log_error("Unable to find incoming slot? %d\n", i); + conn = alloc_and_init_conn(fd); + if (!conn) goto out_close; - } - if (!(conn = conn_alloc())) { - log_error("Fail to allocate %s", "conn\n"); - goto out_close; - } - - conn->fd = fd; conn->target_portal = strdup(target_portal); if (conn->target_portal == NULL) { log_error("Unable to duplicate target portal %s", target_portal); goto out_free; } - incoming[i] = conn; conn->transmit = transmit_sock; conn_read_pdu(conn); set_non_blocking(fd); - pollfd = &poll_array[POLL_INCOMING + i]; - pollfd->fd = fd; - pollfd->events = POLLIN; - pollfd->revents = 0; incoming_cnt++; diff --git a/iscsi-scst/usr/misc.c b/iscsi-scst/usr/misc.c index ae3210663..beb39ffdd 100644 --- a/iscsi-scst/usr/misc.c +++ b/iscsi-scst/usr/misc.c @@ -18,9 +18,74 @@ #include #include #include +#include +#include +#include +#include #include "iscsid.h" +int create_and_open_dev(const char *dev, int readonly) +{ + FILE *f; + char devname[256]; + char buf[256]; + int devn; + int ctlfd = -1; + int err; + int flags; + + f = fopen("/proc/devices", "r"); + if (!f) { + err = -errno; + perror("Cannot open control path to the driver"); + goto out; + } + + devn = 0; + while (!feof(f)) { + if (!fgets(buf, sizeof(buf), f)) + break; + if (sscanf(buf, "%d %s", &devn, devname) != 2) + continue; + if (!strcmp(devname, dev)) + break; + devn = 0; + } + + fclose(f); + if (!devn) { + err = -ENOENT; + printf("cannot find %s in /proc/devices - " + "make sure the module is loaded\n", dev); + goto out; + } + + sprintf(devname, "/dev/%s", dev); + + unlink(devname); + if (mknod(devname, (S_IFCHR | 0600), (devn << 8))) { + err = -errno; + printf("cannot create %s %s\n", devname, strerror(errno)); + goto out; + } + + if (readonly) + flags = O_RDONLY; + else + flags = O_RDWR; + + err = ctlfd = open(devname, flags); + if (ctlfd < 0) { + err = -errno; + printf("cannot open %s %s\n", devname, strerror(errno)); + goto out; + } + +out: + return err; +} + void set_non_blocking(int fd) { int res = fcntl(fd, F_GETFL); diff --git a/iscsi-scst/usr/misc.h b/iscsi-scst/usr/misc.h index 75d2a1583..127332d66 100644 --- a/iscsi-scst/usr/misc.h +++ b/iscsi-scst/usr/misc.h @@ -108,5 +108,6 @@ static inline int list_length_is_one(const struct __qelem *head) extern void set_non_blocking(int fd); extern void sock_set_keepalive(int sock, int timeout); +extern int create_and_open_dev(const char *dev, int readonly); #endif From c03bd27bca17907c74838946c2b1651c5b013ec6 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:19:27 +0000 Subject: [PATCH 008/128] [PATCH 7/9] iscsid: Implement iser support Add iser character device handling for accepting and handling connections received through RDMA transport. Add isert_listener device to the poll() loop and handle incoming connection requests. Differentiate between iser and non iser connections Validate RDMAExtension field and reject it if found in iscsi login request. Also, disable immediate data and first burst for iSER since it is not supported yet Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5235 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/include/iscsi_scst.h | 7 ++ iscsi-scst/kernel/iscsi.h | 3 + iscsi-scst/kernel/param.c | 20 ++++- iscsi-scst/usr/iscsi_scstd.c | 148 ++++++++++++++++++++++++++++++++ iscsi-scst/usr/iscsid.c | 29 +++++++ iscsi-scst/usr/iscsid.h | 7 ++ iscsi-scst/usr/param.c | 8 ++ iscsi-scst/usr/target.c | 3 +- 8 files changed, 222 insertions(+), 3 deletions(-) diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index f147db601..ce9db09d1 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -62,6 +62,13 @@ enum { key_ifmarker, key_ofmarkint, key_ifmarkint, + key_rdma_extensions, + key_target_recv_data_length, + key_initiator_recv_data_length, + key_max_ahs_length, + key_tagged_buffer_for_solicited_data_only, + key_iser_hello_required, + key_max_outstanding_unexpected_pdus, session_key_last, }; diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index f5593320c..ec05699da 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -59,6 +59,9 @@ struct iscsi_sess_params { int ifmarker; int ofmarkint; int ifmarkint; + int rdma_extensions; + int target_recv_data_length; + int initiator_recv_data_length; }; struct iscsi_tgt_params { diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c index aaa476010..4a5936365 100644 --- a/iscsi-scst/kernel/param.c +++ b/iscsi-scst/kernel/param.c @@ -99,12 +99,13 @@ static void log_params(struct iscsi_sess_params *params) iscsi_get_bool_value(params->data_sequence_inorder), params->error_recovery_level); PRINT_INFO(" HeaderDigest %s, DataDigest %s, OFMarker %s, " - "IFMarker %s, OFMarkInt %d, IFMarkInt %d", + "IFMarker %s, OFMarkInt %d, IFMarkInt %d, RDMAExtensions %s", iscsi_get_digest_name(params->header_digest, hdigest_name), iscsi_get_digest_name(params->data_digest, ddigest_name), iscsi_get_bool_value(params->ofmarker), iscsi_get_bool_value(params->ifmarker), - params->ofmarkint, params->ifmarkint); + params->ofmarkint, params->ifmarkint, + iscsi_get_bool_value(params->rdma_extensions)); } /* target_mutex supposed to be locked */ @@ -136,6 +137,11 @@ static void sess_params_check(struct iscsi_kern_params_info *info) CHECK_PARAM(info, iparams, ofmarker, 0, 0); CHECK_PARAM(info, iparams, ifmarker, 0, 0); + /* iSER related parameters */ + CHECK_PARAM(info, iparams, rdma_extensions, 0, 1); + CHECK_PARAM(info, iparams, target_recv_data_length, 512, max_len); + CHECK_PARAM(info, iparams, initiator_recv_data_length, 512, max_len); + return; } @@ -164,6 +170,11 @@ static void sess_params_set(struct iscsi_sess_params *params, SET_PARAM(params, info, iparams, ifmarker); SET_PARAM(params, info, iparams, ofmarkint); SET_PARAM(params, info, iparams, ifmarkint); + + /* iSER related parameters */ + SET_PARAM(params, info, iparams, rdma_extensions); + SET_PARAM(params, info, iparams, target_recv_data_length); + SET_PARAM(params, info, iparams, initiator_recv_data_length); return; } @@ -191,6 +202,11 @@ static void sess_params_get(struct iscsi_sess_params *params, GET_PARAM(params, info, iparams, ifmarker); GET_PARAM(params, info, iparams, ofmarkint); GET_PARAM(params, info, iparams, ifmarkint); + + /* iSER related parameters */ + GET_PARAM(params, info, iparams, rdma_extensions); + GET_PARAM(params, info, iparams, target_recv_data_length); + GET_PARAM(params, info, iparams, initiator_recv_data_length); return; } diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 00a03355c..94e13d3a8 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -199,16 +200,157 @@ static struct connection *alloc_and_init_conn(int fd) pollfd->events = POLLIN; pollfd->revents = 0; + conn_read_pdu(conn); + set_non_blocking(fd); + out: return conn; } +static int transmit_iser(int fd, bool start) +{ + int opt = start; + return ioctl(fd, RDMA_CORK, &opt, sizeof(opt)); +} + +static void create_iser_listen_socket(struct pollfd *array) +{ + struct addrinfo hints, *res, *res0; + char servname[64]; + int rc, i; + int iser_fd; + struct isert_addr_info info; + + iser_fd = create_and_open_dev("isert_scst", 1); + + poll_array[POLL_ISER_LISTEN].fd = iser_fd; + if (iser_fd != -1) { + poll_array[POLL_ISER_LISTEN].events = POLLIN; + + /* RDMAExtensions */ + session_keys[key_rdma_extensions].max = 1; + session_keys[key_rdma_extensions].local_def = 1; + } else { + poll_array[POLL_ISER_LISTEN].events = 0; + return; + } + + memset(servname, 0, sizeof(servname)); + snprintf(servname, sizeof(servname), "%d", server_port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + rc = getaddrinfo(server_address, servname, &hints, &res0); + if (rc != 0) { + log_error("Unable to get address info (%s)!", + get_error_str(rc)); + exit(1); + } + + i = 0; + for (res = res0; res && i < ISERT_MAX_PORTALS; res = res->ai_next) { + memcpy(&info.addr, res->ai_addr, res->ai_addrlen); + info.addr_len = res->ai_addrlen; + + rc = ioctl(iser_fd, SET_LISTEN_ADDR, &info); + if (rc != 0) { + log_error("Unable to set address info (%s)!", + strerror(rc)); + } + ++i; + } + + freeaddrinfo(res0); +} + +static int iser_getsockname(int fd, struct sockaddr *name, socklen_t *namelen) +{ + struct isert_addr_info addr; + int ret; + + ret = ioctl(fd, GET_PORTAL_ADDR, &addr, sizeof(addr)); + if (ret) + return ret; + + memcpy(name, &addr.addr, addr.addr_len); + *namelen = addr.addr_len; + + return ret; +} + +static int iser_is_discovery(int fd) +{ + int val = 1; + + return ioctl(fd, DISCOVERY_SESSION, &val, sizeof(val)); +} + +static void iser_accept(int fd) +{ + char buff[256]; + int ret, conn_fd; + struct connection *conn; + char target_portal[ISCSI_PORTAL_LEN], target_portal_port[NI_MAXSERV]; + struct isert_addr_info addr; + + ret = read(fd, buff, sizeof(buff)); + if (ret == -1) + return; + + conn_fd = open(buff, O_RDWR); + if (conn_fd == -1) { + log_error("open(iser_connection) %s failed: %s\n", + buff, strerror(errno)); + return; + } + + ret = ioctl(conn_fd, GET_PORTAL_ADDR, &addr, sizeof(addr)); + if (ret) + return; + + ret = getnameinfo((struct sockaddr *)&addr, sizeof(addr), target_portal, + sizeof(target_portal), target_portal_port, + sizeof(target_portal_port), + NI_NUMERICHOST | NI_NUMERICSERV); + if (ret != 0) { + log_error("Target portal getnameinfo() failed: %s!", + get_error_str(ret)); + return; + } + + conn = alloc_and_init_conn(conn_fd); + if (!conn) + return; + + conn->target_portal = strdup(target_portal); + if (conn->target_portal == NULL) { + log_error("Unable to duplicate target portal %s", target_portal); + conn_free(conn); + return; + } + + conn->transmit = transmit_iser; + conn->getsockname = iser_getsockname; + conn->is_discovery = iser_is_discovery; + conn->is_iser = true; + incoming_cnt++; + + log_info("iSER connect\n"); +} + static int transmit_sock(int fd, bool start) { int opt = start; return setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); } +static int tcp_is_discovery(int fd) +{ + return 0; +} + static void accept_connection(int listen) { union { @@ -288,6 +430,8 @@ static void accept_connection(int listen) } conn->transmit = transmit_sock; + conn->getsockname = getsockname; + conn->is_discovery = tcp_is_discovery; conn_read_pdu(conn); set_non_blocking(fd); @@ -469,6 +613,7 @@ static void event_loop(void) int res, i; create_listen_socket(poll_array + POLL_LISTEN); + create_iser_listen_socket(poll_array); poll_array[POLL_IPC].fd = ipc_fd; poll_array[POLL_IPC].events = POLLIN; @@ -542,6 +687,9 @@ static void event_loop(void) if (poll_array[POLL_SCN].revents) isns_scn_handle(0); + if (poll_array[POLL_ISER_LISTEN].revents) + iser_accept(poll_array[POLL_ISER_LISTEN].fd); + for (i = 0; i < INCOMING_MAX; i++) { struct connection *conn = incoming[i]; struct pollfd *pollfd = &poll_array[POLL_INCOMING + i]; diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index 615cec4c1..703548006 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -42,6 +42,10 @@ static struct iscsi_key login_keys[] = { {"InitiatorAlias",}, {"SessionType",}, {"TargetName",}, + {"InitiatorRecvDataSegmentLength",}, + {"MaxAHSLength",}, + {"TaggedBufferForSolicitedDataOnly",}, + {"iSERHelloRequired",}, {NULL,}, }; @@ -370,6 +374,26 @@ static void text_scan_login(struct connection *conn) } } + if (conn->is_iser) { + switch (idx) { + case key_rdma_extensions: + if (val != 1) { + login_rsp_ini_err(conn, ISCSI_STATUS_INIT_ERR); + goto out; + } + break; + case key_initial_r2t: + val = 1; + break; + case key_immediate_data: + val = 0; + break; + } + } else if (idx == key_rdma_extensions && val != 0) { + login_rsp_ini_err(conn, ISCSI_STATUS_INIT_ERR); + goto out; + } + params_check_val(session_keys, idx, &val); params_set_val(session_keys, conn->session_params, idx, &val); @@ -503,6 +527,11 @@ static void login_start(struct connection *conn) if (session_type) { if (!strcmp(session_type, "Discovery")) { + int ret = conn->is_discovery(conn->fd); + if (ret) { + login_rsp_tgt_err(conn, ISCSI_STATUS_MISSING_FIELDS); + return; + } conn->session_type = SESSION_DISCOVERY; } else if (strcmp(session_type, "Normal")) { login_rsp_ini_err(conn, ISCSI_STATUS_INV_SESSION_TYPE); diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 2a3b4490e..eb0cbce9d 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -28,8 +28,10 @@ #include "types.h" #ifdef INSIDE_KERNEL_TREE #include +#include #else #include "iscsi_scst.h" +#include "isert_scst.h" #endif #include "iscsi_hdr.h" #include "param.h" @@ -128,7 +130,11 @@ struct connection { struct __qelem clist; + bool is_iser; + int (*transmit)(int fd, bool start); + int (*getsockname)(int fd, struct sockaddr *name, socklen_t *namelen); + int (*is_discovery)(int fd); }; #define IOSTATE_FREE 0 @@ -224,6 +230,7 @@ extern int conn_blocked; enum { POLL_LISTEN, POLL_IPC = POLL_LISTEN + LISTEN_MAX, + POLL_ISER_LISTEN, POLL_NL, POLL_ISNS, POLL_SCN_LISTEN, diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index 9c524cde7..420e1308f 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -383,5 +383,13 @@ struct iscsi_key session_keys[] = { {"IFMarker", 0, 0, 0, 0, 0, &and_ops}, {"OFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops}, {"IFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops}, + {"RDMAExtensions", 0, 0, 0, 0, 1, &and_ops}, + {"TargetRecvDataSegmentLength", 8192, -1, 512, -1, 0, &minimum_ops}, + {"InitiatorRecvDataSegmentLength", 8192, -1, 512, -1, 0, &minimum_ops}, + {"MaxAHSLength", 256, 0, 0, -1, 0, &minimum_ops}, + {"TaggedBufferForSolicitedDataOnly", 0, 0, 0, 0, 0, &and_ops}, + {"iSERHelloRequired", 0, 0, 0, 0, 0, &and_ops}, + {"MaxOutstandingUnexpectedPDUs", 0, 0, 0, -1, 0, &minimum_ops}, {NULL,}, }; + diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index 2d475edb1..ecc00ec6d 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -239,10 +239,11 @@ void target_list_build(struct connection *conn, char *target_name) char portal[NI_MAXHOST]; int family, i; - if (getsockname(conn->fd, (struct sockaddr *) &ss1, &slen)) { + if (conn->getsockname(conn->fd, (struct sockaddr *) &ss1, &slen)) { log_error("getsockname failed: %m"); return; } + family = ss1.ss_family; list_for_each_entry(target, &targets_list, tlist) { From 623cbff5a34dfbae0f35eb5ef8ba5660d0c64cce Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:20:03 +0000 Subject: [PATCH 009/128] [PATCH 8/9] scstadmin: Load isert-scst if iscsi is present Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5236 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scstadmin/init.d/scst | 1 + 1 file changed, 1 insertion(+) diff --git a/scstadmin/init.d/scst b/scstadmin/init.d/scst index d4b1e4f33..eb32774b5 100755 --- a/scstadmin/init.d/scst +++ b/scstadmin/init.d/scst @@ -159,6 +159,7 @@ parse_scst_conf() { x86_64|i686) SCST_OPT_MODULES="crc32c-intel $SCST_OPT_MODULES";; esac + SCST_MODULES="$SCST_MODULES isert_scst" SCST_OPT_MODULES="crc32c $SCST_OPT_MODULES" SCST_DAEMONS="${ISCSI_DAEMON} $SCST_DAEMONS" fi From d3d69d4c89c1c5bb51ba86fc3826444e786241ee Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:20:52 +0000 Subject: [PATCH 010/128] [PATCH 9/9] scst: Add iSER module to RPM build Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5237 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst.spec.in | 1 + 1 file changed, 1 insertion(+) diff --git a/scst.spec.in b/scst.spec.in index 8c73adac1..3608a72a9 100644 --- a/scst.spec.in +++ b/scst.spec.in @@ -100,6 +100,7 @@ rm -rf /usr/local/include/scst /lib/modules/%{kver}/extra/fcst.ko /lib/modules/%{kver}/extra/ib_srpt.ko /lib/modules/%{kver}/extra/iscsi-scst.ko +/lib/modules/%{kver}/extra/isert-scst.ko /lib/modules/%{kver}/extra/qla2x00tgt.ko /lib/modules/%{kver}/extra/qla2xxx_scst.ko /lib/modules/%{kver}/extra/scst.ko From d567113d1293a48064b72e45045204ef9079bb35 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:52:39 +0000 Subject: [PATCH 011/128] iSER target page added git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5238 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- www/handler_fileio_tgt.html | 1 + www/scst_admin.html | 1 + www/target_emulex.html | 1 + www/target_fcoe.html | 1 + www/target_ibmvscsi.html | 1 + www/target_iscsi.html | 1 + www/target_iser.html | 90 +++++++++++++++++++++++++++++++++++++ www/target_local.html | 1 + www/target_lsi.html | 1 + www/target_mvsas.html | 1 + www/target_old.html | 1 + www/target_qla2x00t.html | 1 + www/target_srp.html | 1 + www/targets.html | 1 + 14 files changed, 103 insertions(+) create mode 100644 www/target_iser.html diff --git a/www/handler_fileio_tgt.html b/www/handler_fileio_tgt.html index 12a552850..fad7480d6 100644 --- a/www/handler_fileio_tgt.html +++ b/www/handler_fileio_tgt.html @@ -37,6 +37,7 @@

Target Drivers

  • ISCSI-SCST
  • +
  • iSER
  • QLogic FC qla2x00t
  • SCSI RDMA Protocol (SRP)
  • Marvell SAS adapters
  • diff --git a/www/scst_admin.html b/www/scst_admin.html index 97f93eb15..f0e754ead 100644 --- a/www/scst_admin.html +++ b/www/scst_admin.html @@ -37,6 +37,7 @@

    Target Drivers

    • ISCSI-SCST
    • +
    • iSER
    • QLogic FC qla2x00t
    • SCSI RDMA Protocol (SRP)
    • Marvell SAS adapters
    • diff --git a/www/target_emulex.html b/www/target_emulex.html index 556569f48..171d74de2 100644 --- a/www/target_emulex.html +++ b/www/target_emulex.html @@ -37,6 +37,7 @@

      Target Drivers

      • ISCSI-SCST
      • +
      • iSER
      • QLogic FC qla2x00t
      • SCSI RDMA Protocol (SRP)
      • Marvell SAS adapters
      • diff --git a/www/target_fcoe.html b/www/target_fcoe.html index 534ec58ae..eee6f17ae 100644 --- a/www/target_fcoe.html +++ b/www/target_fcoe.html @@ -37,6 +37,7 @@

        Target Drivers

        • ISCSI-SCST
        • +
        • iSER
        • QLogic FC qla2x00t
        • SCSI RDMA Protocol (SRP)
        • Marvell SAS adapters
        • diff --git a/www/target_ibmvscsi.html b/www/target_ibmvscsi.html index 8635127e9..4b7cf9543 100644 --- a/www/target_ibmvscsi.html +++ b/www/target_ibmvscsi.html @@ -37,6 +37,7 @@

          Target Drivers

          • ISCSI-SCST
          • +
          • iSER
          • QLogic FC qla2x00t
          • SCSI RDMA Protocol (SRP)
          • Marvell SAS adapters
          • diff --git a/www/target_iscsi.html b/www/target_iscsi.html index 0fc1a0faf..4da5ac00a 100644 --- a/www/target_iscsi.html +++ b/www/target_iscsi.html @@ -37,6 +37,7 @@

            Target Drivers

            • ISCSI-SCST
            • +
            • iSER
            • QLogic FC qla2x00t
            • SCSI RDMA Protocol (SRP)
            • Marvell SAS adapters
            • diff --git a/www/target_iser.html b/www/target_iser.html new file mode 100644 index 000000000..47335aee3 --- /dev/null +++ b/www/target_iser.html @@ -0,0 +1,90 @@ + + + + + + + + +iSCSI Target Driver + + + + +
              + + + + + +
              + + +
              +

              iSCSI Extensions for RDMA (iSER) driver for iSCSI-SCST

              +

              ISER extension for ISCSI-SCST has been developed by Yan Burman and Mellanox Technologies (thank you!).

              + +

              It is currently in a beta stage. You can download it from the "iser" SCST SVN branch.

              +


              + +
               
              +
              +
              +
              + + + + + + + + + diff --git a/www/target_local.html b/www/target_local.html index 31a47eb47..eccc1e8d3 100644 --- a/www/target_local.html +++ b/www/target_local.html @@ -37,6 +37,7 @@

              Target Drivers

              • ISCSI-SCST
              • +
              • iSER
              • QLogic FC qla2x00t
              • SCSI RDMA Protocol (SRP)
              • Marvell SAS adapters
              • diff --git a/www/target_lsi.html b/www/target_lsi.html index a50951768..e1764e712 100644 --- a/www/target_lsi.html +++ b/www/target_lsi.html @@ -37,6 +37,7 @@

                Target Drivers

                • ISCSI-SCST
                • +
                • iSER
                • QLogic FC qla2x00t
                • SCSI RDMA Protocol (SRP)
                • Marvell SAS adapters
                • diff --git a/www/target_mvsas.html b/www/target_mvsas.html index e2cf6c5b6..06a33e553 100644 --- a/www/target_mvsas.html +++ b/www/target_mvsas.html @@ -37,6 +37,7 @@

                  Target Drivers

                  • ISCSI-SCST
                  • +
                  • iSER
                  • QLogic FC qla2x00t
                  • SCSI RDMA Protocol (SRP)
                  • Marvell SAS adapters
                  • diff --git a/www/target_old.html b/www/target_old.html index efc8784a7..6a616ea76 100644 --- a/www/target_old.html +++ b/www/target_old.html @@ -37,6 +37,7 @@

                    Target Drivers

                    • ISCSI-SCST
                    • +
                    • iSER
                    • QLogic FC qla2x00t
                    • SCSI RDMA Protocol (SRP)
                    • Marvell SAS adapters
                    • diff --git a/www/target_qla2x00t.html b/www/target_qla2x00t.html index 82ac8b0d9..07db96754 100644 --- a/www/target_qla2x00t.html +++ b/www/target_qla2x00t.html @@ -37,6 +37,7 @@

                      Target Drivers

                      • ISCSI-SCST
                      • +
                      • iSER
                      • QLogic FC qla2x00t
                      • SCSI RDMA Protocol (SRP)
                      • Marvell SAS adapters
                      • diff --git a/www/target_srp.html b/www/target_srp.html index 1e0bcfc4d..283dc3b25 100644 --- a/www/target_srp.html +++ b/www/target_srp.html @@ -37,6 +37,7 @@

                        Target Drivers

                        • ISCSI-SCST
                        • +
                        • iSER
                        • QLogic FC qla2x00t
                        • SCSI RDMA Protocol (SRP)
                        • Marvell SAS adapters
                        • diff --git a/www/targets.html b/www/targets.html index b71293347..c05f88203 100644 --- a/www/targets.html +++ b/www/targets.html @@ -37,6 +37,7 @@

                          Target Drivers

                          • ISCSI-SCST
                          • +
                          • iSER
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • From 42f648fcbcd565b5e2ebfd4e0508e8c98b5aee6b Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 04:56:36 +0000 Subject: [PATCH 012/128] Web copyrights updated git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5239 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- www/comparison.html | 2 +- www/contributing.html | 2 +- www/downloads.html | 2 +- www/handler_fileio_tgt.html | 2 +- www/index.html | 2 +- www/mc_s.html | 2 +- www/scst_admin.html | 2 +- www/scstvslio.html | 2 +- www/scstvsstgt.html | 2 +- www/solutions.html | 2 +- www/target_emulex.html | 2 +- www/target_fcoe.html | 2 +- www/target_ibmvscsi.html | 2 +- www/target_iscsi.html | 2 +- www/target_iser.html | 2 +- www/target_local.html | 2 +- www/target_lsi.html | 2 +- www/target_mvsas.html | 2 +- www/target_old.html | 2 +- www/target_qla2x00t.html | 2 +- www/target_srp.html | 2 +- www/targets.html | 2 +- www/users.html | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/www/comparison.html b/www/comparison.html index 98c198b2a..561be60d9 100644 --- a/www/comparison.html +++ b/www/comparison.html @@ -547,7 +547,7 @@ target reconfiguration in a PnP-like manner) + - diff --git a/www/contributing.html b/www/contributing.html index 0a1a92597..3c356390e 100644 --- a/www/contributing.html +++ b/www/contributing.html @@ -204,7 +204,7 @@ diff --git a/www/downloads.html b/www/downloads.html index 8e1d91143..93e59c599 100644 --- a/www/downloads.html +++ b/www/downloads.html @@ -92,7 +92,7 @@ diff --git a/www/handler_fileio_tgt.html b/www/handler_fileio_tgt.html index fad7480d6..c6c9a56b0 100644 --- a/www/handler_fileio_tgt.html +++ b/www/handler_fileio_tgt.html @@ -80,7 +80,7 @@ diff --git a/www/index.html b/www/index.html index f2d670524..6bd2cffee 100644 --- a/www/index.html +++ b/www/index.html @@ -184,7 +184,7 @@ diff --git a/www/mc_s.html b/www/mc_s.html index f854692ec..c7675a7bc 100644 --- a/www/mc_s.html +++ b/www/mc_s.html @@ -247,7 +247,7 @@ and here. diff --git a/www/scst_admin.html b/www/scst_admin.html index f0e754ead..51735b0c0 100644 --- a/www/scst_admin.html +++ b/www/scst_admin.html @@ -79,7 +79,7 @@ diff --git a/www/scstvslio.html b/www/scstvslio.html index bc0a67c81..06185548b 100644 --- a/www/scstvslio.html +++ b/www/scstvslio.html @@ -100,7 +100,7 @@ diff --git a/www/scstvsstgt.html b/www/scstvsstgt.html index a8e220993..dba073301 100644 --- a/www/scstvsstgt.html +++ b/www/scstvsstgt.html @@ -80,7 +80,7 @@ diff --git a/www/solutions.html b/www/solutions.html index 5851ae147..22a0456dc 100644 --- a/www/solutions.html +++ b/www/solutions.html @@ -57,7 +57,7 @@ diff --git a/www/target_emulex.html b/www/target_emulex.html index 171d74de2..020666203 100644 --- a/www/target_emulex.html +++ b/www/target_emulex.html @@ -90,7 +90,7 @@ diff --git a/www/target_fcoe.html b/www/target_fcoe.html index eee6f17ae..732ebfd3f 100644 --- a/www/target_fcoe.html +++ b/www/target_fcoe.html @@ -77,7 +77,7 @@ diff --git a/www/target_ibmvscsi.html b/www/target_ibmvscsi.html index 4b7cf9543..2d4ffa74d 100644 --- a/www/target_ibmvscsi.html +++ b/www/target_ibmvscsi.html @@ -78,7 +78,7 @@ diff --git a/www/target_iscsi.html b/www/target_iscsi.html index 4da5ac00a..a683a1889 100644 --- a/www/target_iscsi.html +++ b/www/target_iscsi.html @@ -138,7 +138,7 @@ diff --git a/www/target_iser.html b/www/target_iser.html index 47335aee3..72d237ba2 100644 --- a/www/target_iser.html +++ b/www/target_iser.html @@ -70,7 +70,7 @@ diff --git a/www/target_local.html b/www/target_local.html index eccc1e8d3..acd8fc022 100644 --- a/www/target_local.html +++ b/www/target_local.html @@ -86,7 +86,7 @@ diff --git a/www/target_lsi.html b/www/target_lsi.html index e1764e712..96ec00043 100644 --- a/www/target_lsi.html +++ b/www/target_lsi.html @@ -75,7 +75,7 @@ diff --git a/www/target_mvsas.html b/www/target_mvsas.html index 06a33e553..2e7fd2722 100644 --- a/www/target_mvsas.html +++ b/www/target_mvsas.html @@ -75,7 +75,7 @@ diff --git a/www/target_old.html b/www/target_old.html index 6a616ea76..78da83f27 100644 --- a/www/target_old.html +++ b/www/target_old.html @@ -111,7 +111,7 @@ diff --git a/www/target_qla2x00t.html b/www/target_qla2x00t.html index 07db96754..9f992ccdf 100644 --- a/www/target_qla2x00t.html +++ b/www/target_qla2x00t.html @@ -77,7 +77,7 @@ diff --git a/www/target_srp.html b/www/target_srp.html index 283dc3b25..2a7326cdc 100644 --- a/www/target_srp.html +++ b/www/target_srp.html @@ -78,7 +78,7 @@ diff --git a/www/targets.html b/www/targets.html index c05f88203..f0eefddcb 100644 --- a/www/targets.html +++ b/www/targets.html @@ -78,7 +78,7 @@ diff --git a/www/users.html b/www/users.html index 4fff111f2..7283da367 100644 --- a/www/users.html +++ b/www/users.html @@ -167,7 +167,7 @@ From 9effb7ffc5a5e37811df9abb57d5b5d9eba7accd Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 05:12:15 +0000 Subject: [PATCH 013/128] Copyrights updated git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5240 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 5 ++--- iscsi-scst/README | 2 +- iscsi-scst/include/iscsi_scst.h | 5 ++--- iscsi-scst/include/iscsi_scst_ver.h | 5 ++--- iscsi-scst/kernel/Makefile | 5 ++--- iscsi-scst/kernel/config.c | 5 ++--- iscsi-scst/kernel/conn.c | 5 ++--- iscsi-scst/kernel/digest.c | 5 ++--- iscsi-scst/kernel/digest.h | 5 ++--- iscsi-scst/kernel/event.c | 5 ++--- iscsi-scst/kernel/iscsi.c | 5 ++--- iscsi-scst/kernel/iscsi.h | 5 ++--- iscsi-scst/kernel/iscsi_dbg.h | 5 ++--- iscsi-scst/kernel/iscsi_hdr.h | 5 ++--- iscsi-scst/kernel/isert-scst/Makefile | 5 ++--- iscsi-scst/kernel/isert-scst/iser_buf.c | 4 ++-- iscsi-scst/kernel/isert-scst/iser_datamover.c | 4 ++-- iscsi-scst/kernel/isert-scst/iser_global.c | 4 ++-- iscsi-scst/kernel/isert-scst/iser_pdu.c | 4 ++-- iscsi-scst/kernel/isert-scst/iser_rdma.c | 4 ++-- iscsi-scst/kernel/isert-scst/isert.c | 4 ++-- iscsi-scst/kernel/isert-scst/isert.h | 4 ++-- iscsi-scst/kernel/isert-scst/isert_dbg.h | 5 ++--- iscsi-scst/kernel/isert-scst/isert_login.c | 4 ++-- iscsi-scst/kernel/nthread.c | 5 ++--- iscsi-scst/kernel/param.c | 5 ++--- iscsi-scst/kernel/session.c | 5 ++--- iscsi-scst/kernel/target.c | 5 ++--- iscsi-scst/usr/Makefile | 5 ++--- iscsi-scst/usr/chap.c | 5 ++--- iscsi-scst/usr/config.c | 5 ++--- iscsi-scst/usr/conn.c | 5 ++--- iscsi-scst/usr/ctldev.c | 5 ++--- iscsi-scst/usr/event.c | 5 ++--- iscsi-scst/usr/iscsi_adm.c | 5 ++--- iscsi-scst/usr/iscsi_adm.h | 5 ++--- iscsi-scst/usr/iscsi_hdr.h | 5 ++--- iscsi-scst/usr/iscsi_scstd.c | 5 ++--- iscsi-scst/usr/iscsid.c | 5 ++--- iscsi-scst/usr/iscsid.h | 5 ++--- iscsi-scst/usr/isns.c | 5 ++--- iscsi-scst/usr/isns_proto.h | 5 ++--- iscsi-scst/usr/log.c | 5 ++--- iscsi-scst/usr/message.c | 5 ++--- iscsi-scst/usr/misc.c | 4 ++-- iscsi-scst/usr/misc.h | 5 ++--- iscsi-scst/usr/param.c | 5 ++--- iscsi-scst/usr/param.h | 5 ++--- iscsi-scst/usr/session.c | 5 ++--- iscsi-scst/usr/target.c | 5 ++--- iscsi-scst/usr/types.h | 5 ++--- qla2x00t/qla2x00-target/Makefile | 5 ++--- qla2x00t/qla2x00-target/README | 2 +- qla2x00t/qla2x00-target/qla2x00t.c | 5 ++--- qla2x00t/qla2x00-target/qla2x00t.h | 5 ++--- qla2x00t/qla2x_tgt.h | 5 ++--- qla2x00t/qla2x_tgt_def.h | 5 ++--- scst/Makefile | 5 ++--- scst/README | 2 +- scst/include/scst.h | 5 ++--- scst/include/scst_const.h | 5 ++--- scst/include/scst_debug.h | 5 ++--- scst/include/scst_sgv.h | 5 ++--- scst/include/scst_user.h | 5 ++--- scst/src/Makefile | 5 ++--- scst/src/dev_handlers/Makefile | 5 ++--- scst/src/dev_handlers/scst_cdrom.c | 5 ++--- scst/src/dev_handlers/scst_changer.c | 5 ++--- scst/src/dev_handlers/scst_disk.c | 5 ++--- scst/src/dev_handlers/scst_modisk.c | 5 ++--- scst/src/dev_handlers/scst_processor.c | 5 ++--- scst/src/dev_handlers/scst_raid.c | 5 ++--- scst/src/dev_handlers/scst_tape.c | 5 ++--- scst/src/dev_handlers/scst_user.c | 5 ++--- scst/src/dev_handlers/scst_vdisk.c | 7 +++---- scst/src/scst_debug.c | 5 ++--- scst/src/scst_lib.c | 5 ++--- scst/src/scst_main.c | 5 ++--- scst/src/scst_mem.c | 5 ++--- scst/src/scst_mem.h | 5 ++--- scst/src/scst_module.c | 5 ++--- scst/src/scst_pres.c | 2 +- scst/src/scst_pres.h | 2 +- scst/src/scst_priv.h | 5 ++--- scst/src/scst_proc.c | 5 ++--- scst/src/scst_sysfs.c | 5 ++--- scst/src/scst_targ.c | 5 ++--- scst/src/scst_tg.c | 2 +- scst_local/scst_local.c | 2 +- srpt/src/ib_srpt.c | 2 +- srpt/src/ib_srpt.h | 2 +- usr/fileio/Makefile | 5 ++--- usr/fileio/README | 2 +- usr/fileio/common.c | 5 ++--- usr/fileio/common.h | 5 ++--- usr/fileio/debug.c | 5 ++--- usr/fileio/debug.h | 5 ++--- usr/fileio/fileio.c | 5 ++--- 98 files changed, 187 insertions(+), 266 deletions(-) diff --git a/Makefile b/Makefile index d31a00c23..01742ad63 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,8 @@ # # Common makefile for SCSI target mid-level and its drivers # -# Copyright (C) 2004 - 2013 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/README b/iscsi-scst/README index 86e89ddd0..f265be6c3 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -1,7 +1,7 @@ iSCSI SCST target driver ======================== -Version 3.0.0, XX XXXXX 2013 +Version 3.0.0, XX XXXXX 2014 ---------------------------- ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET) diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index ce9db09d1..a79cf56d8 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/include/iscsi_scst_ver.h b/iscsi-scst/include/iscsi_scst_ver.h index bbd519f38..1f5388862 100644 --- a/iscsi-scst/include/iscsi_scst_ver.h +++ b/iscsi-scst/include/iscsi_scst_ver.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/Makefile b/iscsi-scst/kernel/Makefile index d90babd0d..cfe3e4928 100644 --- a/iscsi-scst/kernel/Makefile +++ b/iscsi-scst/kernel/Makefile @@ -1,9 +1,8 @@ # # Makefile for the kernel part of iSCSI-SCST. # -# Copyright (C) 2007 - 2013 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index e7f15ebf5..49d427ad0 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 35d778ab6..49595070d 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/digest.c b/iscsi-scst/kernel/digest.c index 4fe61db17..f6ed94418 100644 --- a/iscsi-scst/kernel/digest.c +++ b/iscsi-scst/kernel/digest.c @@ -3,9 +3,8 @@ * * Copyright (C) 2004 - 2006 Xiranet Communications GmbH * - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/digest.h b/iscsi-scst/kernel/digest.h index fccfb4393..3a72b25f6 100644 --- a/iscsi-scst/kernel/digest.h +++ b/iscsi-scst/kernel/digest.h @@ -2,9 +2,8 @@ * iSCSI digest handling. * * Copyright (C) 2004 Xiranet Communications GmbH - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/event.c b/iscsi-scst/kernel/event.c index 6858509a9..ffd9e13b5 100644 --- a/iscsi-scst/kernel/event.c +++ b/iscsi-scst/kernel/event.c @@ -2,9 +2,8 @@ * Event notification code. * * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 1add43d72..3554f77db 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index ec05699da..ae43e34bd 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/iscsi_dbg.h b/iscsi-scst/kernel/iscsi_dbg.h index 518600696..170a23217 100644 --- a/iscsi-scst/kernel/iscsi_dbg.h +++ b/iscsi-scst/kernel/iscsi_dbg.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/iscsi_hdr.h b/iscsi-scst/kernel/iscsi_hdr.h index 9abd4aa14..6204699c7 100644 --- a/iscsi-scst/kernel/iscsi_hdr.h +++ b/iscsi-scst/kernel/iscsi_hdr.h @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/isert-scst/Makefile b/iscsi-scst/kernel/isert-scst/Makefile index b1245a105..5c3cebda8 100644 --- a/iscsi-scst/kernel/isert-scst/Makefile +++ b/iscsi-scst/kernel/isert-scst/Makefile @@ -1,9 +1,8 @@ # # Makefile for the kernel part of iSER-SCST. # -# Copyright (C) 2007 - 2013 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/isert-scst/iser_buf.c b/iscsi-scst/kernel/isert-scst/iser_buf.c index de07f862a..8d74f1bff 100644 --- a/iscsi-scst/kernel/isert-scst/iser_buf.c +++ b/iscsi-scst/kernel/isert-scst/iser_buf.c @@ -3,8 +3,8 @@ * * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.c b/iscsi-scst/kernel/isert-scst/iser_datamover.c index da94dcc2d..c09897466 100644 --- a/iscsi-scst/kernel/isert-scst/iser_datamover.c +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.c @@ -3,8 +3,8 @@ * * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/isert-scst/iser_global.c b/iscsi-scst/kernel/isert-scst/iser_global.c index 72039e30f..71f85cd6e 100644 --- a/iscsi-scst/kernel/isert-scst/iser_global.c +++ b/iscsi-scst/kernel/isert-scst/iser_global.c @@ -3,8 +3,8 @@ * * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/isert-scst/iser_pdu.c b/iscsi-scst/kernel/isert-scst/iser_pdu.c index 109b3901f..e07f53dc8 100644 --- a/iscsi-scst/kernel/isert-scst/iser_pdu.c +++ b/iscsi-scst/kernel/isert-scst/iser_pdu.c @@ -3,8 +3,8 @@ * * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index b55182e91..5845738a1 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -2,8 +2,8 @@ * isert_rdma.c * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c index 0f0ea73db..d0688fce2 100644 --- a/iscsi-scst/kernel/isert-scst/isert.c +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -1,8 +1,8 @@ /* * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/isert-scst/isert.h b/iscsi-scst/kernel/isert-scst/isert.h index 56a7d3b09..1e692fd01 100644 --- a/iscsi-scst/kernel/isert-scst/isert.h +++ b/iscsi-scst/kernel/isert-scst/isert.h @@ -1,8 +1,8 @@ /* * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/isert-scst/isert_dbg.h b/iscsi-scst/kernel/isert-scst/isert_dbg.h index 37472fe60..49e32de13 100644 --- a/iscsi-scst/kernel/isert-scst/isert_dbg.h +++ b/iscsi-scst/kernel/isert-scst/isert_dbg.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 5beee62a2..ea2c92ab0 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -1,8 +1,8 @@ /* * This file is part of iser target kernel module. * -* Copyright (c) 2013 Mellanox Technologies. All rights reserved. -* Copyright (c) 2013 Yan Burman (yanb@mellanox.com) +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 665ee0da6..09e3a8cc2 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -2,9 +2,8 @@ * Network threads. * * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c index 4a5936365..7b307530f 100644 --- a/iscsi-scst/kernel/param.c +++ b/iscsi-scst/kernel/param.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index 03ddb858e..ddd64adf1 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index 17b83388f..ef364e541 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/Makefile b/iscsi-scst/usr/Makefile index c600435f2..ae70b9a81 100644 --- a/iscsi-scst/usr/Makefile +++ b/iscsi-scst/usr/Makefile @@ -1,9 +1,8 @@ # # Makefile for the user space part of iSCSI-SCST. # -# Copyright (C) 2007 - 2013 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/chap.c b/iscsi-scst/usr/chap.c index 30b785aff..287210a1f 100644 --- a/iscsi-scst/usr/chap.c +++ b/iscsi-scst/usr/chap.c @@ -3,9 +3,8 @@ * * Copyright (C) 2004 Xiranet Communications GmbH * Copyright (C) 2002 - 2003 Ardis Technolgies , - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * and code taken from UNH iSCSI software: * Copyright (C) 2001-2003 InterOperability Lab (IOL) diff --git a/iscsi-scst/usr/config.c b/iscsi-scst/usr/config.c index be1d4b71f..0ea14491a 100644 --- a/iscsi-scst/usr/config.c +++ b/iscsi-scst/usr/config.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/conn.c b/iscsi-scst/usr/conn.c index 1f37a3e3f..8d4391762 100644 --- a/iscsi-scst/usr/conn.c +++ b/iscsi-scst/usr/conn.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index 8fed2e285..1164fc7d4 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/event.c b/iscsi-scst/usr/event.c index e25c50bc6..c13865839 100644 --- a/iscsi-scst/usr/event.c +++ b/iscsi-scst/usr/event.c @@ -2,9 +2,8 @@ * Event notification code. * * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_adm.c b/iscsi-scst/usr/iscsi_adm.c index 7b719ff04..57242f044 100644 --- a/iscsi-scst/usr/iscsi_adm.c +++ b/iscsi-scst/usr/iscsi_adm.c @@ -2,9 +2,8 @@ * iscsi_adm - manage iSCSI-SCST Target software. * * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_adm.h b/iscsi-scst/usr/iscsi_adm.h index ded4e8f21..4408368e4 100644 --- a/iscsi-scst/usr/iscsi_adm.h +++ b/iscsi-scst/usr/iscsi_adm.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_hdr.h b/iscsi-scst/usr/iscsi_hdr.h index 40493e14e..95b8b2280 100644 --- a/iscsi-scst/usr/iscsi_hdr.h +++ b/iscsi-scst/usr/iscsi_hdr.h @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 94e13d3a8..e3d029496 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index 703548006..67accbd64 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index eb0cbce9d..152ae1d25 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/isns.c b/iscsi-scst/usr/isns.c index 3a9bff57f..8fa916099 100644 --- a/iscsi-scst/usr/isns.c +++ b/iscsi-scst/usr/isns.c @@ -2,9 +2,8 @@ * iSNS functions * * Copyright (C) 2006 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/iscsi-scst/usr/isns_proto.h b/iscsi-scst/usr/isns_proto.h index 5de003ccb..c9ab970d4 100644 --- a/iscsi-scst/usr/isns_proto.h +++ b/iscsi-scst/usr/isns_proto.h @@ -2,9 +2,8 @@ * iSNS protocol data types * * Copyright (C) 2006 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/iscsi-scst/usr/log.c b/iscsi-scst/usr/log.c index 6e2c7c01d..273cbc330 100644 --- a/iscsi-scst/usr/log.c +++ b/iscsi-scst/usr/log.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/message.c b/iscsi-scst/usr/message.c index 237edb3f8..89e0610de 100644 --- a/iscsi-scst/usr/message.c +++ b/iscsi-scst/usr/message.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2004 - 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/misc.c b/iscsi-scst/usr/misc.c index beb39ffdd..92c5d96c7 100644 --- a/iscsi-scst/usr/misc.c +++ b/iscsi-scst/usr/misc.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/misc.h b/iscsi-scst/usr/misc.h index 127332d66..37000efb2 100644 --- a/iscsi-scst/usr/misc.h +++ b/iscsi-scst/usr/misc.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index 420e1308f..c4689ed25 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/param.h b/iscsi-scst/usr/param.h index 4bf078dac..670b5af63 100644 --- a/iscsi-scst/usr/param.h +++ b/iscsi-scst/usr/param.h @@ -1,8 +1,7 @@ /* * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/session.c b/iscsi-scst/usr/session.c index cb7b4ec67..57f897683 100644 --- a/iscsi-scst/usr/session.c +++ b/iscsi-scst/usr/session.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index ecc00ec6d..952201f30 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/iscsi-scst/usr/types.h b/iscsi-scst/usr/types.h index d6a835b4a..147b108fb 100644 --- a/iscsi-scst/usr/types.h +++ b/iscsi-scst/usr/types.h @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 - 2003 Ardis Technolgies - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/qla2x00t/qla2x00-target/Makefile b/qla2x00t/qla2x00-target/Makefile index 18d9f4590..c2ec8a50d 100644 --- a/qla2x00t/qla2x00-target/Makefile +++ b/qla2x00t/qla2x00-target/Makefile @@ -1,10 +1,9 @@ # # Qlogic 2x00 SCSI target driver makefile # -# Copyright (C) 2004 - 2013 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2006 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index b63a4eb0a..386eb4801 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -1,7 +1,7 @@ Target driver for QLogic 22xx/23xx/24xx/25xx Fibre Channel cards ================================================================ -Version 3.0.0, XX XXXXX 2013 +Version 3.0.0, XX XXXXX 2014 ---------------------------- This driver consists from two parts: the target mode driver itself and diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 6cfce73bd..4de80917f 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -1,11 +1,10 @@ /* * qla2x00t.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2006 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * QLogic 22xx/23xx/24xx/25xx FC target driver. * diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index f6154a66b..e8bae79c9 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -1,11 +1,10 @@ /* * qla2x00t.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2006 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * QLogic 22xx/23xx/24xx/25xx FC target driver. * diff --git a/qla2x00t/qla2x_tgt.h b/qla2x00t/qla2x_tgt.h index dbc8fa4eb..2562f1178 100644 --- a/qla2x00t/qla2x_tgt.h +++ b/qla2x00t/qla2x_tgt.h @@ -1,11 +1,10 @@ /* * qla2x_tgt.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Additional file for the target driver support. * diff --git a/qla2x00t/qla2x_tgt_def.h b/qla2x00t/qla2x_tgt_def.h index 478143d37..8b0ea8454 100644 --- a/qla2x00t/qla2x_tgt_def.h +++ b/qla2x00t/qla2x_tgt_def.h @@ -1,11 +1,10 @@ /* * qla2x_tgt_def.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Additional file for the target driver support. * diff --git a/scst/Makefile b/scst/Makefile index 828dbbbe0..bcd0770e4 100644 --- a/scst/Makefile +++ b/scst/Makefile @@ -1,10 +1,9 @@ # # Common makefile for SCSI target mid-level and its drivers # -# Copyright (C) 2004 - 2013 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/scst/README b/scst/README index 399aaea29..f9419e25e 100644 --- a/scst/README +++ b/scst/README @@ -1,7 +1,7 @@ Generic SCSI target mid-level for Linux (SCST) ============================================== -Version 3.0.0, XX XXXXX 2013 +Version 3.0.0, XX XXXXX 2014 ---------------------------- SCST is designed to provide unified, consistent interface between SCSI diff --git a/scst/include/scst.h b/scst/include/scst.h index 8c7f89206..af3afebc0 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -1,10 +1,9 @@ /* * include/scst.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * Copyright (C) 2010 - 2011 Bart Van Assche . * * Main SCSI target mid-level include file. diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index b6426a6bf..d534f2739 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -1,9 +1,8 @@ /* * include/scst_const.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Contains common SCST constants. This file supposed to be included * from both kernel and user spaces. diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index ee22b0b15..22145e7bd 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -1,10 +1,9 @@ /* * include/scst_debug.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Contains macros for execution tracing and error reporting * diff --git a/scst/include/scst_sgv.h b/scst/include/scst_sgv.h index fad86686a..8b5084492 100644 --- a/scst/include/scst_sgv.h +++ b/scst/include/scst_sgv.h @@ -1,9 +1,8 @@ /* * include/scst_sgv.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Include file for SCST SGV cache. * diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index 3acbde5ef..0e1306de5 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -1,9 +1,8 @@ /* * include/scst_user.h * - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Contains constants and data structures for scst_user module. * See http://scst.sourceforge.net/doc/scst_user_spec.txt or diff --git a/scst/src/Makefile b/scst/src/Makefile index 6fb934eca..9b6a55862 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -1,10 +1,9 @@ # # SCSI target mid-level makefile # -# Copyright (C) 2004 - 2013 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index 8ba47de3e..a460ce1a6 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -1,10 +1,9 @@ # # SCSI target mid-level dev handler's makefile # -# Copyright (C) 2004 - 2013 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2014 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/scst/src/dev_handlers/scst_cdrom.c b/scst/src/dev_handlers/scst_cdrom.c index 6a89aa6aa..c1ffb454b 100644 --- a/scst/src/dev_handlers/scst_cdrom.c +++ b/scst/src/dev_handlers/scst_cdrom.c @@ -1,10 +1,9 @@ /* * scst_cdrom.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI CDROM (type 5) dev handler * diff --git a/scst/src/dev_handlers/scst_changer.c b/scst/src/dev_handlers/scst_changer.c index 5cbb46c69..23eb11ce7 100644 --- a/scst/src/dev_handlers/scst_changer.c +++ b/scst/src/dev_handlers/scst_changer.c @@ -1,10 +1,9 @@ /* * scst_changer.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI medium changer (type 8) dev handler * diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index 59e7bd896..4e432f6f1 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -1,10 +1,9 @@ /* * scst_disk.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI disk (type 0) dev handler * & diff --git a/scst/src/dev_handlers/scst_modisk.c b/scst/src/dev_handlers/scst_modisk.c index 97014cf1d..eb9e50d1d 100644 --- a/scst/src/dev_handlers/scst_modisk.c +++ b/scst/src/dev_handlers/scst_modisk.c @@ -1,10 +1,9 @@ /* * scst_modisk.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI MO disk (type 7) dev handler * & diff --git a/scst/src/dev_handlers/scst_processor.c b/scst/src/dev_handlers/scst_processor.c index 1c4a2c16e..cad04b576 100644 --- a/scst/src/dev_handlers/scst_processor.c +++ b/scst/src/dev_handlers/scst_processor.c @@ -1,10 +1,9 @@ /* * scst_processor.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI medium processor (type 3) dev handler * diff --git a/scst/src/dev_handlers/scst_raid.c b/scst/src/dev_handlers/scst_raid.c index 9cf0fbf25..7f95d2b5c 100644 --- a/scst/src/dev_handlers/scst_raid.c +++ b/scst/src/dev_handlers/scst_raid.c @@ -1,10 +1,9 @@ /* * scst_raid.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI raid(controller) (type 0xC) dev handler * diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index b012e58e6..1d4b34faf 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -1,10 +1,9 @@ /* * scst_tape.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI tape (type 1) dev handler * & diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index eb3c36f20..6c05b876c 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -1,9 +1,8 @@ /* * scst_user.c * - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * SCSI virtual user space device handler * diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 20c27b86a..d6bd45ebf 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -1,13 +1,12 @@ /* * scst_vdisk.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2007 Ming Zhang * Copyright (C) 2007 Ross Walker - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. - * Copyright (C) 2008 - 2013 Bart Van Assche + * Copyright (C) 2007 - 2014 Fusion-io, Inc. + * Copyright (C) 2008 - 2014 Bart Van Assche * * SCSI disk (type 0) and CDROM (type 5) dev handler using files * on file systems or block devices (VDISK) diff --git a/scst/src/scst_debug.c b/scst/src/scst_debug.c index 1e1e09067..31632c163 100644 --- a/scst/src/scst_debug.c +++ b/scst/src/scst_debug.c @@ -1,10 +1,9 @@ /* * scst_debug.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Contains helper functions for execution tracing and error reporting. * Intended to be included in main .c file. diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 99f32c3d3..c845d4f54 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -1,10 +1,9 @@ /* * scst_lib.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 7cf78f504..2be0ab797 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1,10 +1,9 @@ /* * scst_main.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_mem.c b/scst/src/scst_mem.c index 6509e4897..ea099a727 100644 --- a/scst/src/scst_mem.c +++ b/scst/src/scst_mem.c @@ -1,9 +1,8 @@ /* * scst_mem.c * - * Copyright (C) 2006 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2006 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_mem.h b/scst/src/scst_mem.h index db1400cab..68e33a092 100644 --- a/scst/src/scst_mem.h +++ b/scst/src/scst_mem.h @@ -1,9 +1,8 @@ /* * scst_mem.h * - * Copyright (C) 2006 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2006 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_module.c b/scst/src/scst_module.c index 754e8e24d..53a8a0263 100644 --- a/scst/src/scst_module.c +++ b/scst/src/scst_module.c @@ -1,10 +1,9 @@ /* * scst_module.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Support for loading target modules. The usage is similar to scsi_module.c * diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 5ebb498c1..c08378e01 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -3,7 +3,7 @@ * * Copyright (C) 2009 - 2010 Alexey Obitotskiy * Copyright (C) 2009 - 2010 Open-E, Inc. - * Copyright (C) 2009 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2009 - 2014 Vladislav Bolkhovitin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_pres.h b/scst/src/scst_pres.h index 3ada1afac..9829d4fac 100644 --- a/scst/src/scst_pres.h +++ b/scst/src/scst_pres.h @@ -3,7 +3,7 @@ * * Copyright (C) 2009 - 2010 Alexey Obitotskiy * Copyright (C) 2009 - 2010 Open-E, Inc. - * Copyright (C) 2009 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2009 - 2014 Vladislav Bolkhovitin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 4b03e6725..74d041000 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -1,10 +1,9 @@ /* * scst_priv.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index dd1b36ca4..91e4101c6 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -1,10 +1,9 @@ /* * scst_proc.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 36913ae8f..7f4a89d37 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -2,9 +2,8 @@ * scst_sysfs.c * * Copyright (C) 2009 Daniel Henrique Debonzi - * Copyright (C) 2009 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2009 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2009 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 046d0f039..35be2e746 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -1,10 +1,9 @@ /* * scst_targ.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst/src/scst_tg.c b/scst/src/scst_tg.c index 1464c82dc..e9a3c91b9 100644 --- a/scst/src/scst_tg.c +++ b/scst/src/scst_tg.c @@ -3,7 +3,7 @@ * * SCSI target group related code. * - * Copyright (C) 2011-2013 Bart Van Assche . + * Copyright (C) 2011 - 2014 Bart Van Assche . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 3419e111b..ed46a8527 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 - 2010 Richard Sharpe * Copyright (C) 1992 Eric Youngdale - * Copyright (C) 2008 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2008 - 2014 Vladislav Bolkhovitin * * Simulate a host adapter and an SCST target adapter back to back * diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index e1ec5eb4c..67c8c6424 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2006 - 2009 Mellanox Technology Inc. All rights reserved. - * Copyright (C) 2008 - 2013 Bart Van Assche . + * Copyright (C) 2008 - 2014 Bart Van Assche . * Copyright (C) 2008 Vladislav Bolkhovitin * * This software is available to you under a choice of one of two diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index baa70a727..5e635c2ff 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2006 - 2009 Mellanox Technology Inc. All rights reserved. - * Copyright (C) 2009 - 2013 Bart Van Assche . + * Copyright (C) 2009 - 2014 Bart Van Assche . * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/usr/fileio/Makefile b/usr/fileio/Makefile index 92aee80d0..5cdb3e3ed 100644 --- a/usr/fileio/Makefile +++ b/usr/fileio/Makefile @@ -1,9 +1,8 @@ # # SCSI target mid-level makefile # -# Copyright (C) 2007 - 2013 Vladislav Bolkhovitin -# Copyright (C) 2007 - 2010 ID7 Ltd. -# Copyright (C) 2010 - 2013 SCST Ltd. +# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin +# Copyright (C) 2007 - 2014 Fusion-io, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/usr/fileio/README b/usr/fileio/README index 87945ad3f..79e43ccc8 100644 --- a/usr/fileio/README +++ b/usr/fileio/README @@ -1,7 +1,7 @@ User space FILEIO handler ========================= -Version 3.0.0, XX XXXXX 2013 +Version 3.0.0, XX XXXXX 2014 ---------------------------- User space program fileio_tgt uses interface of SCST's scst_user dev diff --git a/usr/fileio/common.c b/usr/fileio/common.c index 8e3d1fe66..96a674144 100644 --- a/usr/fileio/common.c +++ b/usr/fileio/common.c @@ -1,9 +1,8 @@ /* * common.c * - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/usr/fileio/common.h b/usr/fileio/common.h index 37b399d1b..4f23fc46d 100644 --- a/usr/fileio/common.h +++ b/usr/fileio/common.h @@ -1,9 +1,8 @@ /* * common.h * - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/usr/fileio/debug.c b/usr/fileio/debug.c index 6bccfa380..ea1d9eb85 100644 --- a/usr/fileio/debug.c +++ b/usr/fileio/debug.c @@ -1,10 +1,9 @@ /* * debug.c * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Contains helper functions for execution tracing and error reporting. * Intended to be included in main .c file. diff --git a/usr/fileio/debug.h b/usr/fileio/debug.h index 712508565..fc698e8f6 100644 --- a/usr/fileio/debug.h +++ b/usr/fileio/debug.h @@ -1,10 +1,9 @@ /* * debug.h * - * Copyright (C) 2004 - 2013 Vladislav Bolkhovitin + * Copyright (C) 2004 - 2014 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * Contains macroses for execution tracing and error reporting * diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index dccf8989a..7a30acebf 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -1,9 +1,8 @@ /* * fileio.c * - * Copyright (C) 2007 - 2013 Vladislav Bolkhovitin - * Copyright (C) 2007 - 2010 ID7 Ltd. - * Copyright (C) 2010 - 2013 SCST Ltd. + * Copyright (C) 2007 - 2014 Vladislav Bolkhovitin + * Copyright (C) 2007 - 2014 Fusion-io, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License From 77fb4660a534b15d811ed48182354f5335383984 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 21:24:53 +0000 Subject: [PATCH 014/128] Initialized merge tracking via "svnmerge" with revisions "1-5241" from trunk git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5242 d57e44dd-8a1f-0410-8b47-8ef2f437770f From da19cd6eccec1be82c16c21b0c146003c60812db Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Jan 2014 22:07:45 +0000 Subject: [PATCH 015/128] Small cleanups git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5243 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/include/iscsit_transport.h | 6 ++++-- iscsi-scst/kernel/iscsit_transport.c | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/iscsi-scst/include/iscsit_transport.h b/iscsi-scst/include/iscsit_transport.h index e4e5ff870..32afb08bd 100644 --- a/iscsi-scst/include/iscsit_transport.h +++ b/iscsi-scst/include/iscsit_transport.h @@ -11,7 +11,7 @@ #include #endif -/* forward declarations */ +/* Forward declarations */ struct iscsi_session; struct iscsi_kern_conn_info; struct iscsi_conn; @@ -49,12 +49,14 @@ struct iscsit_transport { int (*iscsit_receive_cmnd_data)(struct iscsi_cmnd *cmnd); void (*iscsit_close_all_portals)(void); +#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) unsigned int need_alloc_write_buf:1; +#endif struct module *owner; const char name[SCST_MAX_NAME]; enum iscsit_transport_type transport_type; - struct list_head list; + struct list_head transport_list_entry; }; extern int iscsit_register_transport(struct iscsit_transport *t); diff --git a/iscsi-scst/kernel/iscsit_transport.c b/iscsi-scst/kernel/iscsit_transport.c index debf87edb..d913e3889 100644 --- a/iscsi-scst/kernel/iscsit_transport.c +++ b/iscsi-scst/kernel/iscsit_transport.c @@ -10,7 +10,7 @@ static struct iscsit_transport *__iscsit_get_transport(enum iscsit_transport_typ { struct iscsit_transport *t; - list_for_each_entry(t, &transport_list, list) { + list_for_each_entry(t, &transport_list, transport_list_entry) { if (t->transport_type == type) return t; } @@ -34,7 +34,7 @@ int iscsit_register_transport(struct iscsit_transport *t) struct iscsit_transport *tmp; int ret = 0; - INIT_LIST_HEAD(&t->list); + INIT_LIST_HEAD(&t->transport_list_entry); mutex_lock(&transport_mutex); tmp = __iscsit_get_transport(t->transport_type); @@ -43,7 +43,7 @@ int iscsit_register_transport(struct iscsit_transport *t) t->transport_type); ret = -EEXIST; } else { - list_add_tail(&t->list, &transport_list); + list_add_tail(&t->transport_list_entry, &transport_list); PRINT_INFO("Registered iSCSI transport: %s\n", t->name); } mutex_unlock(&transport_mutex); @@ -55,7 +55,7 @@ EXPORT_SYMBOL(iscsit_register_transport); void iscsit_unregister_transport(struct iscsit_transport *t) { mutex_lock(&transport_mutex); - list_del(&t->list); + list_del(&t->transport_list_entry); mutex_unlock(&transport_mutex); PRINT_INFO("Unregistered iSCSI transport: %s\n", t->name); From f7915d582b9e69a4d2f949da286190df92c8453c Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 29 Jan 2014 00:07:14 +0000 Subject: [PATCH 016/128] Some more cleanups git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5244 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/conn.c | 1 + iscsi-scst/kernel/iscsi.c | 11 +++-------- iscsi-scst/kernel/isert-scst/isert_dbg.h | 1 + 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 49595070d..ef043cffb 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -311,6 +311,7 @@ out_err: goto out; } EXPORT_SYMBOL(conn_sysfs_add); + #endif /* CONFIG_SCST_PROC */ /* target_mutex supposed to be locked */ diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 3554f77db..33e73f064 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -1915,9 +1915,6 @@ int cmnd_rx_continue(struct iscsi_cmnd *req) struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req); struct scst_cmd *scst_cmd = req->scst_cmd; scst_data_direction dir; -#ifdef CONFIG_SCST_DEBUG - bool unsolicited_data_expected = false; -#endif int res = 0; TRACE_ENTRY(); @@ -1977,11 +1974,9 @@ int cmnd_rx_continue(struct iscsi_cmnd *req) } trace: - TRACE_DBG("req=%p, dir=%d, unsolicited_data_expected=%d, " - "r2t_len_to_receive=%d, r2t_len_to_send=%d, bufflen=%d, " - "own_sg %d", req, dir, unsolicited_data_expected, - req->r2t_len_to_receive, req->r2t_len_to_send, req->bufflen, - req->own_sg); + TRACE_DBG("req=%p, dir=%d, r2t_len_to_receive=%d, r2t_len_to_send=%d, " + "bufflen=%d, own_sg %d", req, dir, req->r2t_len_to_receive, + req->r2t_len_to_send, req->bufflen, req->own_sg); out: TRACE_EXIT_RES(res); diff --git a/iscsi-scst/kernel/isert-scst/isert_dbg.h b/iscsi-scst/kernel/isert-scst/isert_dbg.h index 49e32de13..064c714bf 100644 --- a/iscsi-scst/kernel/isert-scst/isert_dbg.h +++ b/iscsi-scst/kernel/isert-scst/isert_dbg.h @@ -23,6 +23,7 @@ #endif #define LOG_PREFIX "isert" /* Prefix for SCST tracing macros. */ + #ifdef INSIDE_KERNEL_TREE #include #else From 91e6f7d26ab796c2bbe95b2d99b0edf53c4bba9d Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 29 Jan 2014 00:18:04 +0000 Subject: [PATCH 017/128] iser: Make struct iscsit_transport a little bit more CPU cache friendly by combining fast path callbacks in a single cache line. git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5245 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/include/iscsit_transport.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/iscsi-scst/include/iscsit_transport.h b/iscsi-scst/include/iscsit_transport.h index 32afb08bd..6cbcedbcc 100644 --- a/iscsi-scst/include/iscsit_transport.h +++ b/iscsi-scst/include/iscsit_transport.h @@ -24,12 +24,21 @@ enum iscsit_transport_type { struct iscsit_transport { struct iscsi_cmnd* (*iscsit_alloc_cmd)(struct iscsi_conn *conn, struct iscsi_cmnd *parent); - void (*iscsit_free_cmd)(struct iscsi_cmnd *cmnd); void (*iscsit_preprocessing_done)(struct iscsi_cmnd *cmnd); void (*iscsit_send_data_rsp)(struct iscsi_cmnd *req, u8 *sense, int sense_len, u8 status, int send_status); + int (*iscsit_send_locally)(struct iscsi_cmnd *cmnd, + unsigned int cmd_count); + void (*iscsit_set_sense_data)(struct iscsi_cmnd *rsp, + const u8 *sense_buf, int sense_len); + int (*iscsit_receive_cmnd_data)(struct iscsi_cmnd *cmnd); void (*iscsit_make_conn_wr_active)(struct iscsi_conn *conn); + void (*iscsit_free_cmd)(struct iscsi_cmnd *cmnd); + + void (*iscsit_set_req_data)(struct iscsi_cmnd *req, + struct iscsi_cmnd *rsp); + int (*iscsit_conn_alloc)(struct iscsi_session *session, struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn, @@ -38,15 +47,10 @@ struct iscsit_transport { void (*iscsit_conn_free)(struct iscsi_conn *conn); void (*iscsit_conn_close)(struct iscsi_conn *conn, int flags); void (*iscsit_mark_conn_closed)(struct iscsi_conn *conn, int flags); + ssize_t (*iscsit_get_initiator_ip)(struct iscsi_conn *conn, char *buf, int size); - int (*iscsit_send_locally)(struct iscsi_cmnd *cmnd, - unsigned int cmd_count); - void (*iscsit_set_sense_data)(struct iscsi_cmnd *rsp, - const u8 *sense_buf, int sense_len); - void (*iscsit_set_req_data)(struct iscsi_cmnd *req, - struct iscsi_cmnd *rsp); - int (*iscsit_receive_cmnd_data)(struct iscsi_cmnd *cmnd); + void (*iscsit_close_all_portals)(void); #if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) @@ -57,7 +61,7 @@ struct iscsit_transport { const char name[SCST_MAX_NAME]; enum iscsit_transport_type transport_type; struct list_head transport_list_entry; -}; +} ____cacheline_aligned; extern int iscsit_register_transport(struct iscsit_transport *t); extern void iscsit_unregister_transport(struct iscsit_transport *t); From cf03aae45e7158c645f020580bc562817b2b4262 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 31 Jan 2014 08:26:39 +0000 Subject: [PATCH 018/128] scst: Build fix for Debian GNU/Linux 6.0 Avoid that building the iscsi-scst target driver fails as follows: iscsi-scst/kernel/isert-scst/iser_rdma.c: In function 'isert_portal_listen': iscsi-scst/kernel/isert-scst/iser_rdma.c:1450: error: implicit declaration of function 'pr_warn' git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5257 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/include/scst_debug.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index 22145e7bd..d7c689bf1 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -53,7 +53,6 @@ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) #define pr_warning(fmt, ...) \ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) -#define pr_warn pr_warning #define pr_notice(fmt, ...) \ printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) #endif @@ -68,6 +67,15 @@ printk(KERN_CONT fmt, ##__VA_ARGS__) #endif #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) +/* + * See also patch "kernel.h: add pr_warn for symmetry to dev_warn, + * netdev_warn" (commit fc62f2f19edf46c9bdbd1a54725b56b18c43e94f). + */ +#ifndef pr_warn +#define pr_warn pr_warning +#endif +#endif #if !defined(INSIDE_KERNEL_TREE) #ifdef CONFIG_SCST_DEBUG From 1978c7a7edad2dff4382d22e8bb2e8a47a52f54a Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 2 Feb 2014 14:58:34 +0000 Subject: [PATCH 019/128] isert: Do not call rdma_destroy_qp() when there is isert_conn_qp_destroy() Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5258 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 5845738a1..f38695e8b 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1070,7 +1070,7 @@ static void isert_kref_free(struct kref *kref) isert_free_conn_resources(isert_conn); - rdma_destroy_qp(isert_conn->cm_id); + isert_conn_qp_destroy(isert_conn); mutex_lock(&dev_list_mutex); isert_dev->cq_qps[cq->idx]--; From 4375a5f59e884033778422359760d02d7f766395 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 2 Feb 2014 14:58:39 +0000 Subject: [PATCH 020/128] isert: Update OFED compilation instructions for advanced users Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5259 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/README.iser_ofed | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/iscsi-scst/README.iser_ofed b/iscsi-scst/README.iser_ofed index c93150b36..93310d72d 100644 --- a/iscsi-scst/README.iser_ofed +++ b/iscsi-scst/README.iser_ofed @@ -72,6 +72,13 @@ Next, download and install an OFED pacakge. For MLNX_OFED, just run the mlnxofedinstall script inside the MLNX_OFED directory. +NOTE TO ADVANCED USERS: +------------------------ +If you are installing MLNX_OFED by manually selecting which RPMs/DEBs to install, +make sure ofed_scripts package is one of them, since it is required for correct OFED +version detection by iscsi-scst makefile. + + For the OFED package.Make sure to enable at least the kernel-ib and kernel-ib-devel packages. An example: From bde39e463eef6c027e368e7e702864a6df918531 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 4 Feb 2014 07:03:04 +0000 Subject: [PATCH 021/128] isert: Fix smatch issues Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5261 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 13 ++++++++----- iscsi-scst/kernel/isert-scst/isert.c | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index f38695e8b..d3a3b3804 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1461,28 +1461,31 @@ int isert_portal_listen(struct isert_portal *portal, switch (sa->sa_family) { case AF_INET: - pr_info("iser portal cm_id:%p listens on: " #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + pr_info("iser portal cm_id:%p listens on: " NIPQUAD_FMT ":%d\n", portal->cm_id, NIPQUAD(((struct sockaddr_in *)sa)->sin_addr.s_addr), + (int)ntohs(((struct sockaddr_in *)sa)->sin_port)); #else + pr_info("iser portal cm_id:%p listens on: " "%pI4:%d\n", portal->cm_id, &((struct sockaddr_in *)sa)->sin_addr.s_addr, -#endif (int)ntohs(((struct sockaddr_in *)sa)->sin_port)); - +#endif break; case AF_INET6: - pr_info("iser portal cm_id:%p listens on: " #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + pr_info("iser portal cm_id:%p listens on: " NIP6_FMT " %d\n", portal->cm_id, NIP6(((struct sockaddr_in6 *)sa)->sin6_addr.s_addr), + (int)ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); #else + pr_info("iser portal cm_id:%p listens on: " "%pI6 %d\n", portal->cm_id, &((struct sockaddr_in6 *)sa)->sin6_addr, -#endif (int)ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); +#endif break; default: pr_err("Unknown address family\n"); diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c index d0688fce2..b62a8a6fe 100644 --- a/iscsi-scst/kernel/isert-scst/isert.c +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -459,11 +459,12 @@ static ssize_t isert_get_initiator_ip(struct iscsi_conn *conn, switch (ss.ss_family) { case AF_INET: - pos = scnprintf(buf, size, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + pos = scnprintf(buf, size, "%u.%u.%u.%u", NIPQUAD(((struct sockaddr_in *)&ss)->sin_addr.s_addr)); #else + pos = scnprintf(buf, size, "%pI4", &((struct sockaddr_in *)&ss)->sin_addr.s_addr); #endif break; From ea47bb189cf5174f148a7a0443b01c14bf93d241 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 4 Feb 2014 07:19:09 +0000 Subject: [PATCH 022/128] isert: Improve rdma_accept failure handling If rdma_accept fails, use the cleanup mechanism we already have for disconnect, instead of trying to reproduce the same cleanup. From upper layer it really does not matter if rdma_accept failed or we received disconnect immediately after rdma_accept succeeded. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5262 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 39 +++++++++++------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index d3a3b3804..39d11f2cd 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1120,6 +1120,15 @@ static void isert_sched_conn_closed(struct isert_connection *isert_conn) isert_conn_queue_work(&isert_conn->close_work); } +static int isert_cm_timewait_exit_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct isert_connection *isert_conn = cm_id->qp->qp_context; + + isert_sched_conn_closed(isert_conn); + return 0; +} + static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id, struct rdma_cm_event *event) { @@ -1157,6 +1166,11 @@ static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id, isert_conn->state = ISER_CONN_HANDSHAKE; + mutex_lock(&dev_list_mutex); + list_add_tail(&isert_conn->portal_node, &portal->conn_list); + list_add_tail(&isert_conn->dev_node, &isert_dev->conn_list); + mutex_unlock(&dev_list_mutex); + /* initiator is dst, target is src */ memcpy(&isert_conn->peer_addr, &cm_id->route.addr.dst_addr, sizeof(isert_conn->peer_addr)); @@ -1176,28 +1190,20 @@ static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id, err = rdma_accept(cm_id, &tgt_conn_param); if (unlikely(err)) { - module_put(THIS_MODULE); pr_err("Failed to accept conn request, err:%d\n", err); goto fail_accept; } - mutex_lock(&dev_list_mutex); - list_add_tail(&isert_conn->portal_node, &portal->conn_list); - list_add_tail(&isert_conn->dev_node, &isert_dev->conn_list); - mutex_unlock(&dev_list_mutex); - pr_info("iser accepted connection cm_id:%p\n", cm_id); out: TRACE_EXIT_RES(err); return err; fail_accept: - isert_conn_free(isert_conn); - mutex_lock(&dev_list_mutex); - list_del(&isert_conn->portal_node); - list_del(&isert_conn->dev_node); - mutex_unlock(&dev_list_mutex); - isert_conn_qp_destroy(isert_conn); + set_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags); + isert_cm_timewait_exit_handler(cm_id, NULL); + err = 0; + goto out; fail_conn_create: if (new_isert_dev) { @@ -1263,15 +1269,6 @@ static int isert_cm_disconnect_handler(struct rdma_cm_id *cm_id, return 0; } -static int isert_cm_timewait_exit_handler(struct rdma_cm_id *cm_id, - struct rdma_cm_event *event) -{ - struct isert_connection *isert_conn = cm_id->qp->qp_context; - - isert_sched_conn_closed(isert_conn); - return 0; -} - static const char *cm_event_type_str(enum rdma_cm_event_type ev_type) { switch (ev_type) { From 9869d54f6473025e171df64262b9e05d6f66877d Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 4 Feb 2014 11:46:52 +0000 Subject: [PATCH 023/128] isert: Update TODO Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5263 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/iscsi-scst/kernel/isert-scst/TODO b/iscsi-scst/kernel/isert-scst/TODO index a86e3cc1a..2bd1eca90 100644 --- a/iscsi-scst/kernel/isert-scst/TODO +++ b/iscsi-scst/kernel/isert-scst/TODO @@ -7,4 +7,5 @@ * Do not signal every "response sent" notification * Make the code NUMA aware * Add support for AHS +* Add support for bidi commands From d81f105ebb25b013f487d0b955244a51b9de9860 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 11 Feb 2014 08:55:42 +0000 Subject: [PATCH 024/128] isert: iscsi-scstd: Make sure we do not leak any resources on failure during accept Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5278 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/iscsi_scstd.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index e3d029496..b3bd7d238 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -296,18 +296,18 @@ static void iser_accept(int fd) ret = read(fd, buff, sizeof(buff)); if (ret == -1) - return; + goto out; conn_fd = open(buff, O_RDWR); if (conn_fd == -1) { log_error("open(iser_connection) %s failed: %s\n", buff, strerror(errno)); - return; + goto out; } ret = ioctl(conn_fd, GET_PORTAL_ADDR, &addr, sizeof(addr)); if (ret) - return; + goto out_close; ret = getnameinfo((struct sockaddr *)&addr, sizeof(addr), target_portal, sizeof(target_portal), target_portal_port, @@ -316,18 +316,17 @@ static void iser_accept(int fd) if (ret != 0) { log_error("Target portal getnameinfo() failed: %s!", get_error_str(ret)); - return; + goto out_close; } conn = alloc_and_init_conn(conn_fd); if (!conn) - return; + goto out_close; conn->target_portal = strdup(target_portal); if (conn->target_portal == NULL) { log_error("Unable to duplicate target portal %s", target_portal); - conn_free(conn); - return; + goto out_free; } conn->transmit = transmit_iser; @@ -337,6 +336,16 @@ static void iser_accept(int fd) incoming_cnt++; log_info("iSER connect\n"); + +out: + return; + +out_free: + conn_free(conn); + +out_close: + close(conn_fd); + goto out; } static int transmit_sock(int fd, bool start) From 3d3f5250cea38d9d3038d3236e25ebf96fd55763 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 11 Feb 2014 08:55:49 +0000 Subject: [PATCH 025/128] iscsi-scstd: No need to cal set_non_blocking() twice on the same fd during accept. set_non_blocking() is being called in alloc_and_init_conn(), so no need to call it again later Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5279 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/iscsi_scstd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index b3bd7d238..2b27dbe21 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -442,8 +442,6 @@ static void accept_connection(int listen) conn->is_discovery = tcp_is_discovery; conn_read_pdu(conn); - set_non_blocking(fd); - incoming_cnt++; out: From 2b778b99fff58b04c623bf4f406c40224c511f32 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 11 Feb 2014 08:55:54 +0000 Subject: [PATCH 026/128] isert: iscsi-scstd: Take into account con_blocking parameter when accepting iSER connection the same as in TCP code Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5280 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/iscsi_scstd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 2b27dbe21..3ad8d5d79 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -319,6 +319,11 @@ static void iser_accept(int fd) goto out_close; } + if (conn_blocked) { + log_warning("Connection refused due to blocking\n"); + goto out_close; + } + conn = alloc_and_init_conn(conn_fd); if (!conn) goto out_close; From 1a2b839ea6f66ed1caea9c71fdc5c2f1b95f6b50 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 20 Feb 2014 07:20:24 +0000 Subject: [PATCH 027/128] isert: Print QP number when connection is established for debug Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5297 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 39d11f2cd..376264f72 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -952,7 +952,7 @@ static int isert_conn_qp_create(struct isert_connection *isert_conn) } isert_conn->qp = cm_id->qp; - pr_info("iser created cm_id:%p qp:%p\n", cm_id, cm_id->qp); + pr_info("iser created cm_id:%p qp:0x%X\n", cm_id, cm_id->qp->qp_num); out: TRACE_EXIT_RES(err); From 934818459ae3a6fdf6e3194f264d35d5d288227f Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 20 Feb 2014 07:20:32 +0000 Subject: [PATCH 028/128] isert: Add warning print when new connection handling fails Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5298 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index ea2c92ab0..9a96ade4f 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -162,6 +162,7 @@ static int add_new_connection(struct isert_listener_dev *dev, TRACE_ENTRY(); if (!conn_dev) { + PRINT_WARNING("%s", "Unable to allocate new connection"); res = -ENOSPC; goto out; } From be89fd5eff9efa77270cb4c4ad7147b155b52299 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 20 Feb 2014 07:20:37 +0000 Subject: [PATCH 029/128] iscsid: Make sure we print error reason for all iser_accept() failures Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5299 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/iscsi_scstd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 3ad8d5d79..1789ec0c1 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -306,8 +306,11 @@ static void iser_accept(int fd) } ret = ioctl(conn_fd, GET_PORTAL_ADDR, &addr, sizeof(addr)); - if (ret) + if (ret) { + log_error("ioctl(GET_PORTAL_ADDR) failed: %s\n", + strerror(errno)); goto out_close; + } ret = getnameinfo((struct sockaddr *)&addr, sizeof(addr), target_portal, sizeof(target_portal), target_portal_port, From 3c5873c5e503fa015e540f94bd5a93d958220430 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 25 Feb 2014 13:51:27 +0000 Subject: [PATCH 030/128] iscsid: Use {un,}cork_transmit instead of transmit() for clarity Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5313 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/iscsi_scstd.c | 30 ++++++++++++++++++++++++++---- iscsi-scst/usr/iscsid.h | 3 ++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 1789ec0c1..7d9a13577 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -212,6 +212,16 @@ static int transmit_iser(int fd, bool start) return ioctl(fd, RDMA_CORK, &opt, sizeof(opt)); } +static int cork_transmit_iser(int fd) +{ + return transmit_iser(fd, true); +} + +static int uncork_transmit_iser(int fd) +{ + return transmit_iser(fd, false); +} + static void create_iser_listen_socket(struct pollfd *array) { struct addrinfo hints, *res, *res0; @@ -337,7 +347,8 @@ static void iser_accept(int fd) goto out_free; } - conn->transmit = transmit_iser; + conn->cork_transmit = cork_transmit_iser; + conn->uncork_transmit = uncork_transmit_iser; conn->getsockname = iser_getsockname; conn->is_discovery = iser_is_discovery; conn->is_iser = true; @@ -362,6 +373,16 @@ static int transmit_sock(int fd, bool start) return setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt)); } +static int cork_transmit_sock(int fd) +{ + return transmit_sock(fd, true); +} + +static int uncork_transmit_sock(int fd) +{ + return transmit_sock(fd, false); +} + static int tcp_is_discovery(int fd) { return 0; @@ -445,7 +466,8 @@ static void accept_connection(int listen) goto out_free; } - conn->transmit = transmit_sock; + conn->cork_transmit = cork_transmit_sock; + conn->uncork_transmit = uncork_transmit_sock; conn->getsockname = getsockname; conn->is_discovery = tcp_is_discovery; conn_read_pdu(conn); @@ -549,7 +571,7 @@ again: case IOSTATE_WRITE_AHS: case IOSTATE_WRITE_DATA: write_again: - conn->transmit(pollfd->fd, true); + conn->cork_transmit(pollfd->fd); res = write(pollfd->fd, conn->buffer, conn->rwsize); if (res < 0) { if (errno != EINTR && errno != EAGAIN) { @@ -589,7 +611,7 @@ again: goto write_again; } case IOSTATE_WRITE_DATA: - conn->transmit(pollfd->fd, false); + conn->uncork_transmit(pollfd->fd); cmnd_finish(conn); switch (conn->state) { diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 152ae1d25..456b72a25 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -131,7 +131,8 @@ struct connection { bool is_iser; - int (*transmit)(int fd, bool start); + int (*cork_transmit)(int fd); + int (*uncork_transmit)(int fd); int (*getsockname)(int fd, struct sockaddr *name, socklen_t *namelen); int (*is_discovery)(int fd); }; From 8d6b96edbc4d09c80a69df8524c8bff9c52871a3 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 25 Feb 2014 13:51:34 +0000 Subject: [PATCH 031/128] iscsid: Make iser connection request print more verbose Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5314 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/iscsi_scstd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 7d9a13577..b0875c761 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -332,6 +332,8 @@ static void iser_accept(int fd) goto out_close; } + log_info("iSER Connect to %s:%s", target_portal, target_portal_port); + if (conn_blocked) { log_warning("Connection refused due to blocking\n"); goto out_close; @@ -354,8 +356,6 @@ static void iser_accept(int fd) conn->is_iser = true; incoming_cnt++; - log_info("iSER connect\n"); - out: return; From 6637df7f0c91735148ef49033294ead01c551412 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 27 Feb 2014 06:30:26 +0000 Subject: [PATCH 032/128] isert: Rework disconnect handling Instead of waiting for TIMEDWAIT event when disconnecting and blocking, start the disconnect process when TIMEDWAIT event is received and only perform the actual rdma_disconnect upon disconnect request. Note that rdma_disconnect can not be called from atomic context, so need to execute it from workqueue. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5317 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/iscsi.h | 1 + iscsi-scst/kernel/isert-scst/iser.h | 6 +- iscsi-scst/kernel/isert-scst/iser_datamover.c | 3 +- iscsi-scst/kernel/isert-scst/iser_rdma.c | 22 ++----- iscsi-scst/kernel/isert-scst/isert.c | 58 +++--------------- iscsi-scst/kernel/isert-scst/isert.h | 5 -- iscsi-scst/kernel/isert-scst/isert_login.c | 61 ++++++++----------- 7 files changed, 41 insertions(+), 115 deletions(-) diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index ae43e34bd..d93c78d4f 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -310,6 +310,7 @@ struct iscsi_conn { #else struct work_struct nop_in_delayed_work; #endif + struct work_struct close_work; unsigned int nop_in_interval; /* in jiffies */ unsigned int nop_in_timeout; /* in jiffies */ struct list_head nop_req_list; diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index 727ea4116..a2266a009 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -102,8 +102,7 @@ struct isert_cq { int idx; }; -#define ISERT_TIMEWAIT_RECEIVED 0 -#define ISERT_CONNECTION_ABORTED 1 +#define ISERT_CONNECTION_ABORTED 0 struct isert_connection { struct iscsi_conn iscsi ____cacheline_aligned; @@ -157,7 +156,6 @@ struct isert_connection { struct list_head dev_node; struct list_head portal_node; - wait_queue_head_t waitQ; unsigned long flags; struct work_struct close_work; struct kref kref; @@ -227,8 +225,8 @@ int isert_post_send(struct isert_connection *isert_conn, int isert_alloc_conn_resources(struct isert_connection *isert_conn); void isert_free_conn_resources(struct isert_connection *isert_conn); -void isert_conn_close(struct isert_connection *isert_conn, int do_flush); void isert_conn_free(struct isert_connection *isert_conn); +void isert_conn_disconnect(struct isert_connection *isert_conn); static inline struct isert_connection *isert_conn_alloc(void) { diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.c b/iscsi-scst/kernel/isert-scst/iser_datamover.c index c09897466..2dcbf9364 100644 --- a/iscsi-scst/kernel/isert-scst/iser_datamover.c +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.c @@ -272,7 +272,8 @@ int isert_close_connection(struct iscsi_conn *iscsi_conn) { struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn; - isert_conn_close(isert_conn, 1); + isert_conn_disconnect(isert_conn); + return 0; } diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 376264f72..28f164211 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -118,7 +118,7 @@ int isert_post_send(struct isert_connection *isert_conn, return err; } -static void isert_conn_disconnect(struct isert_connection *isert_conn) +void isert_conn_disconnect(struct isert_connection *isert_conn) { int err = rdma_disconnect(isert_conn->cm_id); if (unlikely(err)) @@ -310,7 +310,7 @@ static void isert_recv_completion_handler(struct isert_wr *wr) if (unlikely(err)) { pr_err("err:%d while handling iser pdu\n", err); - isert_conn_close(wr->conn, 0); + isert_conn_disconnect(wr->conn); } TRACE_EXIT(); @@ -1022,8 +1022,6 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id, kref_init(&isert_conn->kref); - init_waitqueue_head(&isert_conn->waitQ); - pr_info("iser created connection cm_id:%p\n", cm_id); TRACE_EXIT(); return isert_conn; @@ -1045,17 +1043,6 @@ fail_get: /* start closing process; * only when all buffers released, can free */ -void isert_conn_close(struct isert_connection *isert_conn, int do_flush) -{ - isert_conn_disconnect(isert_conn); - if (do_flush) { - wait_event_interruptible(isert_conn->waitQ, - test_bit(ISERT_TIMEWAIT_RECEIVED, - &isert_conn->flags)); - flush_workqueue(isert_conn->cq_desc->cq_workqueue); - } -} - static void isert_kref_free(struct kref *kref) { struct isert_connection *isert_conn = container_of(kref, @@ -1068,6 +1055,8 @@ static void isert_kref_free(struct kref *kref) pr_info("isert_conn_free conn:%p\n", isert_conn); + flush_workqueue(isert_conn->cq_desc->cq_workqueue); + isert_free_conn_resources(isert_conn); isert_conn_qp_destroy(isert_conn); @@ -1100,9 +1089,6 @@ static void isert_conn_closed_do_work(struct work_struct *work) struct isert_connection *isert_conn = container_of(work, struct isert_connection, close_work); - set_bit(ISERT_TIMEWAIT_RECEIVED, &isert_conn->flags); - wake_up_interruptible(&isert_conn->waitQ); - /* notify upper layer */ if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) isert_connection_closed(&isert_conn->iscsi); diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c index b62a8a6fe..844fba8ff 100644 --- a/iscsi-scst/kernel/isert-scst/isert.c +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -52,41 +52,8 @@ module_param(isert_nr_devs, uint, S_IRUGO); MODULE_PARM_DESC(isert_nr_devs, "Maximum concurrent number of connection requests to handle."); -static void isert_do_close_conn(struct iscsi_conn *conn) -{ - isert_close_connection(conn); - start_close_conn(conn); -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -static void isert_close_conn_fn(void *ctx) -#else -static void isert_close_conn_fn(struct work_struct *work) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - struct isert_close_conn_work *conn_work = ctx; -#else - struct isert_close_conn_work *conn_work = container_of(work, - struct isert_close_conn_work, close_work); -#endif - struct iscsi_conn *conn = conn_work->conn; - - /* Take care of case where our connection is being closed - * without being connected to a session - if connection allocation - * failed for some reason */ - if (unlikely(!conn->session)) - isert_free_connection(conn); - else - isert_do_close_conn(conn); - - kfree(conn_work); -} - static void isert_mark_conn_closed(struct iscsi_conn *conn, int flags) { - struct isert_close_conn_work *conn_work; - TRACE_ENTRY(); if (flags & ISCSI_CONN_ACTIVE_CLOSE) conn->active_close = 1; @@ -97,25 +64,9 @@ static void isert_mark_conn_closed(struct iscsi_conn *conn, int flags) if (!conn->closing) { conn->closing = 1; - - conn_work = kmalloc(sizeof(*conn_work), GFP_ATOMIC); - if (unlikely(!conn_work)) { - PRINT_CRIT_ERROR("Unable to allocate isert_close_conn_work for conn %p\n", - conn); - goto out; - } - - conn_work->conn = conn; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - INIT_WORK(&conn_work->close_work, isert_close_conn_fn, - conn_work); -#else - INIT_WORK(&conn_work->close_work, isert_close_conn_fn); -#endif - schedule_work(&conn_work->close_work); + schedule_work(&conn->close_work); } -out: TRACE_EXIT(); } @@ -326,6 +277,13 @@ static void isert_conn_free(struct iscsi_conn *conn) int isert_handle_close_connection(struct iscsi_conn *conn) { isert_mark_conn_closed(conn, 0); + /* Take care of case where our connection is being closed + * without being connected to a session - if connection allocation + * failed for some reason */ + if (unlikely(!conn->session)) + isert_free_connection(conn); + else + start_close_conn(conn); return 0; } diff --git a/iscsi-scst/kernel/isert-scst/isert.h b/iscsi-scst/kernel/isert-scst/isert.h index 1e692fd01..78223527b 100644 --- a/iscsi-scst/kernel/isert-scst/isert.h +++ b/iscsi-scst/kernel/isert-scst/isert.h @@ -81,11 +81,6 @@ struct isert_listener_dev { int free_portal_idx; }; -struct isert_close_conn_work { - struct work_struct close_work; - struct iscsi_conn *conn; -}; - enum isert_conn_dev_state { CS_INIT, CS_REQ_BHS, diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 9a96ade4f..0d6ea579b 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -35,7 +35,6 @@ #include #include -#include /* kmalloc() */ #include /* everything... */ #include /* error codes */ #include @@ -103,29 +102,10 @@ static void release_dev(struct isert_conn_dev *dev) spin_unlock(&isert_listen_dev.conn_lock); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -static void isert_login_close_conn_fn(void *ctx) -#else -static void isert_login_close_conn_fn(struct work_struct *work) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - struct isert_close_conn_work *conn_work = ctx; -#else - struct isert_close_conn_work *conn_work = container_of(work, - struct isert_close_conn_work, close_work); -#endif - struct iscsi_conn *conn = conn_work->conn; - - isert_close_connection(conn); - - kfree(conn_work); -} - static void isert_conn_timer_fn(unsigned long arg) { struct isert_conn_dev *conn_dev = (struct isert_conn_dev *)arg; - struct isert_close_conn_work *conn_work; + struct iscsi_conn *conn = conn_dev->conn; TRACE_ENTRY(); @@ -133,23 +113,8 @@ static void isert_conn_timer_fn(unsigned long arg) PRINT_ERROR("Timeout on connection %p\n", conn_dev->conn); - conn_work = kmalloc(sizeof(*conn_work), GFP_ATOMIC); - if (unlikely(!conn_work)) { - PRINT_CRIT_ERROR("Unable to allocate isert_close_conn_work for conn %p\n", - conn_dev->conn); - goto out; - } + schedule_work(&conn->close_work); - conn_work->conn = conn_dev->conn; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - INIT_WORK(&conn_work->close_work, isert_login_close_conn_fn, - conn_work); -#else - INIT_WORK(&conn_work->close_work, isert_login_close_conn_fn); -#endif - schedule_work(&conn_work->close_work); - -out: TRACE_EXIT(); } @@ -191,6 +156,22 @@ static bool have_new_connection(struct isert_listener_dev *dev) return ret; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void isert_close_conn_fn(void *ctx) +#else +static void isert_close_conn_fn(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct iscsi_conn *conn = ctx; +#else + struct iscsi_conn *conn = container_of(work, + struct iscsi_conn, close_work); +#endif + + isert_close_connection(conn); +} + int isert_conn_alloc(struct iscsi_session *session, struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn, @@ -236,6 +217,12 @@ int isert_conn_alloc(struct iscsi_session *session, conn->transport = t; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&conn->close_work, isert_close_conn_fn, conn); +#else + INIT_WORK(&conn->close_work, isert_close_conn_fn); +#endif + res = iscsi_init_conn(session, info, conn); if (unlikely(res)) goto cleanup_conn; From d054d4a77af3e681afdb07b6d460fa5115e05d3f Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 27 Feb 2014 06:30:33 +0000 Subject: [PATCH 033/128] isert: Fix case when iscsid is not able to handle login request on time or at all In some cases, iscsi-scstd chooses to close the connection device and abort the connection. This can cause invalid device state due to order of isert_conn_dev cleanup and disconnect handling. Make sure we release isert_conn_dev only after we received disconnect event, or we passed the connection to the kernel. This also fixes an issue if iscsi-scstd is run on very CPU intensive load and it does not receive CPU time to serve the login requests. This may get to the extreme of initiator disconnecting before iscsi-scstd had the chance to handle the login request. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5318 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert.h | 1 + iscsi-scst/kernel/isert-scst/isert_login.c | 29 ++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert.h b/iscsi-scst/kernel/isert-scst/isert.h index 78223527b..9570f030c 100644 --- a/iscsi-scst/kernel/isert-scst/isert.h +++ b/iscsi-scst/kernel/isert-scst/isert.h @@ -115,6 +115,7 @@ struct isert_conn_dev { int is_discovery; struct timer_list tmo_timer; int timer_active; + struct kref kref; }; #define ISER_CONN_DEV_PREFIX "isert/conn" diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 0d6ea579b..0cbfef731 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -93,15 +93,29 @@ static void isert_del_timer(struct isert_conn_dev *dev) static void release_dev(struct isert_conn_dev *dev) { - isert_del_timer(dev); + kref_init(&dev->kref); spin_lock(&isert_listen_dev.conn_lock); dev->occupied = 0; list_del_init(&dev->conn_list_entry); dev->state = CS_INIT; + atomic_set(&dev->available, 1); spin_unlock(&isert_listen_dev.conn_lock); } +static void isert_kref_release_dev(struct kref *kref) +{ + struct isert_conn_dev *dev = container_of(kref, + struct isert_conn_dev, + kref); + release_dev(dev); +} + +static void isert_dev_release(struct isert_conn_dev *dev) +{ + kref_put(&dev->kref, isert_kref_release_dev); +} + static void isert_conn_timer_fn(unsigned long arg) { struct isert_conn_dev *conn_dev = (struct isert_conn_dev *)arg; @@ -233,9 +247,10 @@ int isert_conn_alloc(struct iscsi_session *session, goto cleanup_iscsi_conn; #endif - list_add_tail(&conn->conn_list_entry, &session->conn_list); - conn->rd_state = 1; + isert_dev_release(dev); + + list_add_tail(&conn->conn_list_entry, &session->conn_list); res = isert_login_rsp_tx(cmnd, true, false); vunmap(dev->sg_virt); dev->sg_virt = NULL; @@ -335,6 +350,7 @@ static ssize_t isert_listen_read(struct file *filp, char __user *buf, conn_dev = list_first_entry(&dev->new_conn_list, struct isert_conn_dev, conn_list_entry); list_move(&conn_dev->conn_list_entry, &dev->curr_conn_list); + kref_get(&conn_dev->kref); spin_unlock(&dev->conn_lock); res = snprintf(k_buff, sizeof(k_buff), "/dev/"ISER_CONN_DEV_PREFIX"%d", @@ -423,6 +439,7 @@ int isert_connection_closed(struct iscsi_conn *iscsi_conn) dev->conn = NULL; wake_up(&dev->waitqueue); + isert_dev_release(dev); } isert_free_connection(iscsi_conn); @@ -480,8 +497,9 @@ static int isert_release(struct inode *inode, struct file *filp) dev->conn = NULL; } - release_dev(dev); - atomic_inc(&dev->available); + isert_del_timer(dev); + + isert_dev_release(dev); TRACE_EXIT_RES(res); return res; @@ -797,6 +815,7 @@ static void __init isert_setup_cdev(struct isert_conn_dev *dev, dev->login_rsp = NULL; spin_lock_init(&dev->pdu_lock); atomic_set(&dev->available, 1); + kref_init(&dev->kref); dev->state = CS_INIT; err = cdev_add(&dev->cdev, dev->devno, 1); /* Fail gracefully if need be */ From f7e140ce26efb6120665dca33c474599fdbe3bce Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 27 Feb 2014 06:30:38 +0000 Subject: [PATCH 034/128] isert: Add iser readme with troubleshooting advice Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5319 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/README.iser | 108 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 iscsi-scst/README.iser diff --git a/iscsi-scst/README.iser b/iscsi-scst/README.iser new file mode 100644 index 000000000..819b1b574 --- /dev/null +++ b/iscsi-scst/README.iser @@ -0,0 +1,108 @@ +iSCSI extensions for RDMA driver: +================================== + +Installation & Configuration: +--------------------------- +For installation and configuration, see iscsi README. +There are no specific configuration options for iSER. +See below for performance optimizations as well as troubleshooting. + + +Performance considerations: +--------------------------- + +In order to achieve better performance, it is recommended to specify +"QueuedCommands 128" parameter per iSER target, since the transport +is very fast and you usually want to connect it to fast backstorage. + + +Troubleshooting: +----------------- +* Initiator fails to connect to target. The following message is seen in dmesg: + Failed to accept conn request, err: -22 + The cause of this is often compilation issues if you have OFED or MLNX_OFED installed: + If you are compiling for OFED/MLNX_OFED, make sure OFED is installed for + the kernel you are running. Also, make sure you followed ALL steps described + in README.iser_ofed including patching the kernel. + If you are compiling for non-OFED kernel, make sure you don't have + OFED/MLNX_OFED installed. + + +* Discovery of iSER targets takes a long time or login to all discovered targets fails. + iSCSI discovery does not have a way to determine between iSCSI and iSER + enabled portals. Thus, initiator tries to connect to all interfaces it + discovered (by default discovery is done over iSCSI TCP). + In order to prevent this behaviour, you should specify + "allowed_portal " parameter for each target you want + to export through specific RDMA capable adapters. + + +* Initiator keeps connecting and disconnecting from target in a loop + with constant interval after target reboot. + The problem may be that connection requests from initiator are received + on wrong port/HCA. This can be one due to one (or both) of the following issues: + 1) net.ipv4.conf.all.arp_ignore sysclt is not set to 2 + rdma-cm relies on ARP responses being received on the same interface + that sent the request. Linux default does not do that. + In order to make Linux behave good for rdma-cm, you _MUST_ add + "net.ipv4.conf.all.arp_ignore = 2" to /etc/sysctl.conf + 2) You have more than 1 HCA and PCI mappings to netdev devices is not + persistent between reboots. Possible solution is to have udev rules + for mapping the ibX devices in persistent way. + See below for udev scripts example: + +/lib/udev/net.sh +------------------- +#!/bin/sh + +. /etc/sysconfig/net.conf + +type_fd="/sys/${DEVPATH}/type" +if [ ! -f $type_fd ]; then + exit +fi +type=`cat /sys/${DEVPATH}/type` + +if [ "$type" = "32" ]; then # IPoIB interface + i=0 + CONFDEV="DEV${i}" + CONFPCI=${!CONFDEV} + PCI=`basename $PHYSDEVPATH` + while [ -n "$CONFPCI" ]; do + if [ "$CONFPCI" = "$PCI" ]; then + devid=$(printf "%d\n" `cat /sys/$DEVPATH/dev_id`) + let id=$i*2+$devid + DEV="ib$id" + echo "$DEV" + exit + fi + let i=i+1 + CONFDEV="DEV$i" + CONFPCI=${!CONFDEV} + done +fi + +/etc/sysconfig/net.conf +----------------------- +DEV0="0000:01:00.0" +DEV1="0000:02:00.0" + +/etc/udev/rules.d/90-network.rules +------------------------------------- +ACTION=="add", SUBSYSTEM=="net", PROGRAM="/lib/udev/net.sh", RESULT=="?*", NAME="$result" + + +* Login to all targets from initiator sometimes times out. + It may be a network problem (try running tools like ibdiagnet + and rping between target and initiator hosts). The description of those tools + is beyond the scope of this readme. + Another issue may be that you failed to set net.ipv4.conf.all.arp_ignore sysctl + to the value of 2 (see above problem for more detailed explanation). + + +* When running IO, latency is getting higher and higher all the time. + If you have enabled intel_iommu either in kernel command line or in + kernel config (it may be enabled by default), you should specify + iommu=pt on kernel command line to avoid the latency issue. + + From 0d0d959485ab66fc988f611658448e157a712479 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 3 Mar 2014 08:18:19 +0000 Subject: [PATCH 035/128] Merged revisions 5246-5256,5260,5264,5266-5277,5281-5296,5300-5312,5315-5316,5320 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r5246 | vlnb | 2014-01-29 05:30:24 +0200 (Wed, 29 Jan 2014) | 3 lines Put CDB control byte parsing in one place ........ r5247 | vlnb | 2014-01-29 06:16:58 +0200 (Wed, 29 Jan 2014) | 3 lines Better version of the previous patch ........ r5248 | vlnb | 2014-01-30 03:40:48 +0200 (Thu, 30 Jan 2014) | 7 lines [PATCH 1/2] scst_sysfs: Make it easier to add new target sysfs attributes This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5249 | vlnb | 2014-01-30 03:41:54 +0200 (Thu, 30 Jan 2014) | 10 lines [PATCH 2/2] scst_sysfs: Add I/O statistics per target Although it is possible to obtain these statistics by iterating over all sessions and by computing the sum of the per-target statistics, make per-target statistics directly available such that these can be retrieved easily. Signed-off-by: Bart Van Assche ........ r5250 | vlnb | 2014-01-30 04:32:44 +0200 (Thu, 30 Jan 2014) | 3 lines Update for 3.13 kernels ........ r5251 | bvassche | 2014-01-30 11:16:27 +0200 (Thu, 30 Jan 2014) | 1 line nightly build: Add kernel 3.13 build infrastructure ........ r5252 | bvassche | 2014-01-30 11:30:18 +0200 (Thu, 30 Jan 2014) | 1 line scripts/kernel-functions: Add a bug fix for the kernel 3.13 series that is not yet present in the kernel 3.13 stable series ........ r5253 | bvassche | 2014-01-30 11:31:32 +0200 (Thu, 30 Jan 2014) | 1 line nightly build: Add kernel version 3.13.1 ........ r5254 | vlnb | 2014-01-31 04:32:02 +0200 (Fri, 31 Jan 2014) | 10 lines scst_pres: Simplify PR locking Since the time during which a PR read or write lock is held is short, use a mutex to implement PR read and write locking. So although this patch excludes multiple simultaneous readers that shouldn't affect the time needed to process a PR operation measurably. Signed-off-by: Bart Van Assche ........ r5255 | vlnb | 2014-01-31 04:33:11 +0200 (Fri, 31 Jan 2014) | 5 lines scst_vdisk: Check that "filename" is specified at most once Signed-off-by: Bart Van Assche ........ r5256 | vlnb | 2014-01-31 04:35:20 +0200 (Fri, 31 Jan 2014) | 5 lines scst_vdisk: Sort "add_device_parameters" alphabetically Signed-off-by: Bart Van Assche ........ r5260 | bvassche | 2014-02-03 11:03:03 +0200 (Mon, 03 Feb 2014) | 1 line scripts/list-source-files: Handle Mercurial subdirectories properly ........ r5264 | bvassche | 2014-02-06 14:46:51 +0200 (Thu, 06 Feb 2014) | 9 lines scst_local: Fix a kernel oops for kernel versions < 2.6.37 Avoid that scst_local triggers "BUG: unable to handle kernel NULL pointer dereference" on kernel versions before 2.6.37. This patch fixes a regression introduced via patch "scst_local: Avoid deadlock during module removal with kernel 3.6" (trunk r4566). Reported-by: Sebastian Herbszt ........ r5266 | bvassche | 2014-02-06 15:30:06 +0200 (Thu, 06 Feb 2014) | 14 lines Hush Coverity warning of scst_ws_push_single_write() uninitialized pointer Coverity warns that sgv may be used uninitialized. The warning applies to WRITE SAME commands with LBDATA == PBDATA == 0 (replicate a single block of user data into the specified LBA range). The warning appears to be spurious - when LBDATA == PBDATA == 0, scst_ws_write_cmd_finished() will not use the uninitialized value saved by scst_ws_push_single_write(). Move initialization of sgv earlier in the function to quiesce the warning. Signed-off-by: Steven J. Magnani ........ r5267 | bvassche | 2014-02-06 15:38:28 +0200 (Thu, 06 Feb 2014) | 7 lines qla2x00t: Re-sync help text with the code The ql2xfdmienable module parameter defaults to 1, but the help text claims it defaults to zero. Signed-off-by: Steven J. Magnani ........ r5268 | bvassche | 2014-02-06 16:17:49 +0200 (Thu, 06 Feb 2014) | 6 lines ib_srpt: Avoid that disabling a target triggers a race condition Avoid that disabling a target triggers a race condition with SRP relogin. At least in theory this race condition could result in a kernel crash. ........ r5269 | bvassche | 2014-02-07 09:31:38 +0200 (Fri, 07 Feb 2014) | 9 lines scst_sysfs: Fix a build failure on kernels 2.6.2[678] The sysfs API is supported from kernel 2.6.26 on and uses the swap() macro while the swap() macro was introduced in kernel 2.6.29. Hence provide a definition of the swap() macro for kernels before 2.6.29. Signed-off-by: Sebastian Herbszt [bvanassche: Moved swap() definition a few lines down and added #ifndef/#endif] ........ r5270 | bvassche | 2014-02-07 09:45:15 +0200 (Fri, 07 Feb 2014) | 2 lines regression tests: Run the 2.6.26..2.6.32 tests on the sysfs code instead of procfs ........ r5271 | bvassche | 2014-02-07 10:11:28 +0200 (Fri, 07 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5272 | bvassche | 2014-02-07 14:43:25 +0200 (Fri, 07 Feb 2014) | 16 lines scst_user, rt: Wake command processing thread when needed In a fully-preemptible realtime kernel (CONFIG_PREEMPT_RT_FULL=y), SCSI commands from an initiator time out because the userland target application is never woken to process them. This is because in a fully-preemptible realtime kernel, soft-IRQ (tasklet) execution always occurs in a ksoftirqd thread and preempt_count is not manipulated on soft-IRQ processing entry/exit. This makes in_interrupt() useless for determining whether soft-IRQ processing is occurring; instead, in_serving_softirq() should be used for that purpose. Signed-off-by: Steven J. Magnani [bvanassche: Elaborated source code comment] ........ r5273 | bvassche | 2014-02-07 14:46:39 +0200 (Fri, 07 Feb 2014) | 9 lines scst_vdisk: Build fix for kernels 2.6.27..2.6.30 add_to_page_cache_lru and __lock_page_killable are exported since kernel version 2.6.30. See also patch "Staging: pohmelfs: kconfig/makefile and vfs changes" (commit 18bc0bbd162e3eb3e7ea2953c315ad4113a57164; included in kernel v2.6.30). Signed-off-by: Sebastian Herbszt ........ r5274 | vlnb | 2014-02-08 03:04:27 +0200 (Sat, 08 Feb 2014) | 9 lines scst_user: Convert sgv_purge_interval to jiffies before use The sgv_purge_interval from userland is passed down without conversion to jiffies. Yet, if it is zero, the default value is (60 * HZ). Convert to jiffies before passing down. Signed-off-by: Steven J. Magnani ........ r5275 | vlnb | 2014-02-08 03:52:03 +0200 (Sat, 08 Feb 2014) | 19 lines Fix spurious BUG when parse_type != SCST_USER_PARSE_STANDARD Changeset 4224 introduced EXTRACHECKS for valid lba/data_len and state at the end of the parsing phase of command processing. However, the checks do not account for deferral of parsing to userland, as occurs when SCST_USER_PARSE_CALL or SCST_USER_PARSE_EXCEPTION are specified. In such cases the checks report errors on commands that userland has not yet had an opportunity to parse. NOTE: this includes a refactoring of the EXTRACHECKS to improve clarity. The rework is not exactly equivalent to the original code, but does conform to the comments describing the original code. Specifically, the original code would not trap an illegal command state unless there was also an illegal lba or data_len. Signed-off-by: Steven J. Magnani with some improvements ........ r5276 | bvassche | 2014-02-08 10:24:28 +0200 (Sat, 08 Feb 2014) | 1 line scst: Build fix for kernel versions before 2.6.37 ........ r5277 | bvassche | 2014-02-09 18:50:10 +0200 (Sun, 09 Feb 2014) | 1 line scst_debug.h: Avoid that the sBUG() and sBUG_ON() definitions confuse the smatch static code checker ........ r5281 | vlnb | 2014-02-13 06:02:56 +0200 (Thu, 13 Feb 2014) | 8 lines iscsi-scst: fix offset calculation Fixed a subtle bug in iSCSI-SCST with incorrectly calculated offsets for non-page aligned transfers. Originally discovered, investigated and fix suggested by Кирилл Тюшев, then Shahar Salzman tested and proved it. See http://sourceforge.net/mailarchive/message.php?msg_id=31924078 ........ r5282 | vlnb | 2014-02-13 06:15:31 +0200 (Thu, 13 Feb 2014) | 3 lines Web update ........ r5283 | bvassche | 2014-02-14 15:05:55 +0200 (Fri, 14 Feb 2014) | 7 lines Makefiles: remove redundant 'depmod' invocations Running 'make modules_install' already triggers invocation of depmod, hence leave it out from those Makefiles that use 'make modules_install'. Signed-off-by: Steven J. Magnani ........ r5284 | bvassche | 2014-02-14 15:48:54 +0200 (Fri, 14 Feb 2014) | 2 lines Makefiles: Convert from "install" to "make modules_install" ........ r5285 | bvassche | 2014-02-14 16:46:11 +0200 (Fri, 14 Feb 2014) | 1 line mvsas_tgt/Makefile: Remove trailing whitespace ........ r5286 | bvassche | 2014-02-14 17:52:10 +0200 (Fri, 14 Feb 2014) | 18 lines Makefiles: calculate KVER properly When deriving the kernel version (KVER) from KDIR, the file $(KDIR)/include/config/kernel.release should be preferred over 'make kernelversion'. For example, the Ubuntu 3.2.0-23-generic kernel has a kernel.release file containing '3.2.0-23-generic', but 'make kernelversion' returns 3.2.14. Since the modules are stored under /lib/modules/3.2.0-23-generic, the value in kernel.release is the correct one to use. Also: - Evaluate KVER only once - All depmod commands must include KVER Signed-off-by: Steven J. Magnani [bvanassche: Split long lines / removed trailing whitespace] ........ r5287 | bvassche | 2014-02-14 21:27:09 +0200 (Fri, 14 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5288 | bvassche | 2014-02-18 10:31:44 +0200 (Tue, 18 Feb 2014) | 22 lines scst, qla2x00t: Prevent inappropriate sleeping with a real-time kernel With a realtime kernel with full preemption (CONFIG_PREEMPT_RT_FULL), spinlocks can sleep, interrupt handlers run in thread context, and the standard local_irq functions manipulate preemptibility, not HW interruptibility. Under these conditions, most calls to local_irq functions should be replaced by no-ops. The CONFIG_PREEMPT_RT patch defines _nort versions of local_irq functions that compile away under CONFIG_PREEMPT_RT_FULL and compile to their "normal" equivalents otherwise. Define _nort equivalents to support compilation against both "normal" and RT-patched kernels, and use the _nort local_irq functons in cases where spinlocks are taken within a local_irq_save() or local_irq_disable() block. Without these changes, runtime warnings about "sleeping function called from invalid context" occur. Signed-off-by: Steven J. Magnani [bvanassche: Edited patch description and comment in scst_priv.h] ........ r5289 | bvassche | 2014-02-18 10:40:36 +0200 (Tue, 18 Feb 2014) | 13 lines Makefiles: respect DESTDIR when specified Not all SCST components handle DESTDIR properly, or at all. In particular: * INSTALL_MOD_PATH should account for DESTDIR when 'make modules_install' is invoked, so the kernel make infrastructure deploys the modules and runs depmod against the proper directory tree. * depmods must include a '-b' option to reference the proper directory tree. * Drop special ISCSI_DESTDIR. Signed-off-by: Steven J. Magnani ........ r5290 | bvassche | 2014-02-18 10:41:30 +0200 (Tue, 18 Feb 2014) | 7 lines Makefiles: 'uninstall' target fixes Some components don't have 'uninstall' targets although the top-level Makefile references them. Some others don't remove the proper file. Signed-off-by: Steven J. Magnani ........ r5291 | vlnb | 2014-02-19 05:45:48 +0200 (Wed, 19 Feb 2014) | 8 lines Fix incorrect start and length calculation for issuing block discard requests Block layer always expects start and length in 512 byte blocks, so they should be corrected for non-512b SCST devices. Original patch from Ken Raeburn ........ r5292 | vlnb | 2014-02-19 06:06:10 +0200 (Wed, 19 Feb 2014) | 3 lines Cleanups ........ r5293 | vlnb | 2014-02-19 06:21:00 +0200 (Wed, 19 Feb 2014) | 12 lines scst_user: Complete "Preparing" / "finished" symmetry Add some TRACE statements so events sent to userland are bracketed by "Preparing" and "finished". This makes it a little easier to find the boundaries between the various stages of command processing in trace output. Note, this patch does not implement a 'finished' message for TM events; there is already a "TM reply" message that can serve that purpose. Signed-off-by: Steven J. Magnani ........ r5294 | bvassche | 2014-02-19 09:38:57 +0200 (Wed, 19 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5295 | bvassche | 2014-02-19 10:51:35 +0200 (Wed, 19 Feb 2014) | 1 line scripts/blockdev-perftest: Fix bashisms ........ r5296 | vlnb | 2014-02-20 07:54:49 +0200 (Thu, 20 Feb 2014) | 3 lines put_page_callback patch for 3.13.3+ kernels ........ r5300 | vlnb | 2014-02-21 04:08:05 +0200 (Fri, 21 Feb 2014) | 3 lines Docs update ........ r5301 | bvassche | 2014-02-21 09:44:55 +0200 (Fri, 21 Feb 2014) | 1 line nightly build: Add support for the put_page_callback-3.13.3 patch ........ r5302 | bvassche | 2014-02-21 09:48:21 +0200 (Fri, 21 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5303 | bvassche | 2014-02-21 12:02:11 +0200 (Fri, 21 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5304 | bvassche | 2014-02-21 12:09:45 +0200 (Fri, 21 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5305 | bvassche | 2014-02-24 08:56:05 +0200 (Mon, 24 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5306 | bvassche | 2014-02-24 08:56:44 +0200 (Mon, 24 Feb 2014) | 1 line Spelling fix: initator -> initiator ........ r5307 | bvassche | 2014-02-24 09:30:50 +0200 (Mon, 24 Feb 2014) | 1 line make rpm: Do not remove rpmbuilddir ........ r5308 | bvassche | 2014-02-24 09:39:45 +0200 (Mon, 24 Feb 2014) | 5 lines scst_local: Add newline to sysfs output Signed-off-by: Sebastian Herbszt [bvanassche: Reduced source code line length to 80 columns] ........ r5309 | bvassche | 2014-02-25 12:55:36 +0200 (Tue, 25 Feb 2014) | 1 line put_page_callback-3.12.11.patch: Add ........ r5310 | bvassche | 2014-02-25 12:57:27 +0200 (Tue, 25 Feb 2014) | 1 line put_page_callback-3.10.30.patch: Add ........ r5311 | bvassche | 2014-02-25 12:58:08 +0200 (Tue, 25 Feb 2014) | 1 line nightly build: Add support for kernels >= 3.10.30 and >= 3.12.11 ........ r5312 | bvassche | 2014-02-25 12:59:54 +0200 (Tue, 25 Feb 2014) | 1 line nightly build: Update kernel versions ........ r5315 | vlnb | 2014-02-26 04:32:39 +0200 (Wed, 26 Feb 2014) | 3 lines Make internal memory layout more cache friendly ........ r5316 | vlnb | 2014-02-26 04:49:38 +0200 (Wed, 26 Feb 2014) | 5 lines scst_vdisk: Make vendor, product ID and related fields configurable via sysfs Signed-off-by: Bart Van Assche ........ r5320 | bvassche | 2014-03-02 10:49:50 +0200 (Sun, 02 Mar 2014) | 1 line Documentation spelling fix: change INQUERY into INQUIRY ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5321 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 22 +- fcst/Makefile | 49 +- ibmvstgt/Makefile | 30 +- ibmvstgt/README.sysfs | 11 + ibmvstgt/src/ibmvstgt.c | 26 - iscsi-scst/Makefile | 44 +- iscsi-scst/kernel/iscsi.c | 2 + iscsi-scst/kernel/nthread.c | 2 + .../patches/put_page_callback-3.10.30.patch | 420 ++++++++++++++ .../patches/put_page_callback-3.12.11.patch | 407 ++++++++++++++ .../patches/put_page_callback-3.13.3.patch | 403 +++++++++++++ .../patches/put_page_callback-3.13.patch | 419 ++++++++++++++ mpt/Makefile | 22 +- mvsas_tgt/Makefile | 28 +- mvsas_tgt/README | 2 +- nightly/conf/nightly.conf | 7 +- qla2x00t/Makefile | 21 +- qla2x00t/qla2x00-target/ChangeLog | 2 +- qla2x00t/qla2x00-target/Makefile | 35 +- qla2x00t/qla2x00-target/Makefile_in-tree-3.13 | 5 + qla2x00t/qla_inline.h | 10 + qla2x00t/qla_os.c | 4 +- qla_isp/README.scst | 2 +- scripts/blockdev-perftest | 12 +- scripts/generate-kernel-patch | 16 +- scripts/kernel-functions | 39 ++ scripts/list-source-files | 8 +- scst.spec.in | 6 +- scst/ChangeLog | 2 +- scst/README | 18 + scst/README_in-tree | 18 + scst/SysfsRules | 6 +- scst/include/scst.h | 101 ++-- scst/include/scst_debug.h | 9 + .../in-tree/Kconfig.drivers.Linux-3.13.patch | 13 + .../kernel/in-tree/Makefile.dev_handlers-3.13 | 14 + .../in-tree/Makefile.drivers.Linux-3.13.patch | 12 + scst/kernel/in-tree/Makefile.scst-2.6.26 | 2 +- scst/kernel/in-tree/Makefile.scst-2.6.27 | 2 +- scst/kernel/in-tree/Makefile.scst-2.6.28 | 2 +- scst/kernel/in-tree/Makefile.scst-2.6.29 | 2 +- scst/kernel/in-tree/Makefile.scst-2.6.30 | 2 +- scst/kernel/in-tree/Makefile.scst-2.6.31 | 2 +- scst/kernel/in-tree/Makefile.scst-2.6.32 | 2 +- scst/kernel/in-tree/Makefile.scst-3.13 | 13 + scst/kernel/scst_exec_req_fifo-3.13.patch | 528 ++++++++++++++++++ scst/src/Makefile | 26 +- scst/src/dev_handlers/Makefile | 27 +- scst/src/dev_handlers/scst_user.c | 21 +- scst/src/dev_handlers/scst_vdisk.c | 498 +++++++++++++++-- scst/src/scst_lib.c | 14 +- scst/src/scst_pres.c | 22 +- scst/src/scst_pres.h | 114 +--- scst/src/scst_priv.h | 20 + scst/src/scst_sysfs.c | 135 +++-- scst/src/scst_targ.c | 105 ++-- scst_local/Makefile | 37 +- scst_local/in-tree/Makefile-3.13 | 2 + scst_local/scst_local.c | 21 +- scstadmin/Makefile | 1 - .../scst-0.9.10/t/after-restore.conf | 2 +- .../scst-0.9.10/t/to-be-restored.conf | 2 +- srpt/Makefile | 35 +- srpt/patches/kernel-3.13-pre-cflags.patch | 12 + srpt/src/ib_srpt.c | 10 +- usr/fileio/Makefile | 2 +- www/handler_fileio_tgt.html | 2 +- www/scst_admin.html | 2 +- www/target_emulex.html | 16 +- www/target_fcoe.html | 2 +- www/target_ibmvscsi.html | 2 +- www/target_iscsi.html | 2 +- www/target_iser.html | 2 +- www/target_local.html | 2 +- www/target_lsi.html | 2 +- www/target_mvsas.html | 2 +- www/target_old.html | 2 +- www/target_qla2x00t.html | 2 +- www/target_srp.html | 2 +- www/targets.html | 4 +- 80 files changed, 3406 insertions(+), 546 deletions(-) create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.10.30.patch create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.12.11.patch create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.13.3.patch create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.13.patch create mode 100644 qla2x00t/qla2x00-target/Makefile_in-tree-3.13 create mode 100644 scst/kernel/in-tree/Kconfig.drivers.Linux-3.13.patch create mode 100644 scst/kernel/in-tree/Makefile.dev_handlers-3.13 create mode 100644 scst/kernel/in-tree/Makefile.drivers.Linux-3.13.patch create mode 100644 scst/kernel/in-tree/Makefile.scst-3.13 create mode 100644 scst/kernel/scst_exec_req_fifo-3.13.patch create mode 100644 scst_local/in-tree/Makefile-3.13 create mode 100644 srpt/patches/kernel-3.13-pre-cflags.patch diff --git a/Makefile b/Makefile index 01742ad63..3e3637749 100644 --- a/Makefile +++ b/Makefile @@ -19,19 +19,21 @@ SHELL = /bin/bash # Define the location to the kernel src. Can be defined here or on -# the command line during the build process. If KDIR is defined, -# we will set an appropriate value for KVER by running "make -# kernelversion" in the kernel source tree. KVER can still be -# overrode by the user via the command line or by defining it in -# this Makefile. If KDIR and KVER are not defined by the user, -# the current running kernel version is used to define KVER. +# the command line during the build process. If KDIR is defined, +# we will determine an appropriate value for KVER from the kernel +# source tree. KVER can still be overridden by the user via the +# command line or by defining it in this Makefile. If KDIR and KVER +# are not defined by the user, the current running kernel version is +# used to define KVER. #export KDIR=/usr/src/linux-2.6 #export KVER=2.6.x ifdef KDIR ifndef KVER - export KVER = $(strip $(shell make -s -C $(KDIR) kernelversion)) + export KVER = $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) endif endif @@ -49,7 +51,6 @@ MVSAS_DIR=mvsas_tgt FCST_DIR=fcst ISCSI_DIR=iscsi-scst -#ISCSI_DESTDIR=../../../iscsi_scst_inst VERSION = $(shell echo -n "$$(sed -n 's/^\#define[[:blank:]]SCST_VERSION_NAME[[:blank:]]*\"\([^-]*\).*\"/\1/p' scst/include/scst_const.h)."; \ if svn info >/dev/null 2>&1; \ @@ -153,7 +154,7 @@ install: # @if [ -d $(QLA_ISP_DIR) ]; then cd $(QLA_ISP_DIR) && $(MAKE) $@; fi # @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi # @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi - @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) DESTDIR=$(ISCSI_DESTDIR) $@; fi + @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi @if [ -d $(SCST_LOCAL_DIR) ]; then cd $(SCST_LOCAL_DIR) && $(MAKE) $@; fi @@ -271,7 +272,7 @@ iscsi: cd $(ISCSI_DIR) && $(MAKE) all iscsi_install: - cd $(ISCSI_DIR) && $(MAKE) DESTDIR=$(ISCSI_DESTDIR) install + cd $(ISCSI_DIR) && $(MAKE) install iscsi_uninstall: cd $(ISCSI_DIR) && $(MAKE) uninstall @@ -388,7 +389,6 @@ scst-rpm: rpmtopdir="$$(if [ $$(id -u) = 0 ]; then echo /usr/src/packages;\ else echo $$PWD/rpmbuilddir; fi)" && \ $(MAKE) scst-dist-gzip && \ - rm -rf $${rpmtopdir} && \ for d in BUILD RPMS SOURCES SPECS SRPMS; do \ mkdir -p $${rpmtopdir}/$$d; \ done && \ diff --git a/fcst/Makefile b/fcst/Makefile index c82997771..5939c958a 100644 --- a/fcst/Makefile +++ b/fcst/Makefile @@ -26,19 +26,6 @@ # - install and uninstall must be made as root # -ifndef PREFIX - PREFIX=/usr/local -endif - -ifeq ($(KVER),) - ifeq ($(KDIR),) - KVER = $(shell uname -r) - KDIR := /lib/modules/$(KVER)/build - endif -else - KDIR := /lib/modules/$(KVER)/build -endif - export PWD := $(shell pwd) export CONFIG_FCST := m @@ -55,16 +42,39 @@ EXTRA_CFLAGS += -I$(SCST_INC_DIR) $(FCSTFLAGS$(BUILDMODE)) MODULE_NAME = fcst -INSTALL_DIR := /lib/modules/$(KVER)/extra - ifneq ($(KERNELRELEASE),) include $(SUBDIRS)/Makefile_in-tree else +######### BEGIN OUT-OF-TREE RULES ######### + +ifndef PREFIX + PREFIX=/usr/local +endif + +ifeq ($(KVER),) + ifeq ($(KDIR),) + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build + else + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + endif +else + KDIR := /lib/modules/$(KVER)/build +endif + +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra + SCST_INC_DIR := $(shell if [ -e "$$PWD/../scst" ]; \ then echo "$$PWD/../scst/include"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) SCST_DIR := $(shell if [ -e "$$PWD/../scst" ]; then echo "$$PWD/../scst/src"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) all: Modules.symvers Module.symvers $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ @@ -77,7 +87,6 @@ tgt: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ SCST_INC_DIR=$(SCST_INC_DIR) modules_install - -depmod -a $(KVER) ins: ./config @@ -102,7 +111,9 @@ endif uninstall: rm -f $(INSTALL_DIR)/$(MODULE_NAME).ko - -/sbin/depmod -a $(KVER) + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) + +########## END OUT-OF-TREE RULES ########## endif clean: diff --git a/ibmvstgt/Makefile b/ibmvstgt/Makefile index bcfa95657..daaf57f4e 100644 --- a/ibmvstgt/Makefile +++ b/ibmvstgt/Makefile @@ -7,15 +7,27 @@ SUBDIRS := $(shell pwd) ifeq ($(KVER),) ifeq ($(KDIR),) - KVER = $(shell uname -r) - KDIR ?= /lib/modules/$(KVER)/build + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build else - KVER = $$KERNELRELEASE + ifeq ($(KERNELRELEASE),) + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + else + KVER := $(KERNELRELEASE) + endif endif else - KDIR ?= /lib/modules/$(KVER)/build + KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra + # The file Modules.symvers has been renamed in the 2.6.18 kernel to # Module.symvers. Find out which name to use by looking in $(KDIR). MODULE_SYMVERS:=$(shell if [ -e $(KDIR)/Module.symvers ]; then \ @@ -26,11 +38,11 @@ all: src/$(MODULE_SYMVERS) $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src modules install: all src/ibmvstgt.ko - @eval `sed -n 's/#define UTS_RELEASE /KERNELRELEASE=/p' $(KDIR)/include/linux/version.h $(KDIR)/include/linux/utsrelease.h 2>/dev/null`; \ - for m in libsrp.ko ibmvstgt.ko; do \ - install -vD -m 644 src/$$m \ - $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/$$m; done - -/sbin/depmod -aq $(KVER) + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src modules_install + +uninstall: + rm -f $(INSTALL_DIR)/libsrp.ko $(INSTALL_DIR)/ibmvstgt.ko + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) src/Module.symvers src/Modules.symvers: $(SCST_DIR)/$(MODULE_SYMVERS) cp $< $@; diff --git a/ibmvstgt/README.sysfs b/ibmvstgt/README.sysfs index 1ffc5ad35..cd27fe19a 100644 --- a/ibmvstgt/README.sysfs +++ b/ibmvstgt/README.sysfs @@ -32,6 +32,17 @@ more information): 2:0:0:0 ram000 ram001 ram002 ram003 ram004 ram005 ram006 ram007 ram008 ram009 ram010 ram011 ram012 ram013 ram014 ram015 +Next, set the vendor ID, product ID etc. fields via sysfs. These fields +must be set to the following values to allow AIX initiators to recognize +SCST devices: +* The Vendor ID (t10_vend_id) must be set to "IBM". +* The Product ID (prod_id) must be set either to "VDASD blkdev" for SCSI disks + or "VOPTA blkdev" for a SCSI CD-ROM. +* The Product Revision Level (prod_rev_lvl) must be set to "0001". +* The Vendor Specific Information in the INQUIRY response (inq_vend_specific) + must be set to the serial number. The serial number is available in the + "usn" sysfs attribute. + After this step a LUN has to be assigned to each exported SCSI device. Some non-Linux initiator operating systems only accept LUN numbes that are multiples of 256 and require that the LUN addressing method is used. diff --git a/ibmvstgt/src/ibmvstgt.c b/ibmvstgt/src/ibmvstgt.c index 39d2eaa1a..1dd4dbc7e 100644 --- a/ibmvstgt/src/ibmvstgt.c +++ b/ibmvstgt/src/ibmvstgt.c @@ -1069,28 +1069,6 @@ static void handle_crq(struct work_struct *work) } } -static void ibmvstgt_get_product_id(const struct scst_tgt_dev *tgt_dev, - char *buf, const int size) -{ - WARN_ON(size != 16); - - /* - * AIX uses hardcoded device names. The AIX SCSI initiator even won't - * work unless we use the names VDASD and VOPTA. - */ - switch (tgt_dev->dev->type) { - case TYPE_DISK: - memcpy(buf, "VDASD blkdev ", 16); - break; - case TYPE_ROM: - memcpy(buf, "VOPTA blkdev ", 16); - break; - default: - snprintf(buf, size, "(devtype %d) ", tgt_dev->dev->type); - break; - } -} - /* * Extract target, bus and LUN information from a 64-bit LUN in CPU-order. */ @@ -1236,12 +1214,8 @@ static struct scst_tgt_template ibmvstgt_template = { #else .sg_tablesize = SCSI_MAX_SG_SEGMENTS, #endif - .vendor = "IBM ", - .revision = "0001", .fake_aca = true, - .get_product_id = ibmvstgt_get_product_id, .get_serial = ibmvstgt_get_serial, - .get_vend_specific = ibmvstgt_get_serial, #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = DEFAULT_IBMVSTGT_TRACE_FLAGS, diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index ed288354c..aa2053136 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -15,9 +15,9 @@ SUBDIRS := $(shell pwd) SCST_INC_DIR := $(shell if [ -e "$$PWD/../scst" ]; \ then echo "$$PWD/../scst/include"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) SCST_DIR := $(shell if [ -e "$$PWD/../scst" ]; then echo "$$PWD/../scst/src"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) SBINDIR := $(PREFIX)/sbin INITDIR := /etc/init.d RCDIR := /etc/rc.d @@ -28,15 +28,27 @@ ISERTMOD := $(KMOD)/isert-scst ifeq ($(KVER),) ifeq ($(KDIR),) - KVER = $(shell uname -r) - KDIR ?= /lib/modules/$(KVER)/build + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build else - KVER = $$KERNELRELEASE + ifeq ($(KERNELRELEASE),) + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + else + KVER := $(KERNELRELEASE) + endif endif else - KDIR ?= /lib/modules/$(KVER)/build + KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra + all: include/iscsi_scst_itf_ver.h progs mods ISER_SYMVERS:=$(KMOD)/Module.symvers @@ -99,12 +111,20 @@ install: all @install -vD -m 644 doc/manpages/iscsi-scstd.8 $(DESTDIR)$(MANDIR)/man8/iscsi-scstd.8 @install -vD -m 755 usr/iscsi-scst-adm $(DESTDIR)$(SBINDIR)/iscsi-scst-adm @install -vD -m 644 doc/manpages/iscsi-scst-adm.8 $(DESTDIR)$(MANDIR)/man8/iscsi-scst-adm.8 - @eval `sed -n 's/#define UTS_RELEASE /KERNELRELEASE=/p' $(KDIR)/include/linux/version.h $(KDIR)/include/linux/utsrelease.h 2>/dev/null`; \ - install -vD -m 644 kernel/iscsi-scst.ko \ - $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/iscsi-scst.ko - install -vD -m 644 kernel/isert-scst/isert-scst.ko \ - $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/isert-scst.ko - -/sbin/depmod -aq $(KVER) + $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(KMOD) \ + modules_install + $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) \ + modules_install + +uninstall: + rm -f $(DESTDIR)$(SBINDIR)/iscsi-scstd \ + $(DESTDIR)$(MANDIR)/man5/iscsi-scstd.conf.5 \ + $(DESTDIR)$(MANDIR)/man8/iscsi-scstd.8 \ + $(DESTDIR)$(SBINDIR)/iscsi-scst-adm \ + $(DESTDIR)$(MANDIR)/man8/iscsi-scst-adm.8 \ + $(INSTALL_DIR)/iscsi-scst.ko \ + $(INSTALL_DIR)/isert-scst.ko + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) ifneq ($(SCST_MOD_VERS),) diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 33e73f064..fb8d153ec 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -1584,6 +1584,8 @@ static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn, buff_offs = offset; idx = (offset + sg[0].offset) >> PAGE_SHIFT; + if (offset + sg[0].offset >= PAGE_SIZE) + offset += sg[0].offset; offset &= ~PAGE_MASK; conn->read_msg.msg_iov = conn->read_iov; diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 09e3a8cc2..04d6875a8 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -1403,6 +1403,8 @@ retry: if (sg != write_cmnd->rsp_sg) { offset = conn->write_offset + sg[0].offset; idx = offset >> PAGE_SHIFT; + if (offset + sg[0].offset >= PAGE_SIZE) + offset += sg[0].offset; offset &= ~PAGE_MASK; length = min(size, (int)PAGE_SIZE - offset); TRACE_WRITE("write_offset %d, sg_size %d, idx %d, offset %d, " diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.10.30.patch b/iscsi-scst/kernel/patches/put_page_callback-3.10.30.patch new file mode 100644 index 000000000..6470f52fc --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.10.30.patch @@ -0,0 +1,420 @@ +diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c +index 4222aff..0d2ac7d 100644 +--- a/drivers/block/drbd/drbd_receiver.c ++++ b/drivers/block/drbd/drbd_receiver.c +@@ -130,7 +130,7 @@ static int page_chain_free(struct page *page) + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; +diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c +index 9e56eb4..74fe728 100644 +--- a/drivers/net/macvtap.c ++++ b/drivers/net/macvtap.c +@@ -527,7 +527,7 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, + int j; + + for (j = 0; j < num_pages; j++) +- put_page(page[i + j]); ++ net_put_page(page[i + j]); + return -EFAULT; + } + truesize = size * PAGE_SIZE; +diff --git a/drivers/net/tun.c b/drivers/net/tun.c +index 5824971..83e0eaa 100644 +--- a/drivers/net/tun.c ++++ b/drivers/net/tun.c +@@ -1013,7 +1013,7 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, + int j; + + for (j = 0; j < num_pages; j++) +- put_page(page[i + j]); ++ net_put_page(page[i + j]); + return -EFAULT; + } + truesize = size * PAGE_SIZE; +diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c +index 55a62ca..dcb9fdf 100644 +--- a/drivers/net/vmxnet3/vmxnet3_drv.c ++++ b/drivers/net/vmxnet3/vmxnet3_drv.c +@@ -1360,7 +1360,7 @@ vmxnet3_rq_cleanup(struct vmxnet3_rx_queue *rq, + rq->buf_info[ring_idx][i].page) { + pci_unmap_page(adapter->pdev, rxd->addr, + rxd->len, PCI_DMA_FROMDEVICE); +- put_page(rq->buf_info[ring_idx][i].page); ++ net_put_page(rq->buf_info[ring_idx][i].page); + rq->buf_info[ring_idx][i].page = NULL; + } + } +diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c +index 36efb41..019681c 100644 +--- a/drivers/net/xen-netback/netback.c ++++ b/drivers/net/xen-netback/netback.c +@@ -1292,7 +1292,7 @@ static void xen_netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb) + skb->truesize += txp->size; + + /* Take an extra reference to offset xen_netbk_idx_release */ +- get_page(netbk->mmap_pages[pending_idx]); ++ net_get_page(netbk->mmap_pages[pending_idx]); + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY); + } + } +@@ -1774,7 +1774,7 @@ static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx, + } while (!pending_tx_is_head(netbk, peek)); + + netbk->mmap_pages[pending_idx]->mapping = 0; +- put_page(netbk->mmap_pages[pending_idx]); ++ net_put_page(netbk->mmap_pages[pending_idx]); + netbk->mmap_pages[pending_idx] = NULL; + } + +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 10a9a17..1a01f46 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -177,6 +177,17 @@ struct page { + #ifdef LAST_NID_NOT_IN_PAGE_FLAGS + int _last_nid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops +diff --git a/include/linux/net.h b/include/linux/net.h +index 65545ac..288d185 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -278,6 +279,45 @@ extern int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + extern int kernel_sock_shutdown(struct socket *sock, + enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index ded45ec..b5c6dda 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2075,7 +2075,7 @@ static inline struct page *skb_frag_page(const skb_frag_t *frag) + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -2098,7 +2098,7 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f) + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** +diff --git a/net/Kconfig b/net/Kconfig +index 2ddc904..ec9bfbd 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -74,6 +74,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" +diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c +index 815a224..f53c802 100644 +--- a/net/ceph/pagevec.c ++++ b/net/ceph/pagevec.c +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty) + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + kfree(pages); + } +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 20ee14d..e3734cf 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -427,7 +427,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -473,7 +473,7 @@ static void skb_clone_fraglist(struct sk_buff *skb) + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -793,7 +793,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) + if (!page) { + while (head) { + struct page *next = (struct page *)head->private; +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1629,7 +1629,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1682,7 +1682,7 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2681,7 +2681,7 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); +diff --git a/net/core/sock.c b/net/core/sock.c +index 50a345e..f9fba8e 100644 +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -1804,7 +1804,7 @@ bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag) + } + if (pfrag->offset < pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + /* We restrict high order allocations to users that can afford to wait */ +@@ -2505,7 +2505,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index 089cb9f..bc38b0e 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -52,6 +52,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o +diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +index 6ca5873..014503d2 100644 +--- a/net/ipv4/ip_output.c ++++ b/net/ipv4/ip_output.c +@@ -1006,7 +1006,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1227,7 +1227,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 1a2e249..b512ddc 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -897,7 +897,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1193,7 +1193,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } +diff --git a/net/ipv4/tcp_zero_copy.c b/net/ipv4/tcp_zero_copy.c +new file mode 100644 +index 0000000..99d41fa +--- /dev/null ++++ b/net/ipv4/tcp_zero_copy.c +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index b98b8e0..2df0fda 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -1432,7 +1432,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +-- +1.8.4.5 + diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.12.11.patch b/iscsi-scst/kernel/patches/put_page_callback-3.12.11.patch new file mode 100644 index 000000000..41658111f --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.12.11.patch @@ -0,0 +1,407 @@ +diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c +index cc29cd3..ba34c70 100644 +--- a/drivers/block/drbd/drbd_receiver.c ++++ b/drivers/block/drbd/drbd_receiver.c +@@ -130,7 +130,7 @@ static int page_chain_free(struct page *page) + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; +diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c +index 7e2788c..70d390c 100644 +--- a/drivers/net/vmxnet3/vmxnet3_drv.c ++++ b/drivers/net/vmxnet3/vmxnet3_drv.c +@@ -1369,7 +1369,7 @@ vmxnet3_rq_cleanup(struct vmxnet3_rx_queue *rq, + rq->buf_info[ring_idx][i].page) { + dma_unmap_page(&adapter->pdev->dev, rxd->addr, + rxd->len, PCI_DMA_FROMDEVICE); +- put_page(rq->buf_info[ring_idx][i].page); ++ net_put_page(rq->buf_info[ring_idx][i].page); + rq->buf_info[ring_idx][i].page = NULL; + } + } +diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c +index 6255850..12a6f14 100644 +--- a/drivers/net/xen-netback/netback.c ++++ b/drivers/net/xen-netback/netback.c +@@ -1055,7 +1055,7 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb) + skb->truesize += txp->size; + + /* Take an extra reference to offset xenvif_idx_release */ +- get_page(vif->mmap_pages[pending_idx]); ++ net_get_page(vif->mmap_pages[pending_idx]); + xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY); + } + } +@@ -1525,7 +1525,7 @@ static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx, + + } while (!pending_tx_is_head(vif, peek)); + +- put_page(vif->mmap_pages[pending_idx]); ++ net_put_page(vif->mmap_pages[pending_idx]); + vif->mmap_pages[pending_idx] = NULL; + } + +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 8e082f1..15f10c8 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -177,6 +177,17 @@ struct page { + #ifdef LAST_NID_NOT_IN_PAGE_FLAGS + int _last_nid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops +diff --git a/include/linux/net.h b/include/linux/net.h +index 41103f8..54c2bffb 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -278,6 +279,45 @@ extern int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + extern int kernel_sock_shutdown(struct socket *sock, + enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index efa1649..5efff79 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -1975,7 +1975,7 @@ static inline struct page *skb_frag_page(const skb_frag_t *frag) + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -1998,7 +1998,7 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f) + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** +diff --git a/net/Kconfig b/net/Kconfig +index b50dacc..88ed9df 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -75,6 +75,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" +diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c +index 815a224..f53c802 100644 +--- a/net/ceph/pagevec.c ++++ b/net/ceph/pagevec.c +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty) + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + kfree(pages); + } +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 2c7baa8..3196557 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -422,7 +422,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -468,7 +468,7 @@ static void skb_clone_fraglist(struct sk_buff *skb) + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -793,7 +793,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1618,7 +1618,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1671,7 +1671,7 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2675,7 +2675,7 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); +diff --git a/net/core/sock.c b/net/core/sock.c +index 5cec994..c756279 100644 +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -1847,7 +1847,7 @@ bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag) + } + if (pfrag->offset < pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + /* We restrict high order allocations to users that can afford to wait */ +@@ -2599,7 +2599,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index 4b81e91..b88113f 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o +diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +index 3982eab..d37f078 100644 +--- a/net/ipv4/ip_output.c ++++ b/net/ipv4/ip_output.c +@@ -1003,7 +1003,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1224,7 +1224,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index be5246e..a349ddd 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -896,7 +896,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1192,7 +1192,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } +diff --git a/net/ipv4/tcp_zero_copy.c b/net/ipv4/tcp_zero_copy.c +new file mode 100644 +index 0000000..430147e +--- /dev/null ++++ b/net/ipv4/tcp_zero_copy.c +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index b6fa35e..b6f3389 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -1413,7 +1413,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c +index ae2e5c1..2ad9e87 100644 +--- a/net/netfilter/nfnetlink_queue_core.c ++++ b/net/netfilter/nfnetlink_queue_core.c +@@ -258,7 +258,7 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } +-- +1.8.4.5 + diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.13.3.patch b/iscsi-scst/kernel/patches/put_page_callback-3.13.3.patch new file mode 100644 index 000000000..19b900310 --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.13.3.patch @@ -0,0 +1,403 @@ +=== modified file 'drivers/block/drbd/drbd_receiver.c' +--- old/drivers/block/drbd/drbd_receiver.c 2014-02-20 05:26:12 +0000 ++++ new/drivers/block/drbd/drbd_receiver.c 2014-02-20 05:35:42 +0000 +@@ -130,7 +130,7 @@ static int page_chain_free(struct page * + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; + +=== modified file 'drivers/net/vmxnet3/vmxnet3_drv.c' +--- old/drivers/net/vmxnet3/vmxnet3_drv.c 2014-02-20 05:26:12 +0000 ++++ new/drivers/net/vmxnet3/vmxnet3_drv.c 2014-02-20 05:35:42 +0000 +@@ -1369,7 +1369,7 @@ vmxnet3_rq_cleanup(struct vmxnet3_rx_que + rq->buf_info[ring_idx][i].page) { + dma_unmap_page(&adapter->pdev->dev, rxd->addr, + rxd->len, PCI_DMA_FROMDEVICE); +- put_page(rq->buf_info[ring_idx][i].page); ++ net_put_page(rq->buf_info[ring_idx][i].page); + rq->buf_info[ring_idx][i].page = NULL; + } + } + +=== modified file 'drivers/net/xen-netback/netback.c' +--- old/drivers/net/xen-netback/netback.c 2014-02-20 05:26:12 +0000 ++++ new/drivers/net/xen-netback/netback.c 2014-02-20 05:35:42 +0000 +@@ -1080,7 +1080,7 @@ static void xenvif_fill_frags(struct xen + skb->truesize += txp->size; + + /* Take an extra reference to offset xenvif_idx_release */ +- get_page(vif->mmap_pages[pending_idx]); ++ net_get_page(vif->mmap_pages[pending_idx]); + xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY); + } + } +@@ -1760,7 +1760,7 @@ static void xenvif_idx_release(struct xe + + } while (!pending_tx_is_head(vif, peek)); + +- put_page(vif->mmap_pages[pending_idx]); ++ net_put_page(vif->mmap_pages[pending_idx]); + vif->mmap_pages[pending_idx] = NULL; + } + + +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2014-02-20 05:26:12 +0000 ++++ new/include/linux/mm_types.h 2014-02-20 05:35:42 +0000 +@@ -195,6 +195,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2014-02-20 05:26:12 +0000 ++++ new/include/linux/net.h 2014-02-20 05:35:42 +0000 +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -295,6 +296,45 @@ int kernel_sendpage(struct socket *sock, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2014-02-20 05:26:12 +0000 ++++ new/include/linux/skbuff.h 2014-02-20 05:35:42 +0000 +@@ -1974,7 +1974,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -1997,7 +1997,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2014-02-20 05:26:12 +0000 ++++ new/net/Kconfig 2014-02-20 05:35:42 +0000 +@@ -75,6 +75,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/ceph/pagevec.c' +--- old/net/ceph/pagevec.c 2014-02-20 05:26:12 +0000 ++++ new/net/ceph/pagevec.c 2014-02-20 05:35:42 +0000 +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page ** + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + kfree(pages); + } + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2014-02-20 05:26:12 +0000 ++++ new/net/core/skbuff.c 2014-02-20 05:35:42 +0000 +@@ -422,7 +422,7 @@ struct sk_buff *__netdev_alloc_skb(struc + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -480,7 +480,7 @@ static void skb_clone_fraglist(struct sk + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -805,7 +805,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1648,7 +1648,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1701,7 +1701,7 @@ static bool spd_fill_page(struct splice_ + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2716,7 +2716,7 @@ int skb_append_datato_frags(struct sock + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); + +=== modified file 'net/core/sock.c' +--- old/net/core/sock.c 2014-02-20 05:26:12 +0000 ++++ new/net/core/sock.c 2014-02-20 05:35:42 +0000 +@@ -1862,7 +1862,7 @@ bool skb_page_frag_refill(unsigned int s + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + /* We restrict high order allocations to users that can afford to wait */ +@@ -2624,7 +2624,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2014-02-20 05:26:12 +0000 ++++ new/net/ipv4/Makefile 2014-02-20 05:35:42 +0000 +@@ -53,6 +53,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah. + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2014-02-20 05:26:12 +0000 ++++ new/net/ipv4/ip_output.c 2014-02-20 05:35:42 +0000 +@@ -1005,7 +1005,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1231,7 +1231,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2014-02-20 05:26:12 +0000 ++++ new/net/ipv4/tcp.c 2014-02-20 05:35:42 +0000 +@@ -898,7 +898,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1194,7 +1194,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2014-02-20 05:35:42 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + +=== modified file 'net/ipv6/ip6_output.c' +--- old/net/ipv6/ip6_output.c 2014-02-20 05:26:12 +0000 ++++ new/net/ipv6/ip6_output.c 2014-02-20 05:35:42 +0000 +@@ -1431,7 +1431,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, + +=== modified file 'net/netfilter/nfnetlink_queue_core.c' +--- old/net/netfilter/nfnetlink_queue_core.c 2014-02-20 05:26:12 +0000 ++++ new/net/netfilter/nfnetlink_queue_core.c 2014-02-20 05:35:42 +0000 +@@ -258,7 +258,7 @@ nfqnl_zcopy(struct sk_buff *to, const st + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } + diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.13.patch b/iscsi-scst/kernel/patches/put_page_callback-3.13.patch new file mode 100644 index 000000000..67aece596 --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.13.patch @@ -0,0 +1,419 @@ +=== modified file 'drivers/block/drbd/drbd_receiver.c' +--- old/drivers/block/drbd/drbd_receiver.c 2014-01-30 00:25:53 +0000 ++++ new/drivers/block/drbd/drbd_receiver.c 2014-01-30 01:02:34 +0000 +@@ -130,7 +130,7 @@ static int page_chain_free(struct page * + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; + +=== modified file 'drivers/net/vmxnet3/vmxnet3_drv.c' +--- old/drivers/net/vmxnet3/vmxnet3_drv.c 2014-01-30 00:25:53 +0000 ++++ new/drivers/net/vmxnet3/vmxnet3_drv.c 2014-01-30 01:02:34 +0000 +@@ -1369,7 +1369,7 @@ vmxnet3_rq_cleanup(struct vmxnet3_rx_que + rq->buf_info[ring_idx][i].page) { + dma_unmap_page(&adapter->pdev->dev, rxd->addr, + rxd->len, PCI_DMA_FROMDEVICE); +- put_page(rq->buf_info[ring_idx][i].page); ++ net_put_page(rq->buf_info[ring_idx][i].page); + rq->buf_info[ring_idx][i].page = NULL; + } + } + +=== modified file 'drivers/net/xen-netback/netback.c' +--- old/drivers/net/xen-netback/netback.c 2014-01-30 00:25:53 +0000 ++++ new/drivers/net/xen-netback/netback.c 2014-01-30 01:02:34 +0000 +@@ -1080,7 +1080,7 @@ static void xenvif_fill_frags(struct xen + skb->truesize += txp->size; + + /* Take an extra reference to offset xenvif_idx_release */ +- get_page(vif->mmap_pages[pending_idx]); ++ net_get_page(vif->mmap_pages[pending_idx]); + xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY); + } + } +@@ -1760,7 +1760,7 @@ static void xenvif_idx_release(struct xe + + } while (!pending_tx_is_head(vif, peek)); + +- put_page(vif->mmap_pages[pending_idx]); ++ net_put_page(vif->mmap_pages[pending_idx]); + vif->mmap_pages[pending_idx] = NULL; + } + + +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2014-01-30 00:25:53 +0000 ++++ new/include/linux/mm_types.h 2014-01-30 01:02:34 +0000 +@@ -195,6 +195,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2014-01-30 00:25:53 +0000 ++++ new/include/linux/net.h 2014-01-30 01:02:34 +0000 +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -295,6 +296,45 @@ int kernel_sendpage(struct socket *sock, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2014-01-30 00:25:53 +0000 ++++ new/include/linux/skbuff.h 2014-01-30 01:02:34 +0000 +@@ -1974,7 +1974,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -1997,7 +1997,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2014-01-30 00:25:53 +0000 ++++ new/net/Kconfig 2014-01-30 01:02:34 +0000 +@@ -75,6 +75,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/ceph/pagevec.c' +--- old/net/ceph/pagevec.c 2014-01-30 00:25:53 +0000 ++++ new/net/ceph/pagevec.c 2014-01-30 01:02:34 +0000 +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page ** + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + kfree(pages); + } + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2014-01-30 00:25:53 +0000 ++++ new/net/core/skbuff.c 2014-01-30 01:02:34 +0000 +@@ -77,13 +77,13 @@ static struct kmem_cache *skbuff_fclone_ + static void sock_pipe_buf_release(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { +- put_page(buf->page); ++ net_put_page(buf->page); + } + + static void sock_pipe_buf_get(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { +- get_page(buf->page); ++ net_get_page(buf->page); + } + + static int sock_pipe_buf_steal(struct pipe_inode_info *pipe, +@@ -452,7 +452,7 @@ struct sk_buff *__netdev_alloc_skb(struc + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -510,7 +510,7 @@ static void skb_clone_fraglist(struct sk + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -835,7 +835,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1678,7 +1678,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1731,7 +1731,7 @@ static bool spd_fill_page(struct splice_ + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2746,7 +2746,7 @@ int skb_append_datato_frags(struct sock + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); + +=== modified file 'net/core/sock.c' +--- old/net/core/sock.c 2014-01-30 00:25:53 +0000 ++++ new/net/core/sock.c 2014-01-30 01:02:34 +0000 +@@ -1862,7 +1862,7 @@ bool skb_page_frag_refill(unsigned int s + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + /* We restrict high order allocations to users that can afford to wait */ +@@ -2624,7 +2624,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2014-01-30 00:25:53 +0000 ++++ new/net/ipv4/Makefile 2014-01-30 01:02:34 +0000 +@@ -53,6 +53,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah. + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2014-01-30 00:25:53 +0000 ++++ new/net/ipv4/ip_output.c 2014-01-30 01:02:34 +0000 +@@ -1005,7 +1005,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1231,7 +1231,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2014-01-30 00:25:53 +0000 ++++ new/net/ipv4/tcp.c 2014-01-30 01:02:34 +0000 +@@ -898,7 +898,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1194,7 +1194,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2014-01-30 01:02:34 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + +=== modified file 'net/ipv6/ip6_output.c' +--- old/net/ipv6/ip6_output.c 2014-01-30 00:25:53 +0000 ++++ new/net/ipv6/ip6_output.c 2014-01-30 01:02:34 +0000 +@@ -1431,7 +1431,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, + +=== modified file 'net/netfilter/nfnetlink_queue_core.c' +--- old/net/netfilter/nfnetlink_queue_core.c 2014-01-30 00:25:53 +0000 ++++ new/net/netfilter/nfnetlink_queue_core.c 2014-01-30 01:02:34 +0000 +@@ -258,7 +258,7 @@ nfqnl_zcopy(struct sk_buff *to, const st + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } + diff --git a/mpt/Makefile b/mpt/Makefile index 13fc36172..957c39a1e 100644 --- a/mpt/Makefile +++ b/mpt/Makefile @@ -38,18 +38,33 @@ EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG ifeq ($(KVER),) ifeq ($(KDIR),) - KDIR := /lib/modules/$(shell uname -r)/build + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build + else + ifeq ($(KERNELRELEASE),) + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + else + KVER := $(KERNELRELEASE) + endif endif else KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra LSI_INC_DIR := $(KDIR)/drivers/message/fusion EXTRA_CFLAGS += -I$(LSI_INC_DIR) ifneq ($(PATCHLEVEL),) obj-m += mpt_scst.o else +######### BEGIN OUT-OF-TREE RULES ######### all: Modules.symvers Module.symvers $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m @@ -60,7 +75,6 @@ tgt: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ modules_install - -/sbin/depmod -a SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) ifneq ($(SCST_MOD_VERS),) @@ -81,7 +95,9 @@ endif uninstall: rm -f $(INSTALL_DIR)/mpt_scst.ko - -/sbin/depmod -a + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) + +########## END OUT-OF-TREE RULES ########## endif clean: diff --git a/mvsas_tgt/Makefile b/mvsas_tgt/Makefile index 35b8a878a..5b4db8d9c 100644 --- a/mvsas_tgt/Makefile +++ b/mvsas_tgt/Makefile @@ -31,13 +31,25 @@ endif ifeq ($(KVER),) ifeq ($(KDIR),) - KVER = $(shell uname -r) + KVER := $(shell uname -r) KDIR := /lib/modules/$(KVER)/build + else + ifeq ($(KERNELRELEASE),) + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + else + KVER := $(KERNELRELEASE) + endif endif else KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + export PWD := $(shell pwd) export LIBSAS := m @@ -49,11 +61,10 @@ SCST_DIR := $(shell pwd)/../scst/src EXTRA_CFLAGS += -I$(SCST_INC_DIR) EXTRA_CFLAGS += -DSUPPORT_TARGET -MODULE_NAME = mvsas_tgt EXTRA_CFLAGS += -DMV_DEBUG -INSTALL_DIR := /lib/modules/$(shell uname -r)/extra +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra #EXTRA_CFLAGS += -DCONFIG_SCST_TRACING #EXTRA_CFLAGS += -DDEBUG_WORK_IN_THREAD @@ -68,6 +79,8 @@ mvsas-y := mv_init.o \ mv_94xx.o \ mv_spi.o else +######### BEGIN OUT-OF-TREE RULES ######### + all: Modules.symvers Module.symvers $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m @@ -77,12 +90,11 @@ tgt: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ modules_install - -depmod -a $(KVER) ins: ./config insmod mvsas.ko - + SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) ifneq ($(SCST_MOD_VERS),) Modules.symvers: $(SCST_DIR)/Modules.symvers @@ -101,8 +113,10 @@ else endif uninstall: - rm -f $(INSTALL_DIR)/$(MODULE_NAME).ko - -/sbin/depmod -a $(KVER) + rm -f $(INSTALL_DIR)/mvsas.ko + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) + +########## END OUT-OF-TREE RULES ########## endif clean: diff --git a/mvsas_tgt/README b/mvsas_tgt/README index ab25f400e..314fedde4 100644 --- a/mvsas_tgt/README +++ b/mvsas_tgt/README @@ -100,7 +100,7 @@ note: 2. In example "add 2:0:0:0 1" the '1' is the LUN in the target, LUN 0 must exist with a target. 3. When one device is added to target disk group, target driver will notify - initator the changing of the phy mode and arriving of the disk, but some + initiator the changing of the phy mode and arriving of the disk, but some initiator driver don't support phy mode changing well, so 'rmmod' and 'modprobe'/'insmod' the initiator driver is neccessary. diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index b1486e905..1b8d709e1 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,15 +3,16 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.12.9 \ +3.13.5 \ +3.12.13-nc \ 3.11.10-nc \ -3.10.28-nc \ +3.10.32-nc \ 3.9.11-nc \ 3.8.13-nc \ 3.7.10-nc \ 3.6.11-nc \ 3.5.7-nc \ -3.4.77-nc \ +3.4.81-nc \ 3.3.8-nc \ 3.2.53-nc \ 3.1.10-nc \ diff --git a/qla2x00t/Makefile b/qla2x00t/Makefile index 24e811095..ac628ce9b 100644 --- a/qla2x00t/Makefile +++ b/qla2x00t/Makefile @@ -16,6 +16,7 @@ extraclean: clean .PHONY: clean extraclean else +######### BEGIN OUT-OF-TREE RULES ######### SHELL=/bin/bash @@ -32,12 +33,23 @@ endif ifeq ($(KVER),) ifeq ($(KDIR),) - KDIR := /lib/modules/$(shell uname -r)/build + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build + else + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) endif else KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra + ifneq ($(PATCHLEVEL),) obj-m := qla2xxx_scst.o qla2xxx_scst-objs := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \ @@ -50,11 +62,12 @@ all: install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ modules_install - -/sbin/depmod -aq $(KVER) uninstall: - rm -f $(INSTALL_DIR)/qla2xxxt.ko - -/sbin/depmod -a $(KVER) + rm -f $(INSTALL_DIR)/qla2xxx_scst.ko + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) + +########## END OUT-OF-TREE RULES ########## endif clean: diff --git a/qla2x00t/qla2x00-target/ChangeLog b/qla2x00t/qla2x00-target/ChangeLog index 587f02587..90bc489f9 100644 --- a/qla2x00t/qla2x00-target/ChangeLog +++ b/qla2x00t/qla2x00-target/ChangeLog @@ -159,7 +159,7 @@ Summary of changes between versions 0.9.3.4 and 0.9.3.5 ------------------------------------------------------- Patch vs: qla2xxx v8.01.03-k (in kernels 2.6.15.x) - - Reset chip when switching from initiator to initator/target and back + - Reset chip when switching from initiator to initiator/target and back implemented - Use 2K loop_id's for 23xx chips and thus change how sessions are diff --git a/qla2x00t/qla2x00-target/Makefile b/qla2x00t/qla2x00-target/Makefile index c2ec8a50d..7dbbf0117 100644 --- a/qla2x00t/qla2x00-target/Makefile +++ b/qla2x00t/qla2x00-target/Makefile @@ -27,16 +27,10 @@ # - install and uninstall must be made as root # -ifndef PREFIX - PREFIX=/usr/local -endif - SHELL=/bin/bash EXTRA_CFLAGS += -I$(SCST_INC_DIR) -INSTALL_DIR := /lib/modules/$(shell uname -r)/extra - EXTRA_CFLAGS += -W -Wno-unused-parameter -Wno-missing-field-initializers EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS @@ -47,13 +41,25 @@ EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions ifeq ($(KVER),) ifeq ($(KDIR),) - KVER = $(shell uname -r) + KVER := $(shell uname -r) KDIR := /lib/modules/$(KVER)/build + else + ifeq ($(KERNELRELEASE),) + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + else + KVER := $(KERNELRELEASE) + endif endif else KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + ifeq ($(BUILD_2X_MODULE),) QLA2XXX_INC_DIR := $(KDIR)/drivers/scsi/qla2xxx else @@ -69,13 +75,20 @@ ifneq ($(PATCHLEVEL),) obj-m := qla2x00tgt.o qla2x00tgt-objs := qla2x00t.o else +######### BEGIN OUT-OF-TREE RULES ######### + +ifndef PREFIX + PREFIX=/usr/local +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \ then echo "$$PWD/../../scst/include"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) SCST_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \ then echo "$$PWD/../../scst/src"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) ifneq ($(BUILD_2X_MODULE),) # We need to make qla2xxx_scst before Module.symvers @@ -93,14 +106,13 @@ ifneq ($(BUILD_2X_MODULE),) endif $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ SCST_INC_DIR=$(SCST_INC_DIR) modules_install - -/sbin/depmod -a $(KVER) uninstall: ifneq ($(BUILD_2X_MODULE),) $(MAKE) SUBDIRS=$(QLA2XXX_DIR) -C $(QLA2XXX_DIR) $@ endif rm -f $(INSTALL_DIR)/qla2[23x]00tgt.ko - -/sbin/depmod -a $(KVER) + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) ifneq ($(BUILD_2X_MODULE),) qla2xxx_scst: @@ -131,6 +143,7 @@ else .PHONY: Module.symvers endif +########## END OUT-OF-TREE RULES ########## endif clean: diff --git a/qla2x00t/qla2x00-target/Makefile_in-tree-3.13 b/qla2x00t/qla2x00-target/Makefile_in-tree-3.13 new file mode 100644 index 000000000..9657aee84 --- /dev/null +++ b/qla2x00t/qla2x00-target/Makefile_in-tree-3.13 @@ -0,0 +1,5 @@ +ccflags-y += -Idrivers/scsi/qla2xxx + +qla2x00tgt-y := qla2x00t.o + +obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o diff --git a/qla2x00t/qla_inline.h b/qla2x00t/qla_inline.h index 19820a804..9b5e618ef 100644 --- a/qla2x00t/qla_inline.h +++ b/qla2x00t/qla_inline.h @@ -36,9 +36,19 @@ qla2x00_poll(scsi_qla_host_t *ha) { unsigned long flags; +#ifdef CONFIG_PREEMPT_RT_FULL + local_irq_save_nort(flags); +#else local_irq_save(flags); +#endif + ha->isp_ops->intr_handler(0, ha); + +#ifdef CONFIG_PREEMPT_RT_FULL + local_irq_restore_nort(flags); +#else local_irq_restore(flags); +#endif } static __inline__ scsi_qla_host_t * diff --git a/qla2x00t/qla_os.c b/qla2x00t/qla_os.c index c37bc7b46..b2b105615 100644 --- a/qla2x00t/qla_os.c +++ b/qla2x00t/qla_os.c @@ -112,8 +112,8 @@ static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha); int ql2xfdmienable=1; module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xfdmienable, - "Enables FDMI registratons " - "Default is 0 - no FDMI. 1 - perfom FDMI."); + "Enables FDMI registrations " + "Default is 1 - perform FDMI. 0 - no FDMI."); #define MAX_Q_DEPTH 32 static int ql2xmaxqdepth = MAX_Q_DEPTH; diff --git a/qla_isp/README.scst b/qla_isp/README.scst index 9109f8ed2..f5de8f662 100644 --- a/qla_isp/README.scst +++ b/qla_isp/README.scst @@ -122,7 +122,7 @@ activating channels/LUNs in /proc/scsi_tgt/qla_isp/N . The driver can also work as both a target and an initiator simultaneously, but this will probably only function for P2P connections. To make the driver work -as a target/initator on one port with a FC switch, you can use N_PORT ID +as a target/initiator on one port with a FC switch, you can use N_PORT ID virtualization, as seen below. N_PORT ID VIRTUALIZATION diff --git a/scripts/blockdev-perftest b/scripts/blockdev-perftest index 4b1db9a1b..db240ef36 100755 --- a/scripts/blockdev-perftest +++ b/scripts/blockdev-perftest @@ -76,11 +76,11 @@ set_frequency_scaling() { if [ ! -e $syscpu/cpufreq ]; then return fi - local governor=$(<$syscpu/cpu0/cpufreq/scaling_governor) - local cpuinfo_min_freq=$(<$syscpu/cpu0/cpufreq/cpuinfo_min_freq) - local cpuinfo_max_freq=$(<$syscpu/cpu0/cpufreq/cpuinfo_max_freq) - local scaling_min_freq=$(<$syscpu/cpu0/cpufreq/scaling_min_freq) - local scaling_max_freq=$(<$syscpu/cpu0/cpufreq/scaling_max_freq) + local governor=$(cat $syscpu/cpu0/cpufreq/scaling_governor) + local cpuinfo_min_freq=$(cat $syscpu/cpu0/cpufreq/cpuinfo_min_freq) + local cpuinfo_max_freq=$(cat $syscpu/cpu0/cpufreq/cpuinfo_max_freq) + local scaling_min_freq=$(cat $syscpu/cpu0/cpufreq/scaling_min_freq) + local scaling_max_freq=$(cat $syscpu/cpu0/cpufreq/scaling_max_freq) if [ -w $syscpu/cpu0/cpufreq/scaling_governor ]; then for d in $syscpu/cpu*/cpufreq do @@ -210,7 +210,7 @@ if [ "${perform_write_test}" = "true" -a ! -w "${device}" ]; then fi if [ "${perform_read_test}" = "true" -a \ - "$(($(" "3.7.10" ]; - then echo iscsi-scst/kernel/patches/*-3.7.10.patch; - else echo iscsi-scst/kernel/patches/*-${kver}.patch; fi) + $(if [ ${kver} = 3.7 ] && [ "${1#3.7.}" -ge 10 ]; then + echo iscsi-scst/kernel/patches/*-3.7.10.patch; + elif [ ${kver} = 3.10 ] && [ "${1#3.10.}" -ge 30 ]; then + echo iscsi-scst/kernel/patches/*-3.10.30.patch; + elif [ ${kver} = 3.12 ] && [ "${1#3.12.}" -ge 11 ]; then + echo iscsi-scst/kernel/patches/*-3.12.11.patch; + elif [ ${kver} = 3.13 ] && [ "${1#3.13.}" -ge 3 ]; then + echo iscsi-scst/kernel/patches/*-3.13.3.patch; + else + echo iscsi-scst/kernel/patches/*-${kver}.patch; + fi) do # Exclude the put_page_callback patch when command-line option -u has been # specified since the current approach is not considered acceptable for diff --git a/scripts/kernel-functions b/scripts/kernel-functions index 7bfdf7133..0bc0584a9 100644 --- a/scripts/kernel-functions +++ b/scripts/kernel-functions @@ -172,6 +172,45 @@ Get rid of sparse errors on sk_buff.protocol. void (*destructor)(struct sk_buff *skb); #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +EOF + fi + if [ "${1#3.13}" != "$1" ]; then + patch -f -s -p1 <<'EOF' +From 7b4ec8dd7d4ac467e9eee4d49f2c9574d773efbb Mon Sep 17 00:00:00 2001 +From: Johannes Berg +Date: Thu, 16 Jan 2014 10:18:48 +1030 +Subject: [PATCH] export: declare ksymtab symbols + +sparse complains about any __ksymtab symbols with the following: + + warning: symbol '__ksymtab_...' was not declared. Should it be static? + +due to Andi's patch making it non-static. + +Mollify sparse by declaring the symbol extern, otherwise we get +drowned in sparse warnings for anything that uses EXPORT_SYMBOL +in the sources, making it easy to miss real warnings. + +Fixes: e0f244c63fc9 ("asmlinkage, module: Make ksymtab [...] __visible") +Signed-off-by: Johannes Berg +Acked-by: Andi Kleen +Signed-off-by: Rusty Russell +--- + include/linux/export.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/linux/export.h b/include/linux/export.h +index 3f2793d..96e45ea 100644 +--- a/include/linux/export.h ++++ b/include/linux/export.h +@@ -59,6 +59,7 @@ extern struct module __this_module; + static const char __kstrtab_##sym[] \ + __attribute__((section("__ksymtab_strings"), aligned(1))) \ + = VMLINUX_SYMBOL_STR(sym); \ ++ extern const struct kernel_symbol __ksymtab_##sym; \ + __visible const struct kernel_symbol __ksymtab_##sym \ + __used \ + __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ EOF fi ) diff --git a/scripts/list-source-files b/scripts/list-source-files index a14307b3b..4a2eed863 100755 --- a/scripts/list-source-files +++ b/scripts/list-source-files @@ -29,7 +29,13 @@ list_source_files() { echo "Ignored directory $1" >&2 fi elif [ -e "$r/.hg" ]; then - hg manifest + subdir="${d#${r}}" + if [ -n "${subdir}" ]; then + subdir="${subdir#/}/" + hg manifest | sed -n "s|^$subdir||p" + else + hg manifest + fi else echo "Not under source control: $1 ?" >&2 fi diff --git a/scst.spec.in b/scst.spec.in index 3608a72a9..579888487 100644 --- a/scst.spec.in +++ b/scst.spec.in @@ -71,11 +71,11 @@ done %install export KVER=%{kver} PREFIX=%{_prefix} MANDIR=%{_mandir} export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y -for d in scst iscsi-scst srpt; do +for d in scst; do DESTDIR=%{buildroot} %{make} -C $d install done -for d in fcst qla2x00t/qla2x00-target scst_local; do - INSTALL_MOD_PATH=%{buildroot} %{make} -C $d install +for d in fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do + DESTDIR=%{buildroot} INSTALL_MOD_PATH=%{buildroot} %{make} -C $d install done rm -f %{buildroot}/lib/modules/%{kver}/[Mm]odule* diff --git a/scst/ChangeLog b/scst/ChangeLog index 8e92bcc7f..949ddc4a9 100644 --- a/scst/ChangeLog +++ b/scst/ChangeLog @@ -75,7 +75,7 @@ Summary of changes between versions 1.0.1 and 1.0.2 - Automatic sessions reassignment implemented with corresponding atomic management commands added - - Generation of INQUERY DATA HAS CHANGED Unit Attention or AEN for + - Generation of INQUIRY DATA HAS CHANGED Unit Attention or AEN for changed devices during automatic sessions reassignment added - Requeue global Unit Attentions on delivery failure added diff --git a/scst/README b/scst/README index f9419e25e..9e91bcc65 100644 --- a/scst/README +++ b/scst/README @@ -953,8 +953,18 @@ Each vdisk_fileio's device has the following attributes in - o_direct - contains O_DIRECT status of this virtual device. + - inq_vend_specific - Vendor specific data that will be reported via + either bytes 36..55 or bytes 96..256 of the INQUIRY response, depending + on whether this field is <= 20 or > 20 bytes long. + - nv_cache - contains NV_CACHE status of this virtual device. + - prod_id - PRODUCT IDENTIFICATION as reported via the INQUIRY response. + The default value for this field is the SCST device name. + + - prod_rev_lvl - PRODUCT REVISION LEVEL as reported via the INQUIRY + response. The default value for this field is " 300". + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -975,6 +985,10 @@ Each vdisk_fileio's device has the following attributes in that these first eight characters are unique or VMware will consider these devices as identical. + - t10_vend_id - Contents of the T10 VENDOR IDENTIFICATION field of the + INQUIRY response. The default value for this field is "SCST_BIO" for + vdisk_block devices and "SCST_FIO" for vdisk_fileio devices. + - usn - contains the virtual device's serial number of INQUIRY data. It is created at the device creation time based on the device name and scst_vdisk_ID scst_vdisk.ko module parameter for procfs (see below) @@ -986,6 +1000,10 @@ Each vdisk_fileio's device has the following attributes in rescan size of the backend file. It is useful if you changed it, for instance, if you resized it. + - vend_specific_id - Vendor specific ID as reported via the Device + Identification VPD page (83h). The default value for this attribute + is the value of the t10_dev_id attribute. + For example: /sys/kernel/scst_tgt/devices/disk1 diff --git a/scst/README_in-tree b/scst/README_in-tree index 055802881..7ff520b93 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -811,8 +811,18 @@ Each vdisk_fileio's device has the following attributes in - o_direct - contains O_DIRECT status of this virtual device. + - inq_vend_specific - Vendor specific data that will be reported via + either bytes 36..55 or bytes 96..256 of the INQUIRY response, depending + on whether this field is <= 20 or > 20 bytes long. + - nv_cache - contains NV_CACHE status of this virtual device. + - prod_id - PRODUCT IDENTIFICATION as reported via the INQUIRY response. + The default value for this field is the SCST device name. + + - prod_rev_lvl - PRODUCT REVISION LEVEL as reported via the INQUIRY + response. The default value for this field is " 300". + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -828,6 +838,10 @@ Each vdisk_fileio's device has the following attributes in created device at creation time based on the device name and scst_vdisk_ID scst_vdisk.ko module parameter (see below). + - t10_vend_id - Contents of the T10 VENDOR IDENTIFICATION field of the + INQUIRY response. The default value for this field is "SCST_BIO" for + vdisk_block devices and "SCST_FIO" for vdisk_fileio devices. + - usn - contains the virtual device's serial number of INQUIRY data. It is created at the device creation time based on the device name and scst_vdisk_ID scst_vdisk.ko module parameter (see below). @@ -838,6 +852,10 @@ Each vdisk_fileio's device has the following attributes in rescan size of the backend file. It is useful if you changed it, for instance, if you resized it. + - vend_specific_id - Vendor specific ID as reported via the Device + Identification VPD page (83h). The default value for this attribute + is the value of the t10_dev_id attribute. + For example: /sys/kernel/scst_tgt/devices/disk1 diff --git a/scst/SysfsRules b/scst/SysfsRules index 182c2ecba..3014c285e 100644 --- a/scst/SysfsRules +++ b/scst/SysfsRules @@ -266,7 +266,11 @@ Identifier attribute. To provide OPTIONAL force close session functionality target drivers MUST implement it using "force_close" write only session's attribute, -which on write to it MUST close the corresponding session. +which on write to it MUST close the corresponding session. The +recommended way to implement it is to add close_session callback to the +target driver's struct scst_tgt_template. This way allows SCST on +initiators' security groups deletion to automatically force close +sessions in those groups. See SCST core's README for more info about those attributes. diff --git a/scst/include/scst.h b/scst/include/scst.h index af3afebc0..229720009 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -119,6 +119,13 @@ typedef _Bool bool; #define nr_cpumask_bits NR_CPUS #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) +#ifndef swap +#define swap(a, b) \ + do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) +#endif +#endif + /* verify cpu argument to cpumask_* operators */ static inline unsigned int cpumask_check(unsigned int cpu) { @@ -179,6 +186,16 @@ static inline unsigned int queue_max_hw_sectors(struct request_queue *q) } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +/* + * See also patch "sched: Fix softirq time accounting" (commit ID + * 75e1056f5c57050415b64cb761a3acc35d91f013). + */ +#ifndef in_serving_softirq +#define in_serving_softirq() in_softirq() +#endif +#endif + #ifndef __list_for_each /* ToDo: cleanup when both are the same for all relevant kernels */ #define __list_for_each list_for_each @@ -1139,29 +1156,6 @@ struct scst_tgt_template { struct completion *tgtt_kobj_release_cmpl; #endif - /* - * Optional vendor to be reported via the SCSI inquiry data. If NULL, - * an SCST device handler specific default value will be used, e.g. - * "SCST_FIO" for scst_vdisk file I/O. - */ - const char *vendor; - - /* - * Optional method that sets the product ID in [buf, buf+size) based - * on the device type (byte 0 of the SCSI inquiry data, which contains - * the peripheral qualifier in the highest three bits and the - * peripheral device type in the lower five bits). - */ - void (*get_product_id)(const struct scst_tgt_dev *tgt_dev, - char *buf, int size); - - /* - * Optional revision to be reported in the SCSI inquiry response. If - * NULL, an SCST device handler specific default value will be used, - * e.g. " 210" for scst_vdisk file I/O. - */ - const char *revision; - /* * Optional method that writes the serial number of a target device in * [buf, buf+size) and returns the number of bytes written. @@ -1175,13 +1169,6 @@ struct scst_tgt_template { */ int (*get_serial)(const struct scst_tgt_dev *tgt_dev, char *buf, int size); - - /* - * Optional method that writes the SCSI inquiry vendor-specific data in - * [buf, buf+size) and returns the number of bytes written. - */ - int (*get_vend_specific)(const struct scst_tgt_dev *tgt_dev, char *buf, - int size); }; /* @@ -1942,15 +1929,15 @@ struct scst_cmd { /* Set if the device was blocked by scst_check_blocked_dev() */ unsigned int unblock_dev:1; - /* Set if this cmd incremented dev->pr_readers_count */ - unsigned int dec_pr_readers_count_needed:1; - /* Set if scst_dec_on_dev_cmd() call is needed on the cmd's finish */ unsigned int dec_on_dev_needed:1; /* Set if cmd is queued as hw pending */ unsigned int cmd_hw_pending:1; + /* Set if cmd has NACA bit set in CDB */ + unsigned int cmd_naca:1; + /* * Set if the target driver wants to alloc data buffers on its own. * In this case tgt_alloc_data_buf() must be provided in the target @@ -2030,6 +2017,9 @@ struct scst_cmd { /* Set if scst_cmd_set_write_not_received_data_len() was called */ unsigned int write_not_received_set:1; + /* Set if cmd has LINK bit set in CDB */ + unsigned int cmd_linked:1; + /**************************************************************/ /* cmd's async flags */ @@ -2359,9 +2349,6 @@ struct scst_dev_registrant { struct scst_device { unsigned int type; /* SCSI type of the device */ - /* Set if reserved via the SPC-2 SCSI RESERVE command. */ - struct scst_session *reserved_by; - /************************************************************* ** Dev's flags. Updates serialized by dev_lock or suspended ** activity @@ -2414,18 +2401,6 @@ struct scst_device { int block_size; int block_shift; - /* - * Set if dev is persistently reserved. Protected by dev_pr_mutex. - * Modified independently to the above field, hence the alignment. - */ - unsigned int pr_is_set:1 __aligned(sizeof(long)); - - /* - * Set if there is a thread changing or going to change PR state(s). - * Protected by dev_pr_mutex. - */ - unsigned int pr_writer_active:1; - struct scst_dev_type *handler; /* corresponding dev handler */ /* Used for storage of dev handler private stuff */ @@ -2454,27 +2429,31 @@ struct scst_device { */ int on_dev_cmd_count; - /* - * How many threads are checking commands for PR allowance. - * Protected by dev_lock. - */ - int pr_readers_count; - /* Memory limits for this device */ struct scst_mem_lim dev_mem_lim; /* List of commands with lock, if dedicated threads are used */ struct scst_cmd_threads dev_cmd_threads; - /************************************************************* - ** Persistent reservation fields. Protected by dev_pr_mutex. - *************************************************************/ + /* Set if reserved via the SPC-2 SCSI RESERVE command. */ + struct scst_session *reserved_by; + + /********************************************************************** + * Persistent reservation fields. Protected as follows: + * - Reading PR data must be protected via scst_pr_read_lock() / + * scst_pr_read_unlock(). + * - Modifying PR data modifications must be protected via + * scst_pr_write_lock() / scst_pr_write_unlock(). + **********************************************************************/ /* - * True if persist through power loss is activated. Modified - * independently to the above field, hence the alignment. + * Set if dev is persistently reserved. Modified independently + * to the above field, hence the alignment. */ - unsigned short pr_aptpl:1 __aligned(sizeof(long)); + unsigned short pr_is_set:1 __aligned(sizeof(long)); + + /* True if persist through power loss is activated. */ + unsigned short pr_aptpl:1; /* Persistent reservation type */ uint8_t pr_type; @@ -2494,6 +2473,8 @@ struct scst_device { /* List of dev's registrants */ struct list_head dev_registrants_list; + /* End of persistent reservation fields protected by dev_pr_mutex. */ + /* * Count of connected tgt_devs from transports, which don't support * PRs, i.e. don't have get_initiator_port_transport_id(). Protected diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index d7c689bf1..29b7c6c5f 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -80,6 +80,14 @@ #if !defined(INSIDE_KERNEL_TREE) #ifdef CONFIG_SCST_DEBUG +#ifdef __CHECKER__ +/* + * Avoid that the while (...) local_bh_enable() loop confuses the lock checking + * code in smatch. + */ +#define sBUG() BUG() +#define sBUG_ON(p) BUG_ON((p)) +#else #define sBUG() do { \ pr_crit("BUG at %s:%d\n", __FILE__, __LINE__); \ local_irq_enable(); \ @@ -98,6 +106,7 @@ BUG(); \ } \ } while (0) +#endif #else diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-3.13.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.13.patch new file mode 100644 index 000000000..0d5a19f0f --- /dev/null +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.13.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/Kconfig b/drivers/Kconfig +index aa43b91..c96860e 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig" + + source "drivers/scsi/Kconfig" + ++source "drivers/scst/Kconfig" ++ + source "drivers/ata/Kconfig" + + source "drivers/md/Kconfig" diff --git a/scst/kernel/in-tree/Makefile.dev_handlers-3.13 b/scst/kernel/in-tree/Makefile.dev_handlers-3.13 new file mode 100644 index 000000000..f933b36f7 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.dev_handlers-3.13 @@ -0,0 +1,14 @@ +ccflags-y += -Wno-unused-parameter + +obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \ + scst_vdisk.o scst_raid.o scst_processor.o scst_user.o + +obj-$(CONFIG_SCST_DISK) += scst_disk.o +obj-$(CONFIG_SCST_TAPE) += scst_tape.o +obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o +obj-$(CONFIG_SCST_MODISK) += scst_modisk.o +obj-$(CONFIG_SCST_CHANGER) += scst_changer.o +obj-$(CONFIG_SCST_RAID) += scst_raid.o +obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o +obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o +obj-$(CONFIG_SCST_USER) += scst_user.o diff --git a/scst/kernel/in-tree/Makefile.drivers.Linux-3.13.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-3.13.patch new file mode 100644 index 000000000..f7213ed4c --- /dev/null +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-3.13.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/Makefile b/drivers/Makefile +index ab93de8..45077ec 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -128,6 +128,7 @@ obj-$(CONFIG_SSB) += ssb/ + obj-$(CONFIG_BCMA) += bcma/ + obj-$(CONFIG_VHOST_RING) += vhost/ + obj-$(CONFIG_VLYNQ) += vlynq/ ++obj-$(CONFIG_SCST) += scst/ + obj-$(CONFIG_STAGING) += staging/ + obj-y += platform/ + #common clk code diff --git a/scst/kernel/in-tree/Makefile.scst-2.6.26 b/scst/kernel/in-tree/Makefile.scst-2.6.26 index e040252e4..1379402be 100644 --- a/scst/kernel/in-tree/Makefile.scst-2.6.26 +++ b/scst/kernel/in-tree/Makefile.scst-2.6.26 @@ -4,7 +4,7 @@ scst-y += scst_main.o scst-y += scst_pres.o scst-y += scst_targ.o scst-y += scst_lib.o -scst-y += scst_proc.o +scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_tg.o scst-y += scst_debug.o diff --git a/scst/kernel/in-tree/Makefile.scst-2.6.27 b/scst/kernel/in-tree/Makefile.scst-2.6.27 index e040252e4..1379402be 100644 --- a/scst/kernel/in-tree/Makefile.scst-2.6.27 +++ b/scst/kernel/in-tree/Makefile.scst-2.6.27 @@ -4,7 +4,7 @@ scst-y += scst_main.o scst-y += scst_pres.o scst-y += scst_targ.o scst-y += scst_lib.o -scst-y += scst_proc.o +scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_tg.o scst-y += scst_debug.o diff --git a/scst/kernel/in-tree/Makefile.scst-2.6.28 b/scst/kernel/in-tree/Makefile.scst-2.6.28 index e040252e4..1379402be 100644 --- a/scst/kernel/in-tree/Makefile.scst-2.6.28 +++ b/scst/kernel/in-tree/Makefile.scst-2.6.28 @@ -4,7 +4,7 @@ scst-y += scst_main.o scst-y += scst_pres.o scst-y += scst_targ.o scst-y += scst_lib.o -scst-y += scst_proc.o +scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_tg.o scst-y += scst_debug.o diff --git a/scst/kernel/in-tree/Makefile.scst-2.6.29 b/scst/kernel/in-tree/Makefile.scst-2.6.29 index e040252e4..1379402be 100644 --- a/scst/kernel/in-tree/Makefile.scst-2.6.29 +++ b/scst/kernel/in-tree/Makefile.scst-2.6.29 @@ -4,7 +4,7 @@ scst-y += scst_main.o scst-y += scst_pres.o scst-y += scst_targ.o scst-y += scst_lib.o -scst-y += scst_proc.o +scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_tg.o scst-y += scst_debug.o diff --git a/scst/kernel/in-tree/Makefile.scst-2.6.30 b/scst/kernel/in-tree/Makefile.scst-2.6.30 index e040252e4..1379402be 100644 --- a/scst/kernel/in-tree/Makefile.scst-2.6.30 +++ b/scst/kernel/in-tree/Makefile.scst-2.6.30 @@ -4,7 +4,7 @@ scst-y += scst_main.o scst-y += scst_pres.o scst-y += scst_targ.o scst-y += scst_lib.o -scst-y += scst_proc.o +scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_tg.o scst-y += scst_debug.o diff --git a/scst/kernel/in-tree/Makefile.scst-2.6.31 b/scst/kernel/in-tree/Makefile.scst-2.6.31 index e040252e4..1379402be 100644 --- a/scst/kernel/in-tree/Makefile.scst-2.6.31 +++ b/scst/kernel/in-tree/Makefile.scst-2.6.31 @@ -4,7 +4,7 @@ scst-y += scst_main.o scst-y += scst_pres.o scst-y += scst_targ.o scst-y += scst_lib.o -scst-y += scst_proc.o +scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_tg.o scst-y += scst_debug.o diff --git a/scst/kernel/in-tree/Makefile.scst-2.6.32 b/scst/kernel/in-tree/Makefile.scst-2.6.32 index e040252e4..1379402be 100644 --- a/scst/kernel/in-tree/Makefile.scst-2.6.32 +++ b/scst/kernel/in-tree/Makefile.scst-2.6.32 @@ -4,7 +4,7 @@ scst-y += scst_main.o scst-y += scst_pres.o scst-y += scst_targ.o scst-y += scst_lib.o -scst-y += scst_proc.o +scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_tg.o scst-y += scst_debug.o diff --git a/scst/kernel/in-tree/Makefile.scst-3.13 b/scst/kernel/in-tree/Makefile.scst-3.13 new file mode 100644 index 000000000..53af5f388 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.scst-3.13 @@ -0,0 +1,13 @@ +ccflags-y += -Wno-unused-parameter + +scst-y += scst_main.o +scst-y += scst_pres.o +scst-y += scst_targ.o +scst-y += scst_lib.o +scst-y += scst_sysfs.o +scst-y += scst_mem.o +scst-y += scst_tg.o +scst-y += scst_debug.o + +obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \ + srpt/ scst_local/ diff --git a/scst/kernel/scst_exec_req_fifo-3.13.patch b/scst/kernel/scst_exec_req_fifo-3.13.patch new file mode 100644 index 000000000..84980e46a --- /dev/null +++ b/scst/kernel/scst_exec_req_fifo-3.13.patch @@ -0,0 +1,528 @@ +=== modified file 'block/blk-map.c' +--- old/block/blk-map.c 2014-01-30 00:25:53 +0000 ++++ new/block/blk-map.c 2014-01-30 00:44:50 +0000 +@@ -5,6 +5,8 @@ + #include + #include + #include ++#include ++#include + #include /* for struct sg_iovec */ + + #include "blk.h" +@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio) + } + EXPORT_SYMBOL(blk_rq_unmap_user); + ++struct blk_kern_sg_work { ++ atomic_t bios_inflight; ++ struct sg_table sg_table; ++ struct scatterlist *src_sgl; ++}; ++ ++static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw) ++{ ++ struct sg_table *sgt = &bw->sg_table; ++ struct scatterlist *sg; ++ int i; ++ ++ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { ++ struct page *pg = sg_page(sg); ++ if (pg == NULL) ++ break; ++ __free_page(pg); ++ } ++ ++ sg_free_table(sgt); ++ kfree(bw); ++ return; ++} ++ ++static void blk_bio_map_kern_endio(struct bio *bio, int err) ++{ ++ struct blk_kern_sg_work *bw = bio->bi_private; ++ ++ if (bw != NULL) { ++ /* Decrement the bios in processing and, if zero, free */ ++ BUG_ON(atomic_read(&bw->bios_inflight) <= 0); ++ if (atomic_dec_and_test(&bw->bios_inflight)) { ++ if ((bio_data_dir(bio) == READ) && (err == 0)) { ++ unsigned long flags; ++ ++ local_irq_save(flags); /* to protect KMs */ ++ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0); ++ local_irq_restore(flags); ++ } ++ blk_free_kern_sg_work(bw); ++ } ++ } ++ ++ bio_put(bio); ++ return; ++} ++ ++static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work **pbw, ++ gfp_t gfp, gfp_t page_gfp) ++{ ++ int res = 0, i; ++ struct scatterlist *sg; ++ struct scatterlist *new_sgl; ++ int new_sgl_nents; ++ size_t len = 0, to_copy; ++ struct blk_kern_sg_work *bw; ++ ++ bw = kzalloc(sizeof(*bw), gfp); ++ if (bw == NULL) ++ goto out; ++ ++ bw->src_sgl = sgl; ++ ++ for_each_sg(sgl, sg, nents, i) ++ len += sg->length; ++ to_copy = len; ++ ++ new_sgl_nents = PFN_UP(len); ++ ++ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp); ++ if (res != 0) ++ goto err_free; ++ ++ new_sgl = bw->sg_table.sgl; ++ ++ for_each_sg(new_sgl, sg, new_sgl_nents, i) { ++ struct page *pg; ++ ++ pg = alloc_page(page_gfp); ++ if (pg == NULL) ++ goto err_free; ++ ++ sg_assign_page(sg, pg); ++ sg->length = min_t(size_t, PAGE_SIZE, len); ++ ++ len -= PAGE_SIZE; ++ } ++ ++ if (rq_data_dir(rq) == WRITE) { ++ /* ++ * We need to limit amount of copied data to to_copy, because ++ * sgl might have the last element in sgl not marked as last in ++ * SG chaining. ++ */ ++ sg_copy(new_sgl, sgl, 0, to_copy); ++ } ++ ++ *pbw = bw; ++ /* ++ * REQ_COPY_USER name is misleading. It should be something like ++ * REQ_HAS_TAIL_SPACE_FOR_PADDING. ++ */ ++ rq->cmd_flags |= REQ_COPY_USER; ++ ++out: ++ return res; ++ ++err_free: ++ blk_free_kern_sg_work(bw); ++ res = -ENOMEM; ++ goto out; ++} ++ ++static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work *bw, gfp_t gfp) ++{ ++ int res; ++ struct request_queue *q = rq->q; ++ int rw = rq_data_dir(rq); ++ int max_nr_vecs, i; ++ size_t tot_len; ++ bool need_new_bio; ++ struct scatterlist *sg, *prev_sg = NULL; ++ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL; ++ int bios; ++ ++ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) { ++ WARN_ON(1); ++ res = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Let's keep each bio allocation inside a single page to decrease ++ * probability of failure. ++ */ ++ max_nr_vecs = min_t(size_t, ++ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)), ++ BIO_MAX_PAGES); ++ ++ need_new_bio = true; ++ tot_len = 0; ++ bios = 0; ++ for_each_sg(sgl, sg, nents, i) { ++ struct page *page = sg_page(sg); ++ void *page_addr = page_address(page); ++ size_t len = sg->length, l; ++ size_t offset = sg->offset; ++ ++ tot_len += len; ++ prev_sg = sg; ++ ++ /* ++ * Each segment must be aligned on DMA boundary and ++ * not on stack. The last one may have unaligned ++ * length as long as the total length is aligned to ++ * DMA padding alignment. ++ */ ++ if (i == nents - 1) ++ l = 0; ++ else ++ l = len; ++ if (((sg->offset | l) & queue_dma_alignment(q)) || ++ (page_addr && object_is_on_stack(page_addr + sg->offset))) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ while (len > 0) { ++ size_t bytes; ++ int rc; ++ ++ if (need_new_bio) { ++ bio = bio_kmalloc(gfp, max_nr_vecs); ++ if (bio == NULL) { ++ res = -ENOMEM; ++ goto out_free_bios; ++ } ++ ++ if (rw == WRITE) ++ bio->bi_rw |= REQ_WRITE; ++ ++ bios++; ++ bio->bi_private = bw; ++ bio->bi_end_io = blk_bio_map_kern_endio; ++ ++ if (hbio == NULL) ++ hbio = tbio = bio; ++ else ++ tbio = tbio->bi_next = bio; ++ } ++ ++ bytes = min_t(size_t, len, PAGE_SIZE - offset); ++ ++ rc = bio_add_pc_page(q, bio, page, bytes, offset); ++ if (rc < bytes) { ++ if (unlikely(need_new_bio || (rc < 0))) { ++ if (rc < 0) ++ res = rc; ++ else ++ res = -EIO; ++ goto out_free_bios; ++ } else { ++ need_new_bio = true; ++ len -= rc; ++ offset += rc; ++ continue; ++ } ++ } ++ ++ need_new_bio = false; ++ offset = 0; ++ len -= bytes; ++ page = nth_page(page, 1); ++ } ++ } ++ ++ if (hbio == NULL) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ /* Total length must be aligned on DMA padding alignment */ ++ if ((tot_len & q->dma_pad_mask) && ++ !(rq->cmd_flags & REQ_COPY_USER)) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ if (bw != NULL) ++ atomic_set(&bw->bios_inflight, bios); ++ ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio->bi_next = NULL; ++ ++ blk_queue_bounce(q, &bio); ++ ++ res = blk_rq_append_bio(q, rq, bio); ++ if (unlikely(res != 0)) { ++ bio->bi_next = hbio; ++ hbio = bio; ++ /* We can have one or more bios bounced */ ++ goto out_unmap_bios; ++ } ++ } ++ ++ res = 0; ++ ++ rq->buffer = NULL; ++out: ++ return res; ++ ++out_unmap_bios: ++ blk_rq_unmap_kern_sg(rq, res); ++ ++out_free_bios: ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio_put(bio); ++ } ++ goto out; ++} ++ ++/** ++ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC ++ * @rq: request to fill ++ * @sgl: area to map ++ * @nents: number of elements in @sgl ++ * @gfp: memory allocation flags ++ * ++ * Description: ++ * Data will be mapped directly if possible. Otherwise a bounce ++ * buffer will be used. ++ */ ++int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp) ++{ ++ int res; ++ ++ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp); ++ if (unlikely(res != 0)) { ++ struct blk_kern_sg_work *bw = NULL; ++ ++ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw, ++ gfp, rq->q->bounce_gfp | gfp); ++ if (unlikely(res != 0)) ++ goto out; ++ ++ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl, ++ bw->sg_table.nents, bw, gfp); ++ if (res != 0) { ++ blk_free_kern_sg_work(bw); ++ goto out; ++ } ++ } ++ ++ rq->buffer = NULL; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(blk_rq_map_kern_sg); ++ ++/** ++ * blk_rq_unmap_kern_sg - unmap a request with kernel sg ++ * @rq: request to unmap ++ * @err: non-zero error code ++ * ++ * Description: ++ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called ++ * only in case of an error! ++ */ ++void blk_rq_unmap_kern_sg(struct request *rq, int err) ++{ ++ struct bio *bio = rq->bio; ++ ++ while (bio) { ++ struct bio *b = bio; ++ bio = bio->bi_next; ++ b->bi_end_io(b, err); ++ } ++ rq->bio = NULL; ++ ++ return; ++} ++EXPORT_SYMBOL(blk_rq_unmap_kern_sg); ++ + /** + * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage + * @q: request queue where request should be inserted + +=== modified file 'include/linux/blkdev.h' +--- old/include/linux/blkdev.h 2014-01-30 00:25:53 +0000 ++++ new/include/linux/blkdev.h 2014-01-30 00:44:50 +0000 +@@ -712,6 +712,8 @@ extern unsigned long blk_max_low_pfn, bl + #define BLK_DEFAULT_SG_TIMEOUT (60 * HZ) + #define BLK_MIN_SG_TIMEOUT (7 * HZ) + ++#define SCSI_EXEC_REQ_FIFO_DEFINED ++ + #ifdef CONFIG_BOUNCE + extern int init_emergency_isa_pool(void); + extern void blk_queue_bounce(struct request_queue *q, struct bio **bio); +@@ -831,6 +833,9 @@ extern int blk_rq_map_kern(struct reques + extern int blk_rq_map_user_iov(struct request_queue *, struct request *, + struct rq_map_data *, struct sg_iovec *, int, + unsigned int, gfp_t); ++extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp); ++extern void blk_rq_unmap_kern_sg(struct request *rq, int err); + extern int blk_execute_rq(struct request_queue *, struct gendisk *, + struct request *, int); + extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, + +=== modified file 'include/linux/scatterlist.h' +--- old/include/linux/scatterlist.h 2014-01-30 00:25:53 +0000 ++++ new/include/linux/scatterlist.h 2014-01-30 00:44:50 +0000 +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + struct sg_table { + struct scatterlist *sgl; /* the list */ +@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt + size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, + void *buf, size_t buflen, off_t skip); + ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len); ++ + /* + * Maximum number of entries that will be allocated in one piece, if + * a list larger than this is required then chaining will be utilized. + +=== modified file 'lib/scatterlist.c' +--- old/lib/scatterlist.c 2014-01-30 00:25:53 +0000 ++++ new/lib/scatterlist.c 2014-01-30 00:44:50 +0000 +@@ -717,3 +717,127 @@ size_t sg_pcopy_to_buffer(struct scatter + return sg_copy_buffer(sgl, nents, buf, buflen, skip, true); + } + EXPORT_SYMBOL(sg_pcopy_to_buffer); ++ ++ ++/* ++ * Can switch to the next dst_sg element, so, to copy to strictly only ++ * one dst_sg element, it must be either last in the chain, or ++ * copy_len == dst_sg->length. ++ */ ++static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len, ++ size_t *pdst_offs, struct scatterlist *src_sg, ++ size_t copy_len) ++{ ++ int res = 0; ++ struct scatterlist *dst_sg; ++ size_t src_len, dst_len, src_offs, dst_offs; ++ struct page *src_page, *dst_page; ++ ++ dst_sg = *pdst_sg; ++ dst_len = *pdst_len; ++ dst_offs = *pdst_offs; ++ dst_page = sg_page(dst_sg); ++ ++ src_page = sg_page(src_sg); ++ src_len = src_sg->length; ++ src_offs = src_sg->offset; ++ ++ do { ++ void *saddr, *daddr; ++ size_t n; ++ ++ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) + ++ (src_offs & ~PAGE_MASK); ++ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) + ++ (dst_offs & ~PAGE_MASK); ++ ++ if (((src_offs & ~PAGE_MASK) == 0) && ++ ((dst_offs & ~PAGE_MASK) == 0) && ++ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) && ++ (copy_len >= PAGE_SIZE)) { ++ copy_page(daddr, saddr); ++ n = PAGE_SIZE; ++ } else { ++ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK), ++ PAGE_SIZE - (src_offs & ~PAGE_MASK)); ++ n = min(n, src_len); ++ n = min(n, dst_len); ++ n = min_t(size_t, n, copy_len); ++ memcpy(daddr, saddr, n); ++ } ++ dst_offs += n; ++ src_offs += n; ++ ++ kunmap_atomic(saddr); ++ kunmap_atomic(daddr); ++ ++ res += n; ++ copy_len -= n; ++ if (copy_len == 0) ++ goto out; ++ ++ src_len -= n; ++ dst_len -= n; ++ if (dst_len == 0) { ++ dst_sg = sg_next(dst_sg); ++ if (dst_sg == NULL) ++ goto out; ++ dst_page = sg_page(dst_sg); ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ } ++ } while (src_len > 0); ++ ++out: ++ *pdst_sg = dst_sg; ++ *pdst_len = dst_len; ++ *pdst_offs = dst_offs; ++ return res; ++} ++ ++/** ++ * sg_copy - copy one SG vector to another ++ * @dst_sg: destination SG ++ * @src_sg: source SG ++ * @nents_to_copy: maximum number of entries to copy ++ * @copy_len: maximum amount of data to copy. If 0, then copy all. ++ * ++ * Description: ++ * Data from the source SG vector will be copied to the destination SG ++ * vector. End of the vectors will be determined by sg_next() returning ++ * NULL. Returns number of bytes copied. ++ */ ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len) ++{ ++ int res = 0; ++ size_t dst_len, dst_offs; ++ ++ if (copy_len == 0) ++ copy_len = 0x7FFFFFFF; /* copy all */ ++ ++ if (nents_to_copy == 0) ++ nents_to_copy = 0x7FFFFFFF; /* copy all */ ++ ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ ++ do { ++ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs, ++ src_sg, copy_len); ++ copy_len -= copied; ++ res += copied; ++ if ((copy_len == 0) || (dst_sg == NULL)) ++ goto out; ++ ++ nents_to_copy--; ++ if (nents_to_copy == 0) ++ goto out; ++ ++ src_sg = sg_next(src_sg); ++ } while (src_sg != NULL); ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(sg_copy); + diff --git a/scst/src/Makefile b/scst/src/Makefile index 9b6a55862..5d268577b 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -54,15 +54,25 @@ obj-$(CONFIG_SCST) += scst.o dev_handlers/ obj-$(BUILD_DEV) += $(DEV_HANDLERS_DIR)/ else +######### BEGIN OUT-OF-TREE RULES ######### + ifeq ($(KVER),) ifeq ($(KDIR),) - KVER = $(shell uname -r) + KVER := $(shell uname -r) KDIR := /lib/modules/$(KVER)/build + else + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) endif else KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + all: $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_DEV=m @@ -97,7 +107,7 @@ ifneq ($(MOD_VERS),) rm -f $(INSTALL_DIR_H)/Modules.symvers install -m 644 Module.symvers $(INSTALL_DIR_H) endif - -/sbin/depmod -a $(KVER) + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) mkdir -p $(DESTDIR)/var/lib/scst/pr @echo "****************************************************************" @echo "*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*" @@ -106,7 +116,7 @@ endif @echo "*!! target drivers, custom dev handlers and necessary user !!*" @echo "*!! space applications. Otherwise, because of the versions !!*" @echo "*!! mismatch, you could have many problems and crashes. !!*" - @echo "*!! See IMPORTANT note in the \"Installation\" section of !!*" + @echo "*!! See IMPORTANT note in the \"Installation\" section of !!*" @echo "*!! SCST's README file for more info. !!*" @echo "*!! !!*" @echo "*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*" @@ -116,15 +126,13 @@ uninstall: cd $(DEV_HANDLERS_DIR) && $(MAKE) $@ rm -f $(INSTALL_DIR)/scst.ko -rmdir $(INSTALL_DIR) 2>/dev/null - -/sbin/depmod -a $(KVER) + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) rm -rf $(INSTALL_DIR_H) + +########## END OUT-OF-TREE RULES ########## endif -ifeq ($(KVER),) -INSTALL_DIR := $(DESTDIR)/lib/modules/$(shell uname -r)/extra -else -INSTALL_DIR := $(DESTDIR)/lib/modules/$(KVER)/extra -endif +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra INSTALL_DIR_H := $(DESTDIR)$(PREFIX)/include/scst enable-Wextra = $(shell uname_r="$$(uname -r)"; if [ "$${uname_r%.el5}" = "$${uname_r}" ]; then echo "$(1)"; fi) diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index a460ce1a6..3ae5471d6 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -46,27 +46,38 @@ obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o obj-$(CONFIG_SCST_USER) += scst_user.o else -ifeq ($(KDIR),) - KVER = $(shell uname -r) +######### BEGIN OUT-OF-TREE RULES ######### + +ifeq ($(KVER),) + ifeq ($(KDIR),) + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build + else + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + endif +else KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra + all: $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) \ modules_install - -/sbin/depmod -a $(KVER) uninstall: rm -f $(INSTALL_DIR)/dev_handlers/scst_*.ko -endif -ifeq ($(KVER),) -INSTALL_DIR := /lib/modules/$(shell uname -r)/extra -else -INSTALL_DIR := /lib/modules/$(KVER)/extra +########## END OUT-OF-TREE RULES ########## endif enable-Wextra = $(shell uname_r="$$(uname -r)"; if [ "$${uname_r%.el5}" = "$${uname_r}" ]; then echo "$(1)"; fi) diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 6c05b876c..962a496b7 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -691,6 +691,9 @@ static int dev_user_alloc_space(struct scst_user_cmd *ucmd) ucmd->user_cmd.alloc_cmd.data_direction = cmd->data_direction; ucmd->user_cmd.alloc_cmd.sn = cmd->tgt_sn; + TRACE_DBG("Preparing ALLOC_MEM for user space (ucmd=%p, h=%d, " + "alloc_len %d)", ucmd, ucmd->h, ucmd->user_cmd.alloc_cmd.alloc_len); + dev_user_add_to_ready(ucmd); res = SCST_CMD_STATE_STOP; @@ -987,6 +990,7 @@ static void dev_user_on_free_cmd(struct scst_cmd *cmd) goto out_reply; } + TRACE_DBG("Preparing ON_FREE_CMD (pbuff 0x%lx)", ucmd->ubuff); ucmd->user_cmd_payload_len = offsetof(struct scst_user_get_cmd, on_free_cmd) + sizeof(ucmd->user_cmd.on_free_cmd); @@ -1129,7 +1133,12 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd) { struct scst_user_dev *dev = ucmd->dev; unsigned long flags; - int do_wake = in_interrupt(); + /* + * Note, a separate softIRQ check is required for real-time kernels + * (CONFIG_PREEMPT_RT_FULL=y) since on such kernels softIRQ's are + * served in thread context. See also http://lwn.net/Articles/302043/. + */ + int do_wake = in_interrupt() || in_serving_softirq(); TRACE_ENTRY(); @@ -1286,6 +1295,7 @@ out_process: scst_post_alloc_data_buf(cmd); scst_process_active_cmd(cmd, false); + TRACE_DBG("%s", "ALLOC_MEM finished"); TRACE_EXIT_RES(res); return res; @@ -1350,6 +1360,8 @@ static int dev_user_process_reply_parse(struct scst_user_cmd *ucmd, out_process: scst_post_parse(cmd); + TRACE_DBG("%s", "PARSE finished"); + scst_process_active_cmd(cmd, false); TRACE_EXIT_RES(res); @@ -1408,6 +1420,7 @@ static int dev_user_process_reply_on_free(struct scst_user_cmd *ucmd) dev_user_free_sgv(ucmd); ucmd_put(ucmd); + TRACE_DBG("%s", "ON_FREE_CMD finished"); TRACE_EXIT_RES(res); return res; } @@ -1422,6 +1435,7 @@ static int dev_user_process_reply_on_cache_free(struct scst_user_cmd *ucmd) ucmd_put(ucmd); + TRACE_MEM("%s", "ON_CACHED_MEM_FREE finished"); TRACE_EXIT_RES(res); return res; } @@ -1535,6 +1549,7 @@ out_compl: /* !! At this point cmd can be already freed !! */ out: + TRACE_DBG("%s", "EXEC finished"); TRACE_EXIT_RES(res); return res; @@ -2968,7 +2983,7 @@ static int dev_user_register_dev(struct file *file, dev->pool = sgv_pool_create(dev->devtype.name, sgv_no_clustering, dev_desc->sgv_single_alloc_pages, dev_desc->sgv_shared, - dev_desc->sgv_purge_interval); + dev_desc->sgv_purge_interval * HZ); if (dev->pool == NULL) { res = -ENOMEM; goto out_deinit_threads; @@ -2985,7 +3000,7 @@ static int dev_user_register_dev(struct file *file, sgv_tail_clustering, dev_desc->sgv_single_alloc_pages, dev_desc->sgv_shared, - dev_desc->sgv_purge_interval); + dev_desc->sgv_purge_interval * HZ); if (dev->pool_clust == NULL) { res = -ENOMEM; goto out_free0; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index d6bd45ebf..e1ce52a4c 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -82,6 +82,7 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define SCST_FIO_REV " 300" #define MAX_USN_LEN (20+1) /* For '\0' */ +#define MAX_INQ_VEND_SPECIFIC_LEN (INQ_BUF_SZ - 96) #define INQ_BUF_SZ 256 #define EVPD 0x01 @@ -175,11 +176,22 @@ struct scst_vdisk_dev { scst_mutex and suspended activities */ uint16_t command_set_version; - /* All 4 protected by vdisk_serial_rwlock */ + /* All 14 protected by vdisk_serial_rwlock */ + unsigned int t10_vend_id_set:1; /* true if t10_vend_id manually set */ + /* true if vend_specific_id manually set */ + unsigned int vend_specific_id_set:1; + unsigned int prod_id_set:1; /* true if prod_id manually set */ + unsigned int prod_rev_lvl_set:1; /* true if prod_rev_lvl manually set */ unsigned int t10_dev_id_set:1; /* true if t10_dev_id manually set */ unsigned int usn_set:1; /* true if usn manually set */ + char t10_vend_id[8 + 1]; + char vend_specific_id[32 + 1]; + char prod_id[16 + 1]; + char prod_rev_lvl[4 + 1]; char t10_dev_id[16+8+2]; /* T10 device ID */ char usn[MAX_USN_LEN]; + uint8_t inq_vend_specific[MAX_INQ_VEND_SPECIFIC_LEN]; + int inq_vend_specific_len; struct scst_device *dev; struct list_head vdev_list_entry; @@ -319,6 +331,22 @@ static ssize_t vdev_sysfs_filename_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_t10_vend_id_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_t10_vend_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_vend_specific_id_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_vend_specific_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_prod_id_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_prod_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_prod_rev_lvl_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_prod_rev_lvl_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj, @@ -327,6 +355,10 @@ static ssize_t vdev_sysfs_usn_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdev_sysfs_usn_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_inq_vend_specific_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_inq_vend_specific_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdev_zero_copy_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); @@ -357,11 +389,28 @@ static struct kobj_attribute vdisk_filename_attr = __ATTR(filename, S_IRUGO, vdev_sysfs_filename_show, NULL); static struct kobj_attribute vdisk_resync_size_attr = __ATTR(resync_size, S_IWUSR, NULL, vdisk_sysfs_resync_size_store); +static struct kobj_attribute vdev_t10_vend_id_attr = + __ATTR(t10_vend_id, S_IWUSR|S_IRUGO, vdev_sysfs_t10_vend_id_show, + vdev_sysfs_t10_vend_id_store); +static struct kobj_attribute vdev_vend_specific_id_attr = + __ATTR(vend_specific_id, S_IWUSR|S_IRUGO, + vdev_sysfs_vend_specific_id_show, + vdev_sysfs_vend_specific_id_store); +static struct kobj_attribute vdev_prod_id_attr = + __ATTR(prod_id, S_IWUSR|S_IRUGO, vdev_sysfs_prod_id_show, + vdev_sysfs_prod_id_store); +static struct kobj_attribute vdev_prod_rev_lvl_attr = + __ATTR(prod_rev_lvl, S_IWUSR|S_IRUGO, vdev_sysfs_prod_rev_lvl_show, + vdev_sysfs_prod_rev_lvl_store); static struct kobj_attribute vdev_t10_dev_id_attr = __ATTR(t10_dev_id, S_IWUSR|S_IRUGO, vdev_sysfs_t10_dev_id_show, vdev_sysfs_t10_dev_id_store); static struct kobj_attribute vdev_usn_attr = __ATTR(usn, S_IWUSR|S_IRUGO, vdev_sysfs_usn_show, vdev_sysfs_usn_store); +static struct kobj_attribute vdev_inq_vend_specific_attr = + __ATTR(inq_vend_specific, S_IWUSR|S_IRUGO, + vdev_sysfs_inq_vend_specific_show, + vdev_sysfs_inq_vend_specific_store); static struct kobj_attribute vdev_zero_copy_attr = __ATTR(zero_copy, S_IRUGO, vdev_zero_copy_show, NULL); @@ -381,8 +430,13 @@ static const struct attribute *vdisk_fileio_attrs[] = { &vdisk_removable_attr.attr, &vdisk_filename_attr.attr, &vdisk_resync_size_attr.attr, + &vdev_t10_vend_id_attr.attr, + &vdev_vend_specific_id_attr.attr, + &vdev_prod_id_attr.attr, + &vdev_prod_rev_lvl_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, + &vdev_inq_vend_specific_attr.attr, &vdev_zero_copy_attr.attr, NULL, }; @@ -397,8 +451,13 @@ static const struct attribute *vdisk_blockio_attrs[] = { &vdisk_rotational_attr.attr, &vdisk_filename_attr.attr, &vdisk_resync_size_attr.attr, + &vdev_t10_vend_id_attr.attr, + &vdev_vend_specific_id_attr.attr, + &vdev_prod_id_attr.attr, + &vdev_prod_rev_lvl_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, + &vdev_inq_vend_specific_attr.attr, &vdisk_tp_attr.attr, NULL, }; @@ -409,8 +468,13 @@ static const struct attribute *vdisk_nullio_attrs[] = { &vdisk_rd_only_attr.attr, &vdev_dummy_attr.attr, &vdisk_removable_attr.attr, + &vdev_t10_vend_id_attr.attr, + &vdev_vend_specific_id_attr.attr, + &vdev_prod_id_attr.attr, + &vdev_prod_rev_lvl_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, + &vdev_inq_vend_specific_attr.attr, &vdisk_rotational_attr.attr, NULL, }; @@ -418,8 +482,13 @@ static const struct attribute *vdisk_nullio_attrs[] = { static const struct attribute *vcdrom_attrs[] = { &vdev_size_attr.attr, &vcdrom_filename_attr.attr, + &vdev_t10_vend_id_attr.attr, + &vdev_vend_specific_id_attr.attr, + &vdev_prod_id_attr.attr, + &vdev_prod_rev_lvl_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, + &vdev_inq_vend_specific_attr.attr, NULL, }; @@ -428,7 +497,10 @@ static const struct attribute *vcdrom_attrs[] = { /* Protects vdisks addition/deletion and related activities, like search */ static DEFINE_MUTEX(scst_vdisk_mutex); -/* Protects devices t10_dev_id and usn */ +/* + * Protects the device attributes t10_vend_id, vend_specific_id, prod_id, + * prod_rev_lvl, t10_dev_id, usn and inq_vend_specific. + */ static DEFINE_RWLOCK(vdisk_serial_rwlock); /* Protected by scst_vdisk_mutex */ @@ -469,9 +541,17 @@ static struct scst_dev_type vdisk_file_devtype = { .add_device = vdisk_add_fileio_device, .del_device = vdisk_del_device, .dev_attrs = vdisk_fileio_attrs, - .add_device_parameters = "filename, blocksize, write_through, " - "nv_cache, o_direct, read_only, removable, rotational, " - "thin_provisioned, zero_copy", + .add_device_parameters = + "blocksize, " + "filename, " + "nv_cache, " + "o_direct, " + "read_only, " + "removable, " + "rotational, " + "thin_provisioned, " + "write_through, " + "zero_copy", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -506,9 +586,15 @@ static struct scst_dev_type vdisk_blk_devtype = { .add_device = vdisk_add_blockio_device, .del_device = vdisk_del_device, .dev_attrs = vdisk_blockio_attrs, - .add_device_parameters = "filename, blocksize, write_through, " - "nv_cache, read_only, removable, rotational, " - "thin_provisioned", + .add_device_parameters = + "blocksize, " + "filename, " + "nv_cache, " + "read_only, " + "removable, " + "rotational, " + "thin_provisioned, " + "write_through", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -541,8 +627,12 @@ static struct scst_dev_type vdisk_null_devtype = { .add_device = vdisk_add_nullio_device, .del_device = vdisk_del_device, .dev_attrs = vdisk_nullio_attrs, - .add_device_parameters = "blocksize, read_only, dummy, removable," - " rotational", + .add_device_parameters = + "blocksize, " + "dummy, " + "read_only, " + "removable, " + "rotational", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -1529,7 +1619,7 @@ out: return res; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) /** * finish_read - Release the pages referenced by prepare_read(). */ @@ -2125,21 +2215,23 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, (unsigned long long)start_lba, (unsigned long long)blocks); if (virt_dev->blockio) { + sector_t start_sector = start_lba << (cmd->dev->block_shift - 9); + sector_t nr_sects = blocks << (cmd->dev->block_shift - 9); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) struct inode *inode = fd->f_dentry->d_inode; gfp_t gfp = cmd->cmd_gfp_mask; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31) - err = blkdev_issue_discard(inode->i_bdev, start_lba, blocks, gfp); + err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp); #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) \ && !(LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 34) \ && defined(CONFIG_SUSE_KERNEL)) - err = blkdev_issue_discard(inode->i_bdev, start_lba, blocks, + err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp, DISCARD_FL_WAIT); #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) - err = blkdev_issue_discard(inode->i_bdev, start_lba, blocks, + err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp, BLKDEV_IFL_WAIT); #else - err = blkdev_issue_discard(inode->i_bdev, start_lba, blocks, gfp, 0); + err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp, 0); #endif if (unlikely(err != 0)) { PRINT_ERROR("blkdev_issue_discard() for " @@ -2215,6 +2307,28 @@ out: return; } +/* + * Copy a zero-terminated string into a fixed-size byte array and fill the + * trailing bytes with @fill_byte. + */ +static void scst_copy_and_fill_b(char *dst, const char *src, int len, + uint8_t fill_byte) +{ + int cpy_len = min_t(int, strlen(src), len); + + memcpy(dst, src, cpy_len); + memset(dst + cpy_len, fill_byte, len - cpy_len); +} + +/* + * Copy a zero-terminated string into a fixed-size char array and fill the + * trailing characters with spaces. + */ +static void scst_copy_and_fill(char *dst, const char *src, int len) +{ + scst_copy_and_fill_b(dst, src, len, ' '); +} + static enum compl_status_e vdisk_exec_write_same(struct vdisk_cmd_params *p) { struct scst_cmd *cmd = p->cmd; @@ -2425,16 +2539,10 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) /* T10 vendor identifier field format (faked) */ buf[num + 0] = 0x2; /* ASCII */ buf[num + 1] = 0x1; /* Vendor ID */ - if (cmd->tgtt->vendor) - memcpy(&buf[num + 4], cmd->tgtt->vendor, 8); - else if (virt_dev->blockio) - memcpy(&buf[num + 4], SCST_BIO_VENDOR, 8); - else - memcpy(&buf[num + 4], SCST_FIO_VENDOR, 8); - read_lock(&vdisk_serial_rwlock); - i = strlen(virt_dev->t10_dev_id); - memcpy(&buf[num + 12], virt_dev->t10_dev_id, i); + scst_copy_and_fill(&buf[num + 4], virt_dev->t10_vend_id, 8); + i = strlen(virt_dev->vend_specific_id); + memcpy(&buf[num + 12], virt_dev->vend_specific_id, i); read_unlock(&vdisk_serial_rwlock); buf[num + 3] = 8 + i; @@ -2588,7 +2696,7 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) goto out_put; } } else { - int len, num; + int num; if (cmd->cdb[2] != 0) { TRACE_DBG("INQUIRY: Unsupported page %x", cmd->cdb[2]); @@ -2608,37 +2716,30 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) buf[6] = 0x10; /* MultiP 1 */ buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */ + read_lock(&vdisk_serial_rwlock); + /* * 8 byte ASCII Vendor Identification of the target * - left aligned. */ - if (cmd->tgtt->vendor) - memcpy(&buf[8], cmd->tgtt->vendor, 8); - else if (virt_dev->blockio) - memcpy(&buf[8], SCST_BIO_VENDOR, 8); - else - memcpy(&buf[8], SCST_FIO_VENDOR, 8); + scst_copy_and_fill(&buf[8], virt_dev->t10_vend_id, 8); /* * 16 byte ASCII Product Identification of the target - left * aligned. */ - memset(&buf[16], ' ', 16); - if (cmd->tgtt->get_product_id) - cmd->tgtt->get_product_id(cmd->tgt_dev, &buf[16], 16); - else { - len = min_t(size_t, strlen(virt_dev->name), 16); - memcpy(&buf[16], virt_dev->name, len); - } + scst_copy_and_fill(&buf[16], virt_dev->prod_id, 16); /* * 4 byte ASCII Product Revision Level of the target - left * aligned. */ - if (cmd->tgtt->revision) - memcpy(&buf[32], cmd->tgtt->revision, 4); - else - memcpy(&buf[32], SCST_FIO_REV, 4); + scst_copy_and_fill(&buf[32], virt_dev->prod_rev_lvl, 4); + + /* Vendor specific information. */ + if (virt_dev->inq_vend_specific_len <= 20) + memcpy(&buf[36], virt_dev->inq_vend_specific, + virt_dev->inq_vend_specific_len); /** Version descriptors **/ @@ -2679,13 +2780,14 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) } /* Vendor specific information. */ - if (cmd->tgtt->get_vend_specific) { - /* Skip to byte 96. */ - num = 96 - 58; - num += cmd->tgtt->get_vend_specific(cmd->tgt_dev, - &buf[96], INQ_BUF_SZ - 96); + if (virt_dev->inq_vend_specific_len > 20) { + memcpy(&buf[96], virt_dev->inq_vend_specific, + virt_dev->inq_vend_specific_len); + num = 96 - 58 + virt_dev->inq_vend_specific_len; } + read_unlock(&vdisk_serial_rwlock); + buf[4] += num; resp_len = buf[4] + 5; } @@ -4620,6 +4722,20 @@ static int vdev_create(struct scst_dev_type *devt, "%llx-%s", dev_id_num, virt_dev->name); TRACE_DBG("t10_dev_id %s", virt_dev->t10_dev_id); + sprintf(virt_dev->t10_vend_id, "%.*s", + (int)(sizeof(virt_dev->t10_vend_id) - 1), + virt_dev->blockio ? SCST_BIO_VENDOR : SCST_FIO_VENDOR); + + sprintf(virt_dev->vend_specific_id, "%.*s", + (int)(sizeof(virt_dev->vend_specific_id) - 1), + virt_dev->t10_dev_id); + + sprintf(virt_dev->prod_id, "%.*s", (int)(sizeof(virt_dev->prod_id) - 1), + virt_dev->name); + + sprintf(virt_dev->prod_rev_lvl, "%.*s", + (int)(sizeof(virt_dev->prod_rev_lvl) - 1), SCST_FIO_REV); + scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", dev_id_num); TRACE_DBG("usn %s", virt_dev->usn); @@ -4710,6 +4826,12 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } if (!strcasecmp("filename", p)) { + if (virt_dev->filename) { + PRINT_ERROR("%s specified more than once" + " (device %s)", p, virt_dev->name); + res = -EINVAL; + goto out; + } if (*pp != '/') { PRINT_ERROR("Filename %s must be global " "(device %s)", pp, virt_dev->name); @@ -5713,6 +5835,239 @@ out: return res; } +static ssize_t vdev_sysfs_t10_vend_id_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + char *p; + int res, len; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + p = memchr(buf, '\n', count); + len = p ? p - buf : count; + + if (len >= sizeof(virt_dev->t10_vend_id)) { + PRINT_ERROR("T10 vendor id is too long (max %zd characters)", + sizeof(virt_dev->t10_vend_id)); + res = -EINVAL; + goto out; + } + + write_lock(&vdisk_serial_rwlock); + sprintf(virt_dev->t10_vend_id, "%.*s", len, buf); + virt_dev->t10_vend_id_set = 1; + write_unlock(&vdisk_serial_rwlock); + + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t vdev_sysfs_t10_vend_id_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int pos; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + pos = sprintf(buf, "%s\n%s", virt_dev->t10_vend_id, + virt_dev->t10_vend_id_set ? SCST_SYSFS_KEY_MARK "\n" : + ""); + read_unlock(&vdisk_serial_rwlock); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdev_sysfs_vend_specific_id_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + char *p; + int res, len; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + p = memchr(buf, '\n', count); + len = p ? p - buf : count; + + if (len >= sizeof(virt_dev->vend_specific_id)) { + PRINT_ERROR("Vendor specific id is too long (max %zd" + " characters)", + sizeof(virt_dev->vend_specific_id) - 1); + res = -EINVAL; + goto out; + } + + write_lock(&vdisk_serial_rwlock); + sprintf(virt_dev->vend_specific_id, "%.*s", len, buf); + virt_dev->vend_specific_id_set = 1; + write_unlock(&vdisk_serial_rwlock); + + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t vdev_sysfs_vend_specific_id_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int pos; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + pos = sprintf(buf, "%s\n%s", virt_dev->vend_specific_id, + virt_dev->vend_specific_id_set ? + SCST_SYSFS_KEY_MARK "\n" : ""); + read_unlock(&vdisk_serial_rwlock); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdev_sysfs_prod_id_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + char *p; + int res, len; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + p = memchr(buf, '\n', count); + len = p ? p - buf : count; + + if (len >= sizeof(virt_dev->prod_id)) { + PRINT_ERROR("Product id is too long (max %zd characters)", + sizeof(virt_dev->prod_id)); + res = -EINVAL; + goto out; + } + + write_lock(&vdisk_serial_rwlock); + sprintf(virt_dev->prod_id, "%.*s", len, buf); + virt_dev->prod_id_set = 1; + write_unlock(&vdisk_serial_rwlock); + + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t vdev_sysfs_prod_id_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int pos; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + pos = sprintf(buf, "%s\n%s", virt_dev->prod_id, + virt_dev->prod_id_set ? SCST_SYSFS_KEY_MARK "\n" : ""); + read_unlock(&vdisk_serial_rwlock); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdev_sysfs_prod_rev_lvl_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + char *p; + int res, len; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + p = memchr(buf, '\n', count); + len = p ? p - buf : count; + + if (len >= sizeof(virt_dev->prod_rev_lvl)) { + PRINT_ERROR("Product revision level is too long (max %zd" + " characters)", + sizeof(virt_dev->prod_rev_lvl)); + res = -EINVAL; + goto out; + } + + write_lock(&vdisk_serial_rwlock); + sprintf(virt_dev->prod_rev_lvl, "%.*s", len, buf); + virt_dev->prod_rev_lvl_set = 1; + write_unlock(&vdisk_serial_rwlock); + + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t vdev_sysfs_prod_rev_lvl_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int pos; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + pos = sprintf(buf, "%s\n%s", virt_dev->prod_rev_lvl, + virt_dev->prod_rev_lvl_set ? SCST_SYSFS_KEY_MARK "\n" : + ""); + read_unlock(&vdisk_serial_rwlock); + + TRACE_EXIT_RES(pos); + return pos; +} + static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { @@ -5853,6 +6208,55 @@ static ssize_t vdev_sysfs_usn_show(struct kobject *kobj, return pos; } +static ssize_t vdev_sysfs_inq_vend_specific_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + char *p; + int res = -EINVAL, len; + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + p = memchr(buf, '\n', count); + len = p ? p - buf : count; + if (len > MAX_INQ_VEND_SPECIFIC_LEN) + goto out; + + write_lock(&vdisk_serial_rwlock); + memcpy(virt_dev->inq_vend_specific, buf, len); + virt_dev->inq_vend_specific_len = len; + write_unlock(&vdisk_serial_rwlock); + + res = count; + +out: + return res; +} + +static ssize_t vdev_sysfs_inq_vend_specific_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int pos; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + pos = snprintf(buf, PAGE_SIZE, "%.*s\n%s", + virt_dev->inq_vend_specific_len, + virt_dev->inq_vend_specific, + virt_dev->inq_vend_specific_len ? + SCST_SYSFS_KEY_MARK "\n" : ""); + read_unlock(&vdisk_serial_rwlock); + + return pos; +} + static ssize_t vdev_zero_copy_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index c845d4f54..6c86a813f 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -4645,7 +4645,7 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, uint8_t write16_cdb[16]; struct scatterlist *sg; int sg_cnt, len = blocks << ws_cmd->dev->block_shift; - struct sgv_pool_obj *sgv; + struct sgv_pool_obj *sgv = NULL; struct scst_cmd *cmd; int64_t cur_lba; @@ -4686,7 +4686,6 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, goto set_add; } - sgv = NULL; /* we don't supply sgv */ sg = sgv_pool_alloc(ws_cmd->tgt_dev->pool, len, GFP_KERNEL, 0, &sg_cnt, &sgv, &cmd->dev->dev_mem_lim, NULL); if (sg == NULL) { @@ -5433,8 +5432,7 @@ void scst_free_cmd(struct scst_cmd *cmd) if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) TRACE_MGMT_DBG("Freeing aborted cmd %p", cmd); - EXTRACHECKS_BUG_ON(cmd->unblock_dev || cmd->dec_on_dev_needed || - cmd->dec_pr_readers_count_needed); + EXTRACHECKS_BUG_ON(cmd->unblock_dev || cmd->dec_on_dev_needed); /* * Target driver can already free sg buffer before calling @@ -6687,7 +6685,7 @@ static int get_cdb_info_write_same16(struct scst_cmd *cmd, } /** - * scst_get_cdb_info_apt() - Parse ATA PASS-THROUGH CDB. + * get_cdb_info_apt() - Parse ATA PASS-THROUGH CDB. * * Parse ATA PASS-THROUGH(12) and ATA PASS-THROUGH(16). See also SAT-3 for a * detailed description of these commands. @@ -6870,6 +6868,8 @@ int scst_get_cdb_info(struct scst_cmd *cmd) } cmd->cdb_len = SCST_GET_CDB_LEN(op); + cmd->cmd_naca = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT); + cmd->cmd_linked = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_LINK_BIT); cmd->op_name = ptr->info_op_name; cmd->data_direction = ptr->info_data_direction; cmd->op_flags = ptr->info_op_flags | SCST_INFO_VALID; @@ -8267,7 +8267,7 @@ void scst_unblock_dev(struct scst_device *dev) struct scst_cmd *cmd, *tcmd; unsigned long flags; - local_irq_save(flags); + local_irq_save_nort(flags); list_for_each_entry_safe(cmd, tcmd, &dev->blocked_cmd_list, blocked_cmd_list_entry) { bool strictly_serialized; @@ -8287,7 +8287,7 @@ void scst_unblock_dev(struct scst_device *dev) if (dev->strictly_serialized_cmd_waiting && strictly_serialized) break; } - local_irq_restore(flags); + local_irq_restore_nort(flags); dev->strictly_serialized_cmd_waiting = 0; } diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index c08378e01..e7210d433 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -1126,15 +1126,16 @@ out: /* Called under scst_mutex */ void scst_pr_clear_tgt_dev(struct scst_tgt_dev *tgt_dev) { + struct scst_device *dev = tgt_dev->dev; + struct scst_dev_registrant *reg; + struct scst_tgt_dev *t; + TRACE_ENTRY(); - if (tgt_dev->registrant != NULL) { - struct scst_dev_registrant *reg = tgt_dev->registrant; - struct scst_device *dev = tgt_dev->dev; - struct scst_tgt_dev *t; - - scst_pr_write_lock(dev); + scst_pr_write_lock(dev); + reg = tgt_dev->registrant; + if (reg) { tgt_dev->registrant = NULL; reg->tgt_dev = NULL; @@ -1155,10 +1156,10 @@ void scst_pr_clear_tgt_dev(struct scst_tgt_dev *tgt_dev) break; } } - - scst_pr_write_unlock(dev); } + scst_pr_write_unlock(dev); + TRACE_EXIT(); return; } @@ -2324,11 +2325,10 @@ bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd) struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; struct scst_dev_registrant *reg; uint8_t type; - bool unlock; TRACE_ENTRY(); - unlock = scst_pr_read_lock(cmd); + scst_pr_read_lock(dev); TRACE_DBG("Testing if command %s (0x%x) from %s allowed to execute", cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); @@ -2388,7 +2388,7 @@ bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd) cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); out_unlock: - scst_pr_read_unlock(cmd, unlock); + scst_pr_read_unlock(dev); TRACE_EXIT_RES(allowed); return allowed; diff --git a/scst/src/scst_pres.h b/scst/src/scst_pres.h index 9829d4fac..acb0e7e49 100644 --- a/scst/src/scst_pres.h +++ b/scst/src/scst_pres.h @@ -54,63 +54,6 @@ /* Persistent reservation SCOPE field */ #define SCOPE_LU 0x00 -static inline void scst_inc_pr_readers_count(struct scst_cmd *cmd, - bool locked) -{ - struct scst_device *dev = cmd->dev; - - EXTRACHECKS_BUG_ON(cmd->dec_pr_readers_count_needed); - - if (!locked) - spin_lock_bh(&dev->dev_lock); - -#ifdef CONFIG_SMP - EXTRACHECKS_BUG_ON(!spin_is_locked(&dev->dev_lock)); -#endif - - dev->pr_readers_count++; - cmd->dec_pr_readers_count_needed = 1; - TRACE_DBG("New inc pr_readers_count %d (cmd %p)", dev->pr_readers_count, - cmd); - - if (!locked) - spin_unlock_bh(&dev->dev_lock); - return; -} - -static inline void scst_dec_pr_readers_count(struct scst_cmd *cmd, - bool locked) -{ - struct scst_device *dev = cmd->dev; - - if (unlikely(!cmd->dec_pr_readers_count_needed)) { - PRINT_ERROR("__scst_check_local_events(x, false) should not " - "be called twice (cmd %p, op %x)! Use " - "scst_check_local_events() instead.", cmd, cmd->cdb[0]); - WARN_ON(1); - goto out; - } - - if (!locked) - spin_lock_bh(&dev->dev_lock); - -#ifdef CONFIG_SMP - EXTRACHECKS_BUG_ON(!spin_is_locked(&dev->dev_lock)); -#endif - - dev->pr_readers_count--; - cmd->dec_pr_readers_count_needed = 0; - TRACE_DBG("New dec pr_readers_count %d (cmd %p)", dev->pr_readers_count, - cmd); - - if (!locked) - spin_unlock_bh(&dev->dev_lock); - -out: - EXTRACHECKS_BUG_ON(dev->pr_readers_count < 0); - return; -} - static inline bool scst_pr_type_valid(uint8_t type) { switch (type) { @@ -126,73 +69,24 @@ static inline bool scst_pr_type_valid(uint8_t type) } } -static inline bool scst_pr_read_lock(struct scst_cmd *cmd) +static inline void scst_pr_read_lock(struct scst_device *dev) { - struct scst_device *dev = cmd->dev; - bool unlock = false; - - TRACE_ENTRY(); - - smp_mb(); /* to sync with scst_pr_write_lock() */ - if (unlikely(dev->pr_writer_active)) { - unlock = true; - scst_dec_pr_readers_count(cmd, false); - mutex_lock(&dev->dev_pr_mutex); - } - - TRACE_EXIT_RES(unlock); - return unlock; + mutex_lock(&dev->dev_pr_mutex); } -static inline void scst_pr_read_unlock(struct scst_cmd *cmd, bool unlock) +static inline void scst_pr_read_unlock(struct scst_device *dev) { - struct scst_device *dev = cmd->dev; - - TRACE_ENTRY(); - - if (unlikely(unlock)) - mutex_unlock(&dev->dev_pr_mutex); - else - scst_dec_pr_readers_count(cmd, false); - - TRACE_EXIT(); - return; + mutex_unlock(&dev->dev_pr_mutex); } static inline void scst_pr_write_lock(struct scst_device *dev) { - TRACE_ENTRY(); - mutex_lock(&dev->dev_pr_mutex); - - dev->pr_writer_active = 1; - /* to sync with scst_pr_read_lock() and unlock() */ - smp_mb(); - - while (true) { - int readers; - spin_lock_bh(&dev->dev_lock); - readers = dev->pr_readers_count; - spin_unlock_bh(&dev->dev_lock); - if (readers == 0) - break; - TRACE_DBG("Waiting for %d readers (dev %p)", readers, dev); - msleep(1); - } - - TRACE_EXIT(); - return; } static inline void scst_pr_write_unlock(struct scst_device *dev) { - TRACE_ENTRY(); - - dev->pr_writer_active = 0; mutex_unlock(&dev->dev_pr_mutex); - - TRACE_EXIT(); - return; } int scst_pr_init_dev(struct scst_device *dev); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 74d041000..f56d185be 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -120,6 +120,26 @@ extern unsigned long scst_trace_flag; #define SCST_MAX_EACH_INTERNAL_IO_SIZE (128*1024) #define SCST_MAX_IN_FLIGHT_INTERNAL_COMMANDS 32 +/* + * Compatibility with real-time (CONFIG_PREEMPT_RT_FULL) kernels. + * In such kernels: + * - Interrupt handlers run in kernel thread context (see e.g. + * http://lwn.net/Articles/302043/). + * - spin_lock() calls can sleep (see e.g. http://lwn.net/Articles/271817/). + * - local_irq functions manipulate preemptibility, not HW interruptibility + * (see also http://lwn.net/Articles/146861). + * For the upstream kernels up to at least kernel 3.14 _nort functions are + * only defined if a CONFIG PREEMPT RT patch has been applied to the kernel. + * See https://rt.wiki.kernel.org/index.php/CONFIG_PREEMPT_RT_Patch. + */ +#ifndef local_irq_enable_nort +/* Kernel does not have CONFIG_PREEMPT_RT patch */ +#define local_irq_enable_nort() local_irq_enable() +#define local_irq_disable_nort() local_irq_disable() +#define local_irq_save_nort(flags) local_irq_save(flags) +#define local_irq_restore_nort(flags) local_irq_restore(flags) +#endif + typedef void (*scst_i_finish_fn_t) (struct scst_cmd *cmd); extern struct mutex scst_mutex2; diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 7f4a89d37..c7e08ab7d 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -1185,11 +1185,6 @@ static void scst_tgt_release(struct kobject *kobj) return; } -static struct kobj_type tgt_ktype = { - .sysfs_ops = &scst_sysfs_ops, - .release = scst_tgt_release, -}; - static int __scst_process_luns_mgmt_store(char *buffer, struct scst_tgt *tgt, struct scst_acg *acg, bool tgt_kobj) { @@ -2405,6 +2400,97 @@ out: } EXPORT_SYMBOL(scst_create_tgt_attr); +#define SCST_TGT_SYSFS_STAT_ATTR(member_name, attr, dir, result_op) \ +static int scst_tgt_sysfs_##attr##_show_work_fn( \ + struct scst_sysfs_work_item *work) \ +{ \ + struct scst_tgt *tgt = work->tgt; \ + struct scst_session *sess; \ + int res; \ + uint64_t c = 0; \ + \ + BUILD_BUG_ON((unsigned)(dir) >= ARRAY_SIZE(sess->io_stats)); \ + \ + res = mutex_lock_interruptible(&scst_mutex); \ + if (res) \ + goto out; \ + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) \ + c += sess->io_stats[(dir)].member_name; \ + mutex_unlock(&scst_mutex); \ + \ + work->res_buf = kasprintf(GFP_KERNEL, "%llu\n", c result_op); \ + res = work->res_buf ? 0 : -ENOMEM; \ + \ +out: \ + kobject_put(&tgt->tgt_kobj); \ + return res; \ +} \ + \ +static ssize_t scst_tgt_sysfs_##attr##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + char *buf) \ +{ \ + struct scst_tgt *tgt = \ + container_of(kobj, struct scst_tgt, tgt_kobj); \ + struct scst_sysfs_work_item *work; \ + int res; \ + \ + res = scst_alloc_sysfs_work(scst_tgt_sysfs_##attr##_show_work_fn, \ + true, &work); \ + if (res) \ + goto out; \ + \ + work->tgt = tgt; \ + SCST_SET_DEP_MAP(work, &scst_tgt_dep_map); \ + kobject_get(&tgt->tgt_kobj); \ + scst_sysfs_work_get(work); \ + res = scst_sysfs_queue_wait_work(work); \ + if (res == 0) \ + res = scnprintf(buf, PAGE_SIZE, "%s", work->res_buf); \ + scst_sysfs_work_put(work); \ + \ +out: \ + return res; \ +} \ + \ +static struct kobj_attribute scst_tgt_##attr##_attr = \ + __ATTR(attr, S_IRUGO, scst_tgt_sysfs_##attr##_show, NULL); + +SCST_TGT_SYSFS_STAT_ATTR(cmd_count, unknown_cmd_count, SCST_DATA_UNKNOWN, >> 0); +SCST_TGT_SYSFS_STAT_ATTR(cmd_count, write_cmd_count, SCST_DATA_WRITE, >> 0); +SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, write_io_count_kb, SCST_DATA_WRITE, + >> 10); +SCST_TGT_SYSFS_STAT_ATTR(cmd_count, read_cmd_count, SCST_DATA_READ, >> 0); +SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, read_io_count_kb, SCST_DATA_READ, + >> 10); +SCST_TGT_SYSFS_STAT_ATTR(cmd_count, bidi_cmd_count, SCST_DATA_BIDI, >> 0); +SCST_TGT_SYSFS_STAT_ATTR(io_byte_count, bidi_io_count_kb, SCST_DATA_BIDI, + >> 10); +SCST_TGT_SYSFS_STAT_ATTR(cmd_count, none_cmd_count, SCST_DATA_NONE, >> 0); + +static struct attribute *scst_tgt_attrs[] = { + &scst_rel_tgt_id.attr, + &scst_tgt_comment.attr, + &scst_tgt_addr_method.attr, + &scst_tgt_io_grouping_type.attr, + &scst_tgt_cpu_mask.attr, + &scst_tgt_unknown_cmd_count_attr.attr, + &scst_tgt_write_cmd_count_attr.attr, + &scst_tgt_write_io_count_kb_attr.attr, + &scst_tgt_read_cmd_count_attr.attr, + &scst_tgt_read_io_count_kb_attr.attr, + &scst_tgt_bidi_cmd_count_attr.attr, + &scst_tgt_bidi_io_count_kb_attr.attr, + &scst_tgt_none_cmd_count_attr.attr, + NULL, +}; + +static struct kobj_type tgt_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_tgt_release, + .default_attrs = scst_tgt_attrs, +}; + /* * Supposed to be called under scst_mutex. In case of error will drop, * then reacquire it. @@ -2468,45 +2554,6 @@ int scst_tgt_sysfs_create(struct scst_tgt *tgt) goto out_err; } - res = sysfs_create_file(&tgt->tgt_kobj, - &scst_rel_tgt_id.attr); - if (res != 0) { - PRINT_ERROR("Can't add attribute %s for tgt %s", - scst_rel_tgt_id.attr.name, tgt->tgt_name); - goto out_err; - } - - res = sysfs_create_file(&tgt->tgt_kobj, - &scst_tgt_comment.attr); - if (res != 0) { - PRINT_ERROR("Can't add attribute %s for tgt %s", - scst_tgt_comment.attr.name, tgt->tgt_name); - goto out_err; - } - - res = sysfs_create_file(&tgt->tgt_kobj, - &scst_tgt_addr_method.attr); - if (res != 0) { - PRINT_ERROR("Can't add attribute %s for tgt %s", - scst_tgt_addr_method.attr.name, tgt->tgt_name); - goto out_err; - } - - res = sysfs_create_file(&tgt->tgt_kobj, - &scst_tgt_io_grouping_type.attr); - if (res != 0) { - PRINT_ERROR("Can't add attribute %s for tgt %s", - scst_tgt_io_grouping_type.attr.name, tgt->tgt_name); - goto out_err; - } - - res = sysfs_create_file(&tgt->tgt_kobj, &scst_tgt_cpu_mask.attr); - if (res != 0) { - PRINT_ERROR("Can't add attribute %s for tgt %s", - scst_tgt_cpu_mask.attr.name, tgt->tgt_name); - goto out_err; - } - if (tgt->tgtt->tgt_attrs) { res = sysfs_create_files(&tgt->tgt_kobj, tgt->tgtt->tgt_attrs); if (res != 0) { diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 35be2e746..697083bf6 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -119,8 +119,6 @@ static bool scst_check_blocked_dev(struct scst_cmd *cmd) cmd->dec_on_dev_needed = 1; TRACE_DBG("New inc on_dev_count %d (cmd %p)", dev->on_dev_cmd_count, cmd); - scst_inc_pr_readers_count(cmd, true); - if (unlikely(dev->block_count > 0) || unlikely(dev->dev_double_ua_possible) || unlikely((cmd->op_flags & SCST_SERIALIZED) != 0)) @@ -134,8 +132,6 @@ static bool scst_check_blocked_dev(struct scst_cmd *cmd) cmd->dec_on_dev_needed = 0; TRACE_DBG("New dec on_dev_count %d (cmd %p)", dev->on_dev_cmd_count, cmd); - - scst_dec_pr_readers_count(cmd, true); } spin_unlock_bh(&dev->dev_lock); @@ -159,9 +155,6 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd) dev->on_dev_cmd_count, cmd); } - if (unlikely(cmd->dec_pr_readers_count_needed)) - scst_dec_pr_readers_count(cmd, true); - if (unlikely(cmd->unblock_dev)) { TRACE_BLOCK("cmd %p (tag %llu): unblocking dev %s", cmd, (long long unsigned int)cmd->tag, dev->virt_name); @@ -744,7 +737,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) cmd->op_flags &= ~SCST_UNKNOWN_LENGTH; } - if (unlikely(cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT)) { + if (unlikely(cmd->cmd_naca)) { PRINT_ERROR("NACA bit in control byte CDB is not supported " "(opcode 0x%02x)", cmd->cdb[0]); scst_set_cmd_error(cmd, @@ -752,7 +745,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) goto out_done; } - if (unlikely(cmd->cdb[cmd->cdb_len-1] & CONTROL_BYTE_LINK_BIT)) { + if (unlikely(cmd->cmd_linked)) { PRINT_ERROR("Linked commands are not supported " "(opcode 0x%02x)", cmd->cdb[0]); scst_set_invalid_field_in_cdb(cmd, cmd->cdb_len-1, @@ -948,23 +941,34 @@ set_res: out: #ifdef CONFIG_SCST_EXTRACHECKS - /* - * At this point either both lba and data_len must be initialized to - * at least 0 for not data transfer commands, or cmd must be - * completed (with an error) and have correct state set. - */ - if (unlikely((((cmd->lba == SCST_DEF_LBA_DATA_LEN) && - !(cmd->op_flags & SCST_LBA_NOT_VALID)) || - (cmd->data_len == SCST_DEF_LBA_DATA_LEN)) && - (!cmd->completed || - (((cmd->state < SCST_CMD_STATE_PRE_XMIT_RESP) || - (cmd->state >= SCST_CMD_STATE_LAST_ACTIVE)) && - (cmd->state != SCST_CMD_STATE_PREPROCESSING_DONE))))) { - PRINT_CRIT_ERROR("Not initialized data_len for going to " - "execute command or bad state (cmd %p, data_len %lld, " - "completed %d, state %d)", cmd, - (long long)cmd->data_len, cmd->completed, cmd->state); - sBUG(); + if (unlikely(cmd->completed)) { + /* Command completed with error */ + bool valid_state = (cmd->state == SCST_CMD_STATE_PREPROCESSING_DONE) || + ((cmd->state >= SCST_CMD_STATE_PRE_XMIT_RESP) && + (cmd->state < SCST_CMD_STATE_LAST_ACTIVE)); + + if (!valid_state) { + PRINT_CRIT_ERROR("Bad state for completed cmd " + "(cmd %p, state %d)", cmd, cmd->state); + sBUG(); + } + } else if (cmd->state != SCST_CMD_STATE_PARSE) { + /* + * Ready to execute. At this point both lba and data_len must + * be initialized or marked non-applicable. + */ + bool bad_lba = (cmd->lba == SCST_DEF_LBA_DATA_LEN) && + !(cmd->op_flags & SCST_LBA_NOT_VALID); + bool bad_data_len = (cmd->data_len == SCST_DEF_LBA_DATA_LEN); + + if (unlikely(bad_lba || bad_data_len)) { + PRINT_CRIT_ERROR("Uninitialized lba or data_len for " + "ready-to-execute command (cmd %p, lba %lld, " + "data_len %lld, state %d)", cmd, + (long long)cmd->lba, (long long)cmd->data_len, + cmd->state); + sBUG(); + } } #endif @@ -2211,7 +2215,7 @@ static int scst_persistent_reserve_in_local(struct scst_cmd *cmd) if (unlikely(buffer_size <= 0)) goto out_done; - scst_pr_write_lock(dev); + scst_pr_read_lock(dev); /* We can be aborted by another PR command while waiting for the lock */ if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) { @@ -2246,7 +2250,7 @@ out_complete: cmd->completed = 1; out_unlock: - scst_pr_write_unlock(dev); + scst_pr_read_unlock(dev); scst_put_buf_full(cmd, buffer); @@ -2303,6 +2307,12 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) goto out_done; } + buffer_size = scst_get_buf_full_sense(cmd, &buffer); + if (unlikely(buffer_size <= 0)) + goto out_done; + + scst_pr_write_lock(dev); + /* * Check if tgt_dev already registered. Also by this check we make * sure that table "PERSISTENT RESERVE OUT service actions that are @@ -2314,20 +2324,16 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) (tgt_dev->registrant == NULL)) { TRACE_PR("'%s' not registered", cmd->sess->initiator_name); scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); - goto out_done; + goto out_unlock; } - buffer_size = scst_get_buf_full_sense(cmd, &buffer); - if (unlikely(buffer_size <= 0)) - goto out_done; - /* Check scope */ if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && (action != PR_CLEAR) && (cmd->cdb[2] >> 4) != SCOPE_LU) { TRACE_PR("Scope must be SCOPE_LU for action %x", action); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); - goto out_put_buf_full; + goto out_unlock; } /* Check SPEC_I_PT (PR_REGISTER_AND_MOVE has another format) */ @@ -2336,7 +2342,7 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) TRACE_PR("SPEC_I_PT must be zero for action %x", action); scst_set_cmd_error(cmd, SCST_LOAD_SENSE( scst_sense_invalid_field_in_cdb)); - goto out_put_buf_full; + goto out_unlock; } /* Check ALL_TG_PT (PR_REGISTER_AND_MOVE has another format) */ @@ -2345,11 +2351,9 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) TRACE_PR("ALL_TG_PT must be zero for action %x", action); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); - goto out_put_buf_full; + goto out_unlock; } - scst_pr_write_lock(dev); - /* We can be aborted by another PR command while waiting for the lock */ aborted = test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); if (unlikely(aborted)) { @@ -2400,7 +2404,6 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) out_unlock: scst_pr_write_unlock(dev); -out_put_buf_full: scst_put_buf_full(cmd, buffer); out_done: @@ -2438,7 +2441,6 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only) /* * The original command passed all checks and not finished yet */ - sBUG_ON(cmd->dec_pr_readers_count_needed); res = 0; goto out; } @@ -2455,7 +2457,7 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only) if ((cmd->op_flags & SCST_REG_RESERVE_ALLOWED) == 0) { scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); - goto out_dec_pr_readers_count; + goto out_complete; } } @@ -2466,8 +2468,7 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only) SAM_STAT_RESERVATION_CONFLICT); goto out_complete; } - } else - scst_dec_pr_readers_count(cmd, false); + } } /* @@ -2521,10 +2522,6 @@ out: TRACE_EXIT_RES(res); return res; -out_dec_pr_readers_count: - if (cmd->dec_pr_readers_count_needed) - scst_dec_pr_readers_count(cmd, false); - out_complete: res = 1; sBUG_ON(!cmd->completed); @@ -5163,7 +5160,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, continue; spin_lock_bh(&dev->dev_lock); - local_irq_disable(); + local_irq_disable_nort(); list_for_each_entry_safe(cmd, tcmd, &dev->blocked_cmd_list, blocked_cmd_list_entry) { @@ -5177,10 +5174,10 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, TRACE_MGMT_DBG("Unblock aborted blocked cmd %p", cmd); } } - local_irq_enable(); + local_irq_enable_nort(); spin_unlock_bh(&dev->dev_lock); - local_irq_disable(); + local_irq_disable_nort(); list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { struct scst_order_data *order_data = tgt_dev->curr_order_data; @@ -5203,7 +5200,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, } spin_unlock(&order_data->sn_lock); } - local_irq_enable(); + local_irq_enable_nort(); } if (!scst_mutex_held) @@ -6241,7 +6238,7 @@ static int scst_post_rx_mgmt_cmd(struct scst_session *sess, sBUG(); } - local_irq_save(flags); + local_irq_save_nort(flags); spin_lock(&sess->sess_list_lock); @@ -6270,7 +6267,7 @@ static int scst_post_rx_mgmt_cmd(struct scst_session *sess, list_add_tail(&mcmd->mgmt_cmd_list_entry, &scst_active_mgmt_cmd_list); spin_unlock(&scst_mcmd_lock); - local_irq_restore(flags); + local_irq_restore_nort(flags); wake_up(&scst_mgmt_cmd_list_waitQ); @@ -6280,7 +6277,7 @@ out: out_unlock: spin_unlock(&sess->sess_list_lock); - local_irq_restore(flags); + local_irq_restore_nort(flags); goto out; } diff --git a/scst_local/Makefile b/scst_local/Makefile index 7d7e318e2..2dee7552a 100644 --- a/scst_local/Makefile +++ b/scst_local/Makefile @@ -2,10 +2,6 @@ # A Makefile for the scst-local ... # -ifndef PREFIX - PREFIX=/usr/local -endif - SHELL=/bin/bash KMOD := $(shell pwd)/kernel @@ -24,24 +20,40 @@ EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions +ifneq ($(PATCHLEVEL),) +obj-m := scst_local.o +else +######### BEGIN OUT-OF-TREE RULES ######### + +ifndef PREFIX + PREFIX=/usr/local +endif + ifeq ($(KVER),) ifeq ($(KDIR),) - KDIR := /lib/modules/$(shell uname -r)/build + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build + else + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) endif else KDIR := /lib/modules/$(KVER)/build endif -ifneq ($(PATCHLEVEL),) -obj-m := scst_local.o -else +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra SCST_INC_DIR := $(shell if [ -e "$$PWD/../scst" ]; \ then echo "$$PWD/../scst/include"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) SCST_DIR := $(shell if [ -e "$$PWD/../scst" ]; \ then echo "$$PWD/../scst/src"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) all: Modules.symvers Module.symvers $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ @@ -50,7 +62,6 @@ all: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ SCST_INC_DIR=$(SCST_INC_DIR) modules_install - -/sbin/depmod -aq $(KVER) SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) ifneq ($(SCST_MOD_VERS),) @@ -71,7 +82,9 @@ endif uninstall: rm -f $(INSTALL_DIR)/scst_local.ko - -/sbin/depmod -a $(KVER) + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) + +########## END OUT-OF-TREE RULES ########## endif clean: diff --git a/scst_local/in-tree/Makefile-3.13 b/scst_local/in-tree/Makefile-3.13 new file mode 100644 index 000000000..8cbbbff63 --- /dev/null +++ b/scst_local/in-tree/Makefile-3.13 @@ -0,0 +1,2 @@ +obj-$(CONFIG_SCST_LOCAL) += scst_local.o + diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index ed46a8527..5953ae38e 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -375,9 +375,10 @@ static ssize_t scst_local_stats_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "Aborts: %d, Device Resets: %d, Target Resets: %d", - atomic_read(&num_aborts), atomic_read(&num_dev_resets), - atomic_read(&num_target_resets)); + return sprintf(buf, + "Aborts: %d, Device Resets: %d, Target Resets: %d\n", + atomic_read(&num_aborts), atomic_read(&num_dev_resets), + atomic_read(&num_target_resets)); } static struct kobj_attribute scst_local_stats_attr = @@ -960,6 +961,14 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt, TRACE_DBG("lun %d, cmd: 0x%02X", SCpnt->device->lun, SCpnt->cmnd[0]); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + /* + * We save a pointer to the done routine in SCpnt->scsi_done and + * we save that as tgt specific stuff below. + */ + SCpnt->scsi_done = done; +#endif + sess = to_scst_lcl_sess(scsi_get_device(SCpnt->device->host)); if (sess->unregistering) { @@ -983,12 +992,6 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt, } tgt_specific->cmnd = SCpnt; tgt_specific->done = done; -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) - /* - * We save a pointer to the done routine in SCpnt->scsi_done and - * we save that as tgt specific stuff below. - */ - SCpnt->scsi_done = done; #endif /* diff --git a/scstadmin/Makefile b/scstadmin/Makefile index 53821b3a8..5d7b1be0a 100644 --- a/scstadmin/Makefile +++ b/scstadmin/Makefile @@ -136,7 +136,6 @@ rpm: rpmtopdir="$$(if [ $$(id -u) = 0 ]; then echo /usr/src/packages;\ else echo $$PWD/rpmbuilddir; fi)" && \ $(MAKE) dist-gzip && \ - rm -rf $${rpmtopdir} && \ for d in BUILD RPMS SOURCES SPECS SRPMS; do \ mkdir -p $${rpmtopdir}/$$d; \ done && \ diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf index 9bcda73e5..5e248d128 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf @@ -14,7 +14,7 @@ TARGET_DRIVER scst_local { LUN 0 disk01 LUN 1 disk01 - GROUP initator_group { + GROUP initiator_group { LUN 0 disk01 LUN 1 disk01 { read_only 1 diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf index c5c3d6320..b4988b721 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/to-be-restored.conf @@ -20,7 +20,7 @@ TARGET_DRIVER scst_local { LUN 0 disk01 LUN 1 disk01 - GROUP initator_group { + GROUP initiator_group { LUN 0 disk01 LUN 1 disk01 { read_only 1 diff --git a/srpt/Makefile b/srpt/Makefile index 046026325..3a5c1c8c7 100644 --- a/srpt/Makefile +++ b/srpt/Makefile @@ -7,23 +7,35 @@ endif SCST_INC_DIR := $(shell if [ -e "$$PWD/../scst" ]; \ then echo "$$PWD/../scst/include"; \ - else echo "$(PREFIX)/include/scst"; fi) + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) SCST_SYMVERS_DIR := $(shell if [ -e $$PWD/../scst ]; then \ echo $$PWD/../scst/src; \ - else echo $(PREFIX)/include/scst; fi) + else echo $(DESTDIR)$(PREFIX)/include/scst; fi) SUBDIRS := $(shell pwd) ifeq ($(KVER),) ifeq ($(KDIR),) - KVER = $(shell uname -r) - KDIR ?= /lib/modules/$(KVER)/build + KVER := $(shell uname -r) + KDIR := /lib/modules/$(KVER)/build else - KVER = $$KERNELRELEASE + ifeq ($(KERNELRELEASE),) + KVER := $(strip $(shell \ + cat $(KDIR)/include/config/kernel.release 2>/dev/null || \ + make -s -C $(KDIR) kernelversion)) + else + KVER := $(KERNELRELEASE) + endif endif else - KDIR ?= /lib/modules/$(KVER)/build + KDIR := /lib/modules/$(KVER)/build endif +ifeq ($(INSTALL_MOD_PATH),) + export INSTALL_MOD_PATH := $(DESTDIR) +endif + +INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra + # Set variable $(2) to value $(3) in file $(1). set_var = $(shell { if [ -e "$(1)" ]; then grep -v '^$(2)=' "$(1)"; fi; echo "$(2)=$(3)"; } >/tmp/$(1)-$$$$.tmp && mv /tmp/$(1)-$$$$.tmp $(1)) @@ -59,10 +71,13 @@ all: src/$(MODULE_SYMVERS) PRE_CFLAGS="$(OFED_CFLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) modules install: all src/ib_srpt.ko - @eval `sed -n 's/#define UTS_RELEASE /KERNELRELEASE=/p' $(KDIR)/include/linux/version.h $(KDIR)/include/linux/utsrelease.h 2>/dev/null`; \ - install -vD -m 644 src/ib_srpt.ko \ - $(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/ib_srpt.ko - -/sbin/depmod -aq $(KVER) + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src \ + PRE_CFLAGS="$(OFED_CFLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) \ + modules_install + +uninstall: + rm -f $(INSTALL_DIR)/ib_srpt.ko + -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) @if $(OFED_KERNEL_IB_RPM_INSTALLED); then \ diff --git a/srpt/patches/kernel-3.13-pre-cflags.patch b/srpt/patches/kernel-3.13-pre-cflags.patch new file mode 100644 index 000000000..3964ee179 --- /dev/null +++ b/srpt/patches/kernel-3.13-pre-cflags.patch @@ -0,0 +1,12 @@ +diff --git a/Makefile b/Makefile +index 540f7b2..078307f 100644 +--- a/Makefile ++++ b/Makefile +@@ -361,6 +361,7 @@ USERINCLUDE := \ + # Use LINUXINCLUDE when you must reference the include/ directory. + # Needed to be compatible with the O= option + LINUXINCLUDE := \ ++ $(PRE_CFLAGS) \ + -I$(srctree)/arch/$(hdr-arch)/include \ + -Iarch/$(hdr-arch)/include/generated \ + $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 67c8c6424..6b9b54fa8 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -2340,17 +2340,21 @@ static void srpt_drain_channel(struct ib_cm_id *cm_id) static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) { - struct srpt_rdma_ch *ch, *next_ch; + struct srpt_rdma_ch *ch; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&srpt_tgt->spinlock); #endif - list_for_each_entry_safe(ch, next_ch, &srpt_tgt->rch_list, list) { +restart: + list_for_each_entry(ch, &srpt_tgt->rch_list, list) { + if (ch->state >= CH_DISCONNECTING) + continue; PRINT_INFO("Closing channel %s because target %s has been" " disabled", ch->sess_name, srpt_tgt->scst_tgt->tgt_name); - __srpt_close_ch(ch); + WARN_ON_ONCE(!__srpt_close_ch(ch)); + goto restart; } } diff --git a/usr/fileio/Makefile b/usr/fileio/Makefile index 5cdb3e3ed..6b193e219 100644 --- a/usr/fileio/Makefile +++ b/usr/fileio/Makefile @@ -28,7 +28,7 @@ OBJS_F = $(SRCS_F:.c=.o) SCST_INC_DIR := ../../scst/include #SCST_INC_DIR := $(PREFIX)/include/scst -INSTALL_DIR := $(PREFIX)/bin/scst +INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \ -I$(SCST_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \ diff --git a/www/handler_fileio_tgt.html b/www/handler_fileio_tgt.html index c6c9a56b0..f1e1c52c4 100644 --- a/www/handler_fileio_tgt.html +++ b/www/handler_fileio_tgt.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/scst_admin.html b/www/scst_admin.html index 51735b0c0..fe75d019d 100644 --- a/www/scst_admin.html +++ b/www/scst_admin.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_emulex.html b/www/target_emulex.html index 020666203..9d5668bca 100644 --- a/www/target_emulex.html +++ b/www/target_emulex.html @@ -6,7 +6,7 @@ -Emulex lpfc FC/FCoE target driver +Emulex FC/FCoE target driver @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • @@ -56,17 +56,17 @@
                            -

                            Target driver for Emulex lpfc FC/FCoE adapters

                            +

                            Target driver for Emulex FC/FCoE adapters

                            SCST Emulex -

                            The ocs_fc_scst target driver for Emulex lpfc FC/FCoE adapters is developed by the Emulex team - and is available at the Emulex OneCore SDK - developer site. Register for free at the Developer Portal Access +

                            The ocs_fc_scst target driver for Emulex FC/FCoE adapters is developed by the Emulex team + and is available at the Emulex OneCore SDK + developer site. Register for free at the Developer Portal Access page to get the source code and documentation. The Emulex OneCore SDK is built around the Emulex SLI-4 (Service Level Interface) API and is compatible with Emulex 16 Gb/s Fibre Channel HBAs (LPe16000 series) and Ethernet based target mode FCoE UCNAs (OCe11102-F series).

                            -

                            The ocs_fc_scst driver allows for lpfc adapters to be placed in initiator and/or target mode. +

                            The ocs_fc_scst driver allows for Emulex adapters to be placed in initiator and/or target mode. The driver effectively maps SCST to the Emulex SLI-4 interface allowing a simple transition to Emulex 16Gb/s fibre channel technology for existing or new SCST users. NPIV is also supported, allowing virtual ports to be created with individual SCST target instances bound to them. @@ -79,7 +79,7 @@

                             
                            diff --git a/www/target_fcoe.html b/www/target_fcoe.html index 732ebfd3f..cdf4ae8f8 100644 --- a/www/target_fcoe.html +++ b/www/target_fcoe.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_ibmvscsi.html b/www/target_ibmvscsi.html index 2d4ffa74d..6b01c9621 100644 --- a/www/target_ibmvscsi.html +++ b/www/target_ibmvscsi.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_iscsi.html b/www/target_iscsi.html index a683a1889..3a7c7ecd0 100644 --- a/www/target_iscsi.html +++ b/www/target_iscsi.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_iser.html b/www/target_iser.html index 72d237ba2..cc9448569 100644 --- a/www/target_iser.html +++ b/www/target_iser.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_local.html b/www/target_local.html index acd8fc022..dffa4e96b 100644 --- a/www/target_local.html +++ b/www/target_local.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_lsi.html b/www/target_lsi.html index 96ec00043..1f8ab013b 100644 --- a/www/target_lsi.html +++ b/www/target_lsi.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_mvsas.html b/www/target_mvsas.html index 2e7fd2722..4941e8751 100644 --- a/www/target_mvsas.html +++ b/www/target_mvsas.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_old.html b/www/target_old.html index 78da83f27..6de51239b 100644 --- a/www/target_old.html +++ b/www/target_old.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_qla2x00t.html b/www/target_qla2x00t.html index 9f992ccdf..d804fdbf1 100644 --- a/www/target_qla2x00t.html +++ b/www/target_qla2x00t.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/target_srp.html b/www/target_srp.html index 2a7326cdc..53ae21f5e 100644 --- a/www/target_srp.html +++ b/www/target_srp.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • diff --git a/www/targets.html b/www/targets.html index f0eefddcb..892d9afdc 100644 --- a/www/targets.html +++ b/www/targets.html @@ -41,7 +41,7 @@
                          • QLogic FC qla2x00t
                          • SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters
                          • FCoE Target
                          • Local Target Driver
                          • @@ -64,7 +64,7 @@
                          • Fibre Channel QLogic qla2xxx series
                          • Infiniband SCSI RDMA Protocol (SRP)
                          • Marvell SAS adapters
                          • -
                          • Emulex lpfc FC/FCoE adapters
                          • +
                          • Emulex FC/FCoE adapters
                          • LSI/MPT adapters (parallel SCSI, including Wide Ultra320, SAS, Fibre Channel)
                          • FCoE
                          • Local access
                          • From e900e70a068fcd37e90342be3dd892b779ece75c Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 13 Mar 2014 14:48:33 +0000 Subject: [PATCH 036/128] isert: Fix race between target_del_all() and iscsit_unregister_transport() When iscsi-scstd exits for some reason, release on character device ctr_name is called. The release function of this character device calls target_del_all(). In order to avoid a scenario where isert-scst is being removed at the same time as iscsi-scstd is closing ctr_name device, increment reference count in portal creation so that the module will not disappear before target_del_all() finishes using it. This scenario is only relevant for isert-scst, as iscsi-scst will have positive reference count at least untill target_del_all() finishes due to __fput() being called only after the release method of the character device has returned. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5327 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 25 ++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 28f164211..030c923e9 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1393,10 +1393,17 @@ struct isert_portal *isert_portal_create(void) struct rdma_cm_id *cm_id; int err; + if (!try_module_get(THIS_MODULE)) { + pr_err("Unable increment module reference\n"); + portal = ERR_PTR(-EINVAL); + goto out; + } + portal = kzalloc(sizeof(*portal), GFP_KERNEL); - if (!portal) { + if (unlikely(!portal)) { pr_err("Unable to allocate struct portal\n"); - return ERR_PTR(-ENOMEM); + portal = ERR_PTR(-ENOMEM); + goto err_alloc; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) && !defined(RHEL_MAJOR) @@ -1405,10 +1412,10 @@ struct isert_portal *isert_portal_create(void) cm_id = rdma_create_id(isert_cm_evt_handler, portal, RDMA_PS_TCP, IB_QPT_RC); #endif - if (IS_ERR(cm_id)) { + if (unlikely(IS_ERR(cm_id))) { err = PTR_ERR(cm_id); pr_err("Failed to create rdma id, err:%d\n", err); - return ERR_PTR(err); + goto create_id_err; } portal->cm_id = cm_id; @@ -1420,7 +1427,15 @@ struct isert_portal *isert_portal_create(void) #endif pr_info("Created iser portal cm_id:%p\n", cm_id); +out: return portal; + +create_id_err: + kfree(portal); + portal = ERR_PTR(err); +err_alloc: + module_put(THIS_MODULE); + goto out; } int isert_portal_listen(struct isert_portal *portal, @@ -1495,6 +1510,8 @@ void isert_portal_release(struct isert_portal *portal) mutex_unlock(&dev_list_mutex); isert_portal_list_remove(portal); + + module_put(THIS_MODULE); } struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len) From 1807512bdab27363bf24bb56f043abd6f44eafb4 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 13 Mar 2014 14:48:48 +0000 Subject: [PATCH 037/128] isert: Remove unused code Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5328 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 1 - iscsi-scst/kernel/isert-scst/iser_datamover.c | 11 ----------- iscsi-scst/kernel/isert-scst/iser_datamover.h | 1 - iscsi-scst/kernel/isert-scst/iser_rdma.c | 14 -------------- 4 files changed, 27 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index a2266a009..8141ebcfd 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -215,7 +215,6 @@ int isert_portal_listen(struct isert_portal *portal, void isert_portal_release(struct isert_portal *portal); void isert_portal_list_release_all(void); struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len); -struct isert_portal *isert_portal_add_addr_any(u16 port); /* iser connection */ int isert_post_recv(struct isert_connection *isert_conn, diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.c b/iscsi-scst/kernel/isert-scst/iser_datamover.c index 2dcbf9364..f46e7285e 100644 --- a/iscsi-scst/kernel/isert-scst/iser_datamover.c +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.c @@ -90,17 +90,6 @@ out: return ret; } -void create_sockaddr_any(struct sockaddr *sa, u16 port, size_t *addr_len) -{ - struct sockaddr_in *sa_any = (struct sockaddr_in *)sa; - - memset(sa_any, 0, sizeof(*sa_any)); - sa_any->sin_family = AF_INET; - sa_any->sin_port = cpu_to_be16(port); - sa_any->sin_addr.s_addr = cpu_to_be32(INADDR_ANY); - *addr_len = sizeof(*sa_any); -} - void *isert_portal_add(struct sockaddr *saddr, size_t addr_len) { struct isert_portal *portal = isert_portal_start(saddr, addr_len); diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.h b/iscsi-scst/kernel/isert-scst/iser_datamover.h index 864b3d3ec..f403ce509 100644 --- a/iscsi-scst/kernel/isert-scst/iser_datamover.h +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.h @@ -7,7 +7,6 @@ int isert_datamover_init(void); int isert_datamover_cleanup(void); -void create_sockaddr_any(struct sockaddr *sa, u16 port, size_t *addr_len); void *isert_portal_add(struct sockaddr *sa, size_t addr_len); int isert_portal_remove(void *portal_h); diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 030c923e9..f5cfeb19b 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1531,17 +1531,3 @@ struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len) return portal; } -struct isert_portal *isert_portal_add_addr_any(u16 port) -{ - struct sockaddr_storage sa_any; - size_t addr_len; - struct isert_portal *portal; - - create_sockaddr_any((struct sockaddr *)&sa_any, port, &addr_len); - - portal = isert_portal_start((struct sockaddr *)&sa_any, addr_len); - if (IS_ERR(portal)) - portal = NULL; - - return portal; -} From a89367848e5f56d646a9279deb4df37ac6417f65 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 19 Mar 2014 14:30:42 +0000 Subject: [PATCH 038/128] isert: Print error to user if QueuedCommands value exceeds supported maximum Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5341 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 1 + iscsi-scst/kernel/isert-scst/iser_pdu.c | 7 +++++++ iscsi-scst/kernel/isert-scst/iser_rdma.c | 1 - 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index 8141ebcfd..8c1883c39 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -58,6 +58,7 @@ struct isert_wr { #define ISER_MAX_RDMAS 5 #define ISER_SQ_SIZE 128 +#define ISER_MAX_WCE 2048 struct isert_cmnd { struct iscsi_cmnd iscsi ____cacheline_aligned; diff --git a/iscsi-scst/kernel/isert-scst/iser_pdu.c b/iscsi-scst/kernel/isert-scst/iser_pdu.c index e07f53dc8..7d151db26 100644 --- a/iscsi-scst/kernel/isert-scst/iser_pdu.c +++ b/iscsi-scst/kernel/isert-scst/iser_pdu.c @@ -381,6 +381,13 @@ int isert_alloc_conn_resources(struct isert_connection *isert_conn) isert_conn->repost_threshold = 32; to_alloc = isert_conn->queue_depth * 2 + isert_conn->repost_threshold; + if (unlikely(to_alloc > ISER_MAX_WCE)) { + pr_err("QueuedCommands larger than %d not supported\n", + (ISER_MAX_WCE - isert_conn->repost_threshold) / 2); + err = -EINVAL; + goto out; + } + for (i = 0; i < to_alloc; i++) { pdu = isert_rx_pdu_alloc(isert_conn, t_datasz); if (unlikely(!pdu)) { diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index f5cfeb19b..8185847d6 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -41,7 +41,6 @@ #include "iser_datamover.h" #define ISER_CQ_ENTRIES (128 * 1024) -#define ISER_MAX_WCE 2048 #define ISER_LISTEN_BACKLOG 8 static DEFINE_MUTEX(dev_list_mutex); From a892d7b0b3d09d2bfd21fe601e64560d1b8b7df5 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 19 Mar 2014 14:30:48 +0000 Subject: [PATCH 039/128] isert: Avoid hanging scst_mgmtd in case of login response send failure The following is seen if isert_login_rsp_tx fails, since we do not destroy the created sysfs. [ 840.532111] INFO: task scst_mgmtd:4614 blocked for more than 120 seconds. [ 840.532174] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 840.532230] scst_mgmtd D ffff8800371c4ee0 0 4614 2 0x00000000 [ 840.532233] ffff8800378d1bd8 0000000000000046 ffff8800378d1b78 ffff88007e513ec0 [ 840.532236] ffff8800378d1fd8 ffff8800378d1fd8 ffff8800378d1fd8 0000000000013ec0 [ 840.532238] ffff880077b28000 ffff8800379c9740 ffff8800378d1bc8 7fffffffffffffff [ 840.532241] Call Trace: [ 840.532249] [] schedule+0x29/0x70 [ 840.532253] [] schedule_timeout+0x1e5/0x250 [ 840.532259] [] ? console_unlock+0x1a/0x30 [ 840.532263] [] wait_for_common+0xdf/0x180 [ 840.532286] [] ? debug_print_with_prefix+0x165/0x1f0 [scst] [ 840.532289] [] ? try_to_wake_up+0x200/0x200 [ 840.532291] [] wait_for_completion+0x1d/0x20 [ 840.532302] [] scst_kobject_put_and_wait+0x177/0x220 [scst] [ 840.532314] [] scst_sess_sysfs_del+0xb3/0x180 [scst] [ 840.532324] [] scst_free_session+0xaa/0x2c0 [scst] [ 840.532326] [] ? mutex_lock+0x1d/0x50 [ 840.532336] [] scst_free_session_callback+0x9c/0x170 [scst] [ 840.532343] [] ? __raw_spin_unlock_irq+0xe/0x10 [scst] [ 840.532350] [] scst_global_mgmt_thread+0x2e0/0x560 [scst] [ 840.532354] [] ? add_wait_queue+0x60/0x60 [ 840.532362] [] ? scst_register_session_non_gpl+0x20/0x20 [scst] [ 840.532364] [] kthread+0xc0/0xd0 [ 840.532366] [] ? flush_kthread_worker+0xb0/0xb0 [ 840.532369] [] ret_from_fork+0x7c/0xb0 [ 840.532372] [] ? flush_kthread_worker+0xb0/0xb0 Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5342 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 0cbfef731..332f07cda 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -241,16 +241,9 @@ int isert_conn_alloc(struct iscsi_session *session, if (unlikely(res)) goto cleanup_conn; -#ifndef CONFIG_SCST_PROC - res = conn_sysfs_add(conn); - if (unlikely(res)) - goto cleanup_iscsi_conn; -#endif - conn->rd_state = 1; isert_dev_release(dev); - list_add_tail(&conn->conn_list_entry, &session->conn_list); res = isert_login_rsp_tx(cmnd, true, false); vunmap(dev->sg_virt); dev->sg_virt = NULL; @@ -258,12 +251,19 @@ int isert_conn_alloc(struct iscsi_session *session, if (unlikely(res)) goto cleanup_iscsi_conn; +#ifndef CONFIG_SCST_PROC + res = conn_sysfs_add(conn); + if (unlikely(res)) + goto cleanup_iscsi_conn; +#endif + + list_add_tail(&conn->conn_list_entry, &session->conn_list); + goto out; cleanup_iscsi_conn: if (conn->nop_in_interval > 0) cancel_delayed_work_sync(&conn->nop_in_delayed_work); - list_del(&conn->conn_list_entry); cleanup_conn: conn->session = NULL; isert_close_connection(conn); From 4beae6794ea01cba23d9b6b9ed83d94af1c6d4b2 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 19 Mar 2014 14:30:54 +0000 Subject: [PATCH 040/128] isert: Make sure we cleanup all states if resource allocation fails Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5343 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 332f07cda..6049e9584 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -243,6 +243,7 @@ int isert_conn_alloc(struct iscsi_session *session, conn->rd_state = 1; isert_dev_release(dev); + isert_set_priv(conn, NULL); res = isert_login_rsp_tx(cmnd, true, false); vunmap(dev->sg_virt); @@ -262,6 +263,7 @@ int isert_conn_alloc(struct iscsi_session *session, goto out; cleanup_iscsi_conn: + conn->rd_state = 0; if (conn->nop_in_interval > 0) cancel_delayed_work_sync(&conn->nop_in_delayed_work); cleanup_conn: From 799ddae08511a123489ef5c72799661ed0419e51 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 6 Apr 2014 08:13:25 +0000 Subject: [PATCH 041/128] Merged revisions 5322-5326,5329-5340,5344-5371,5382-5407 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5322 | vlnb | 2014-03-05 05:27:21 +0200 (Wed, 05 Mar 2014) | 13 lines scst_vdisk: Make vdisk_nullio size configurable Keep the default size of vdisk_nullio devices at VDISK_NULLIO_SIZE. Add a sysfs attribute 'size' which is the size of a vdisk device in bytes. Make the size of vdisk_nullio devices configurable. Accept "size" and "size_mb" as creation parameters for vdisk_nullio devices. Generate a CAPACITY DATA HAS CHANGED unit attention after size changes. Refuse any attempt to change the size into a number that is not a multiple of the block size. Signed-off-by: Bart Van Assche ........ r5323 | bvassche | 2014-03-06 09:29:00 +0200 (Thu, 06 Mar 2014) | 1 line scst_vdisk: Avoid that smatch complains about unreachable code ........ r5324 | vlnb | 2014-03-07 06:02:39 +0200 (Fri, 07 Mar 2014) | 27 lines PERSISTENT RESERVE IN: Suppress a kernel warning for small output buffer sizes This patch suppresses the following error message and kernel warning: scst: ***ERROR***: Too big response data len 24 (max 8), limiting it to the max (dev iis) Call Trace: [] ? dump_stack+0x41/0x56 [] ? scst_set_resp_data_len+0x82/0xb1 [scst] [] ? scst_pr_read_reservation+0xbf/0xc4 [scst] [] ? scst_persistent_reserve_in_local+0x140/0x1ce [scst] [] ? scst_exec_check_blocking+0x57/0xf1 [scst] [] ? scst_process_active_cmd+0x86c/0x136f [scst] [] ? scst_do_job_active+0x45/0x5b [scst] [] ? scst_cmd_thread+0x218/0x2b7 [scst] [] ? wake_up_bit+0x23/0x23 [] ? scst_cmd_tasklet+0x32/0x32 [scst] [] ? kthread_freezable_should_stop+0x51/0x51 [] ? scst_cmd_tasklet+0x32/0x32 [scst] [] ? kthread+0xab/0xb3 [] ? kthread_freezable_should_stop+0x51/0x51 [] ? ret_from_fork+0x7c/0xb0 [] ? kthread_freezable_should_stop+0x51/0x51 Reported-by: Roman Bogdanov Signed-off-by: Bart Van Assche ........ r5325 | bvassche | 2014-03-08 15:30:29 +0200 (Sat, 08 Mar 2014) | 1 line nightly build: Update kernel versions ........ r5326 | vlnb | 2014-03-13 05:50:37 +0200 (Thu, 13 Mar 2014) | 3 lines Update link to Gentoo HOWTO from Jurie Botha ........ r5329 | vlnb | 2014-03-15 03:38:06 +0200 (Sat, 15 Mar 2014) | 3 lines Cleanup ........ r5330 | vlnb | 2014-03-15 03:38:37 +0200 (Sat, 15 Mar 2014) | 3 lines Implement REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS command ........ r5331 | vlnb | 2014-03-15 03:40:57 +0200 (Sat, 15 Mar 2014) | 8 lines scst_vdisk: Remove an unused parameter from vdisk_fsync*() The "struct vdisk_cmd_params *p" parameter is neither used by vdisk_fsync(), vdisk_fsync_blockio() nor by vdisk_fsync_fileio() so remove it. Signed-off-by: Bart Van Assche ........ r5332 | vlnb | 2014-03-15 03:42:06 +0200 (Sat, 15 Mar 2014) | 8 lines vdisk_blockio: Change default vendor name back to "SCST_BIO" In r5316 the default vendor name for vdisk_blockio devices was changed into "SCST_FIO". Change this back into "SCST_BIO". Signed-off-by: Bart Van Assche ........ r5333 | vlnb | 2014-03-15 03:44:35 +0200 (Sat, 15 Mar 2014) | 8 lines vdisk_blockio: Add VERIFY implementation There is already an implementation of the VERIFY command for vdisk_fileio devices. Add an implementation for vdisk_blockio devices. Signed-off-by: Bart Van Assche ........ r5334 | vlnb | 2014-03-15 04:01:17 +0200 (Sat, 15 Mar 2014) | 8 lines scst_vdisk: Implement COMPARE AND WRITE Ensure that COMPARE AND WRITE is executed atomically by serializing all COMPARE AND WRITE commands per device (SCST_SERIALIZED). Signed-off-by: Bart Van Assche ........ r5335 | vlnb | 2014-03-15 04:09:17 +0200 (Sat, 15 Mar 2014) | 5 lines Fix URL to SCST website Signed-off-by: Steven J. Magnani ........ r5336 | vlnb | 2014-03-15 04:13:58 +0200 (Sat, 15 Mar 2014) | 3 lines Cleanup ........ r5337 | bvassche | 2014-03-15 08:47:26 +0200 (Sat, 15 Mar 2014) | 1 line scripts/kernel-functions: Kernel 3.13.6 build fix ........ r5338 | bvassche | 2014-03-16 15:38:50 +0200 (Sun, 16 Mar 2014) | 1 line srpt: Minor buid process terminology change ........ r5339 | bvassche | 2014-03-18 17:35:13 +0200 (Tue, 18 Mar 2014) | 1 line ib_srpt: Avoid that session logout hangs sporadically ........ r5340 | vlnb | 2014-03-19 06:28:46 +0200 (Wed, 19 Mar 2014) | 8 lines scst/README: Show how to read SCST sysfs attributes Make the behavior of SCST sysfs attributes more clear by adding examples in scst/README of code for reading and writing these attributes. Signed-off-by: Bart Van Assche ........ r5344 | bvassche | 2014-03-20 17:13:50 +0200 (Thu, 20 Mar 2014) | 1 line ib_srpt: Simplify srpt_handle_cmd() ........ r5345 | bvassche | 2014-03-20 17:14:45 +0200 (Thu, 20 Mar 2014) | 5 lines ib_srpt: Micro-optimize I/O context state manipulation All ioctx->state manipulations are serialized per command so it is not necessary to use locking to protect these manipulations. ........ r5346 | bvassche | 2014-03-20 17:15:54 +0200 (Thu, 20 Mar 2014) | 8 lines ib_srpt: Handle GID change events properly The mlx4_core driver generates a GID change event after a port has been changed from IB into Ethernet mode. Avoid that this causes the following error message to appear in the system log: ib_srpt: ***ERROR***: received unrecognized IB event 18 ........ r5347 | bvassche | 2014-03-20 17:16:27 +0200 (Thu, 20 Mar 2014) | 1 line ib_srpt/Makefile: Add kerneldoc target ........ r5348 | bvassche | 2014-03-20 17:17:04 +0200 (Thu, 20 Mar 2014) | 1 line ib_srpt: Fix an error reported by the kerneldoc tool ........ r5349 | bvassche | 2014-03-20 17:18:06 +0200 (Thu, 20 Mar 2014) | 5 lines ib_srpt: Avoid that cmd_wait_list processing triggers command reordering Although harmless for SCSI commands with SIMPLE ordering, avoid that commands received before RTU can get reordered. ........ r5350 | bvassche | 2014-03-20 17:18:38 +0200 (Thu, 20 Mar 2014) | 2 lines ib_srpt: Micro-optimize SRP_CMD parsing ........ r5351 | bvassche | 2014-03-20 17:19:05 +0200 (Thu, 20 Mar 2014) | 1 line ib_srpt: Sync information unit memory only once ........ r5352 | bvassche | 2014-03-20 17:19:34 +0200 (Thu, 20 Mar 2014) | 2 lines ib_srpt: Introduce a temporary variable in srpt_handle_new_iu() ........ r5353 | bvassche | 2014-03-20 17:20:55 +0200 (Thu, 20 Mar 2014) | 2 lines ib_srpt: Micro-optimize polling ........ r5354 | bvassche | 2014-03-20 17:22:19 +0200 (Thu, 20 Mar 2014) | 5 lines ib_srpt: Rework multi-channel support Store initiator and target port ID's once per nexus instead of in each channel data structure. ........ r5355 | bvassche | 2014-03-20 17:23:16 +0200 (Thu, 20 Mar 2014) | 2 lines ib_srpt: Simplify channel state management code ........ r5356 | bvassche | 2014-03-20 17:24:18 +0200 (Thu, 20 Mar 2014) | 5 lines ib_srpt: Defer destroying the QP until the TimeWait state has been left This is necessary to avoid that a login gets rejected due to reusing a queue pair number that has not yet been freed by the target side. ........ r5357 | bvassche | 2014-03-20 17:25:34 +0200 (Thu, 20 Mar 2014) | 5 lines ib_srpt: Rework waiting for last WQE After having changed the queue pair state into "error", queue an additional work request instead of waiting for the last WQE event. ........ r5358 | bvassche | 2014-03-20 17:26:48 +0200 (Thu, 20 Mar 2014) | 1 line srpt/session-management.txt: Document how sessions are managed by the ib_srpt driver ........ r5359 | bvassche | 2014-03-20 18:10:19 +0200 (Thu, 20 Mar 2014) | 1 line scst_const.h: Make COMPARE_AND_WRITE definition available for kernel versions 3.6..3.11 ........ r5360 | vlnb | 2014-03-21 03:58:13 +0200 (Fri, 21 Mar 2014) | 3 lines In VERIFY commands BYTCHK 1x is not supported (yet) ........ r5361 | bvassche | 2014-03-21 18:29:26 +0200 (Fri, 21 Mar 2014) | 1 line srpt/Makefile: Avoid that the build process depends on source control tools ........ r5362 | vlnb | 2014-03-22 01:12:42 +0200 (Sat, 22 Mar 2014) | 3 lines Fix error recovery of internal commands ........ r5363 | bvassche | 2014-03-24 13:53:00 +0200 (Mon, 24 Mar 2014) | 1 line ib_srpt: Clarify a kernel-doc comment ........ r5364 | bvassche | 2014-03-24 13:55:56 +0200 (Mon, 24 Mar 2014) | 1 line ib_srpt: Add newline at the end of kernel warning statements ........ r5365 | bvassche | 2014-03-24 13:57:39 +0200 (Mon, 24 Mar 2014) | 5 lines ib_srpt: Change the severity level of a log message Make sure that target port state changes get logged even with debugging disabled. ........ r5366 | bvassche | 2014-03-24 13:59:27 +0200 (Mon, 24 Mar 2014) | 5 lines ib_srpt: Clean up srpt_destroy_ch_ib() All callers guarantee that the completion queue is empty so it is not necessary to invoke ib_poll_cq() from inside this function. ........ r5367 | bvassche | 2014-03-24 14:01:07 +0200 (Mon, 24 Mar 2014) | 5 lines ib_srpt: Micro-optimize srpt_adjust_srq_wr_avail() The overhead of atomic_add_return() is lower than that of a spin_lock() / spin_unlock() pair, hence switch to the former. ........ r5368 | bvassche | 2014-03-24 14:03:09 +0200 (Mon, 24 Mar 2014) | 20 lines ib_srpt: Fix a kernel warning Avoid that the following (very rare) kernel warning is reported when an ib_srpt target port is disabled while I/O is ongoing: WARNING: CPU: 3 PID: 12259 at srpt/src/ib_srpt.c:3334 srpt_xmit_response+0x165/0x300 [ib_srpt]() Unexpected command state 6 Call Trace: [] dump_stack+0x4e/0x7a [] warn_slowpath_common+0x7d/0xa0 [] warn_slowpath_fmt+0x4c/0x50 [] srpt_xmit_response+0x165/0x300 [ib_srpt] [] scst_xmit_response+0xbc/0x560 [scst] [] scst_process_active_cmd+0x29d/0x7b0 [scst] [] scst_do_job_active+0x89/0x1a0 [scst] [] scst_cmd_thread+0x15f/0x350 [scst] [] kthread+0xed/0x110 [] ret_from_fork+0x7c/0xb0 ---[ end trace 591f7af7d006fc0e ]--- ........ r5369 | bvassche | 2014-03-24 14:04:44 +0200 (Mon, 24 Mar 2014) | 5 lines ib_srpt: Add a kernel warning Invoking srpt_zerolength_write() before the queue pair has reached the error state is a bug, so complain loudly if that happens. ........ r5370 | bvassche | 2014-03-24 14:07:43 +0200 (Mon, 24 Mar 2014) | 7 lines ib_srpt: Avoid waiting for missing error completions Apparently with mlx4 firmware up to and including 2.30.8000 it is not guaranteed that for a QP associated with an SRQ error completions are generated for all pending work requests. Avoid triggering srpt_pending_cmd_timeout() for missing error completions. ........ r5371 | bvassche | 2014-03-25 18:19:00 +0200 (Tue, 25 Mar 2014) | 1 line nightly build: Update kernel versions ........ r5382 | vlnb | 2014-03-26 04:31:52 +0200 (Wed, 26 Mar 2014) | 6 lines Black hole functionality added Scst_mutex intentially used directly in the sysfs handler, because comming sysfs improvements will allow that. ........ r5383 | vlnb | 2014-03-26 05:18:03 +0200 (Wed, 26 Mar 2014) | 5 lines documentation: Document SCST_SERIALIZED and SCST_STRICTLY_SERIALIZED Signed-off-by: Bart Van Assche ........ r5384 | vlnb | 2014-03-26 05:18:36 +0200 (Wed, 26 Mar 2014) | 3 lines Cleanup ........ r5385 | vlnb | 2014-03-26 05:20:04 +0200 (Wed, 26 Mar 2014) | 9 lines scst_local: Remove two superfluous tests The to_scst_lcl_sess() macro is based on container_of() and hence never returns NULL. Hence remove the two tests that compare the result of that macro against NULL. Signed-off-by: Bart Van Assche ........ r5386 | vlnb | 2014-03-26 05:21:25 +0200 (Wed, 26 Mar 2014) | 7 lines iscsi-scst: Introduce ARRAY_SIZE() This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5387 | vlnb | 2014-03-26 05:22:16 +0200 (Wed, 26 Mar 2014) | 8 lines scst: Clarify a comment The comment above scst_nexus_loss() is somewhat confusing so change it into something that is more clear. Signed-off-by: Bart Van Assche ........ r5388 | vlnb | 2014-03-26 05:23:57 +0200 (Wed, 26 Mar 2014) | 9 lines scst_vdisk: Fix READ CAPACITY(10) SBC-2 defines the LBA as a 32-bit field that starts at offset 2 and not as a 64-bit field. Reported-by: Mike Christie Signed-off-by: Bart Van Assche ........ r5389 | bvassche | 2014-03-26 13:56:13 +0200 (Wed, 26 Mar 2014) | 4 lines ib_srpt: Clean up srpt_handle_rdma_comp() This patch does not change any functionality. ........ r5390 | bvassche | 2014-03-26 13:56:59 +0200 (Wed, 26 Mar 2014) | 4 lines ib_srpt: Clean up srpt_handle_send_err_comp() This patch does not change any functionality. ........ r5391 | bvassche | 2014-03-26 13:58:25 +0200 (Wed, 26 Mar 2014) | 4 lines ib_srpt: Clean up srpt_handle_rdma_err_comp() This patch does not change any functionality. ........ r5392 | bvassche | 2014-03-26 13:59:37 +0200 (Wed, 26 Mar 2014) | 5 lines ib_srpt: Suppress superfluous error messages Only complain about a missing completion for I/O contexts that are in a state where the ib_srpt driver is waiting for the HCA. ........ r5393 | bvassche | 2014-03-26 14:00:43 +0200 (Wed, 26 Mar 2014) | 5 lines ib_srpt: Make srpt_abort_cmd() state checks more strict Complain if srpt_abort_cmd() is called for an I/O context that is being processed by SCST and not by the HCA. ........ r5394 | vlnb | 2014-03-27 00:16:16 +0200 (Thu, 27 Mar 2014) | 3 lines Cosmetics ........ r5395 | vlnb | 2014-03-27 01:51:36 +0200 (Thu, 27 Mar 2014) | 3 lines Reimplement dropping of TM requests in a more reliable manner ........ r5396 | vlnb | 2014-03-27 03:57:08 +0200 (Thu, 27 Mar 2014) | 3 lines Possibility to specify SCSI target device name added ........ r5397 | bvassche | 2014-03-27 10:27:45 +0200 (Thu, 27 Mar 2014) | 6 lines Fix two checkpatch complaints about whitespace Avoid that checkpatch reports the following error message: ERROR: "(foo*)" should be "(foo *)" ........ r5398 | bvassche | 2014-03-27 10:34:27 +0200 (Thu, 27 Mar 2014) | 6 lines Avoid that checkpatch complains that return is not a function Avoid that checkpatch reports the following error message: ERROR: return is not a function, parentheses are not required ........ r5399 | bvassche | 2014-03-27 10:39:53 +0200 (Thu, 27 Mar 2014) | 1 line scripts/generate-kernel-patch: Fix for kernel versions 3.7, 3.10, 3.12 and 3.13 ........ r5400 | vlnb | 2014-03-29 04:07:22 +0300 (Sat, 29 Mar 2014) | 3 lines Cleanup ........ r5401 | bvassche | 2014-04-01 20:46:36 +0300 (Tue, 01 Apr 2014) | 1 line vdisk_blockio: Temporarily disable COMPARE AND WRITE support ........ r5402 | bvassche | 2014-04-02 00:05:31 +0300 (Wed, 02 Apr 2014) | 1 line vdisk_blockio: Fix the (recently enabled) VERIFY command ........ r5403 | bvassche | 2014-04-03 18:58:16 +0300 (Thu, 03 Apr 2014) | 1 line ib_srpt: RHEL 6.5 build fix ........ r5404 | vlnb | 2014-04-04 03:57:30 +0300 (Fri, 04 Apr 2014) | 3 lines Fix typo in scst_report_supported_tm_fns() reported by Steve Magnani ........ r5405 | bvassche | 2014-04-04 07:38:33 +0300 (Fri, 04 Apr 2014) | 1 line scripts/specialize-patch: Handle numbers surrounded by parentheses properly ........ r5406 | bvassche | 2014-04-04 08:50:52 +0300 (Fri, 04 Apr 2014) | 1 line scripts/specialize-patch: Rework r5405 ........ r5407 | bvassche | 2014-04-04 08:56:25 +0300 (Fri, 04 Apr 2014) | 1 line nightly build: Update kernel versions ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5408 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- doc/scst_pg.sgml | 15 +- doc/scst_user_spec.sgml | 2 +- iscsi-scst/kernel/iscsi.c | 34 +- iscsi-scst/kernel/nthread.c | 10 +- nightly/conf/nightly.conf | 6 +- qla2x00t/qla2x00-target/qla2x00t.c | 34 +- scripts/generate-kernel-patch | 8 +- scripts/kernel-functions | 4 +- scripts/specialize-patch | 14 +- scst/README | 66 ++- scst/README_in-tree | 66 ++- scst/include/scst.h | 51 ++- scst/include/scst_const.h | 15 +- scst/src/Makefile | 6 +- scst/src/dev_handlers/scst_vdisk.c | 639 ++++++++++++++++++++++---- scst/src/scst_lib.c | 122 ++++- scst/src/scst_main.c | 2 +- scst/src/scst_pres.c | 10 +- scst/src/scst_sysfs.c | 159 +++++++ scst/src/scst_targ.c | 105 ++++- scst_local/scst_local.c | 8 - srpt/Makefile | 7 +- srpt/README | 2 +- srpt/session-management.txt | 45 ++ srpt/src/ib_srpt.c | 691 ++++++++++++++--------------- srpt/src/ib_srpt.h | 60 ++- www/index.html | 2 +- 27 files changed, 1635 insertions(+), 548 deletions(-) create mode 100644 srpt/session-management.txt diff --git a/doc/scst_pg.sgml b/doc/scst_pg.sgml index e3e899bd2..f690b2536 100644 --- a/doc/scst_pg.sgml +++ b/doc/scst_pg.sgml @@ -1272,7 +1272,20 @@ Where: User space API diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index fb8d153ec..b6e5599e5 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -62,7 +62,8 @@ static struct page *dummy_page; static struct scatterlist dummy_sg; static void cmnd_remove_data_wait_hash(struct iscsi_cmnd *cmnd); -static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status); +static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status, + bool dropped); static void iscsi_check_send_delayed_tm_resp(struct iscsi_session *sess); static int cmnd_insert_data_wait_hash(struct iscsi_cmnd *cmnd); static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags); @@ -2721,7 +2722,7 @@ static void execute_task_management(struct iscsi_cmnd *req) reject: if (rc != 0) - iscsi_send_task_mgmt_resp(req, status); + iscsi_send_task_mgmt_resp(req, status, false); return; } @@ -3610,7 +3611,8 @@ out: return; } -static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status) +static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status, + bool drop) { struct iscsi_cmnd *rsp; struct iscsi_task_mgt_hdr *req_hdr = @@ -3622,7 +3624,26 @@ static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status) TRACE_ENTRY(); TRACE_MGMT_DBG("TM req %p finished", req); - TRACE(TRACE_MGMT, "iSCSI TM fn %d finished, status %d", fn, status); + TRACE(TRACE_MGMT, "iSCSI TM fn %d finished, status %d, dropped %d", + fn, status, drop); + + if (drop) { + spin_lock(&sess->sn_lock); + sess->tm_active--; + spin_unlock(&sess->sn_lock); + if (fn == ISCSI_FUNCTION_TARGET_COLD_RESET) { + struct iscsi_target *target = req->conn->session->target; + + PRINT_INFO("Closing all connections for target %x at " + "COLD RESET from initiator %s", target->tid, + req->conn->session->initiator_name); + + mutex_lock(&target->target_mutex); + target_del_all_sess(target, 0); + mutex_unlock(&target->target_mutex); + } + goto out_release; + } rsp = iscsi_alloc_rsp(req); rsp_hdr = (struct iscsi_task_rsp_hdr *)&rsp->pdu.bhs; @@ -3691,8 +3712,7 @@ static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) int fn = scst_mgmt_cmd_get_fn(scst_mcmd); struct iscsi_cmnd *req = (struct iscsi_cmnd *) scst_mgmt_cmd_get_tgt_priv(scst_mcmd); - int status = - iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd)); + int status = iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd)); if ((status == ISCSI_RESPONSE_UNKNOWN_TASK) && (fn == SCST_ABORT_TASK)) { @@ -3714,7 +3734,7 @@ static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) sBUG_ON(1); break; default: - iscsi_send_task_mgmt_resp(req, status); + iscsi_send_task_mgmt_resp(req, status, scst_mgmt_cmd_dropped(scst_mcmd)); scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); break; } diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 04d6875a8..145174009 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -1331,8 +1331,7 @@ static int write_data(struct iscsi_conn *conn) loff_t off = 0; int rest; - sBUG_ON(count > (signed)(sizeof(conn->write_iov) / - sizeof(conn->write_iov[0]))); + sBUG_ON(count > ARRAY_SIZE(conn->write_iov)); retry: oldfs = get_fs(); set_fs(KERNEL_DS); @@ -1367,8 +1366,8 @@ retry: break; goto out_iov; } - sBUG_ON(iop > conn->write_iov + sizeof(conn->write_iov) - /sizeof(conn->write_iov[0])); + sBUG_ON(iop > + conn->write_iov + ARRAY_SIZE(conn->write_iov)); iop->iov_base += rest; iop->iov_len -= rest; } @@ -1626,8 +1625,7 @@ static void init_tx_hdigest(struct iscsi_cmnd *cmnd) digest_tx_header(cmnd); - sBUG_ON(conn->write_iop_used >= - (signed)(sizeof(conn->write_iov)/sizeof(conn->write_iov[0]))); + sBUG_ON(conn->write_iop_used >= ARRAY_SIZE(conn->write_iov)); iop = &conn->write_iop[conn->write_iop_used]; conn->write_iop_used++; diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index 1b8d709e1..15253e973 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,16 +3,16 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.13.5 \ +3.13.9 \ 3.12.13-nc \ 3.11.10-nc \ -3.10.32-nc \ +3.10.36-nc \ 3.9.11-nc \ 3.8.13-nc \ 3.7.10-nc \ 3.6.11-nc \ 3.5.7-nc \ -3.4.81-nc \ +3.4.86-nc \ 3.3.8-nc \ 3.2.53-nc \ 3.1.10-nc \ diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 4de80917f..720daea09 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -1235,25 +1235,25 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, spin_lock_irq(&pha->hardware_lock); sess = q2t_find_sess_by_port_name_include_deleted(tgt, fcport->port_name); if (sess != NULL) { - TRACE_MGMT_DBG("Double sess %p found (s_id %x:%x:%x, " - "loop_id %d), updating to d_id %x:%x:%x, " - "loop_id %d", sess, sess->s_id.b.domain, - sess->s_id.b.area, sess->s_id.b.al_pa, - sess->loop_id, fcport->d_id.b.domain, - fcport->d_id.b.area, fcport->d_id.b.al_pa, - fcport->loop_id); + TRACE_MGMT_DBG("Double sess %p found (s_id %x:%x:%x, " + "loop_id %d), updating to d_id %x:%x:%x, " + "loop_id %d", sess, sess->s_id.b.domain, + sess->s_id.b.area, sess->s_id.b.al_pa, + sess->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + fcport->loop_id); - if (sess->deleted) - q2t_undelete_sess(sess); + if (sess->deleted) + q2t_undelete_sess(sess); - q2t_sess_get(sess); - sess->s_id = fcport->d_id; - sess->loop_id = fcport->loop_id; - sess->conf_compl_supported = fcport->conf_compl_supported; - if (sess->local && !local) - sess->local = 0; - spin_unlock_irq(&pha->hardware_lock); - goto out; + q2t_sess_get(sess); + sess->s_id = fcport->d_id; + sess->loop_id = fcport->loop_id; + sess->conf_compl_supported = fcport->conf_compl_supported; + if (sess->local && !local) + sess->local = 0; + spin_unlock_irq(&pha->hardware_lock); + goto out; } spin_unlock_irq(&pha->hardware_lock); diff --git a/scripts/generate-kernel-patch b/scripts/generate-kernel-patch index e66b287ef..532572fb4 100755 --- a/scripts/generate-kernel-patch +++ b/scripts/generate-kernel-patch @@ -268,13 +268,13 @@ done scsi_exec_req_fifo_defined=0 scst_io_context=0 for p in scst/kernel/*-${kver}.patch \ - $(if [ ${kver} = 3.7 ] && [ "${1#3.7.}" -ge 10 ]; then + $(if [ "${1#3.7.}" != "$1" ] && [ "${1#3.7.}" -ge 10 ]; then echo iscsi-scst/kernel/patches/*-3.7.10.patch; - elif [ ${kver} = 3.10 ] && [ "${1#3.10.}" -ge 30 ]; then + elif [ "${1#3.10.}" != "$1" ] && [ "${1#3.10.}" -ge 30 ]; then echo iscsi-scst/kernel/patches/*-3.10.30.patch; - elif [ ${kver} = 3.12 ] && [ "${1#3.12.}" -ge 11 ]; then + elif [ "${1#3.12.}" != "$1" ] && [ "${1#3.12.}" -ge 11 ]; then echo iscsi-scst/kernel/patches/*-3.12.11.patch; - elif [ ${kver} = 3.13 ] && [ "${1#3.13.}" -ge 3 ]; then + elif [ "${1#3.13.}" != "$1" ] && [ "${1#3.13.}" -ge 3 ]; then echo iscsi-scst/kernel/patches/*-3.13.3.patch; else echo iscsi-scst/kernel/patches/*-${kver}.patch; diff --git a/scripts/kernel-functions b/scripts/kernel-functions index 0bc0584a9..58751563f 100644 --- a/scripts/kernel-functions +++ b/scripts/kernel-functions @@ -175,7 +175,8 @@ Get rid of sparse errors on sk_buff.protocol. EOF fi if [ "${1#3.13}" != "$1" ]; then - patch -f -s -p1 <<'EOF' + if [ "$1" = "3.13" ] || [ "${1#3.13.}" -lt 6 ]; then + patch -f -s -p1 <<'EOF' From 7b4ec8dd7d4ac467e9eee4d49f2c9574d773efbb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 16 Jan 2014 10:18:48 +1030 @@ -212,6 +213,7 @@ index 3f2793d..96e45ea 100644 __used \ __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ EOF + fi fi ) rmdir "${tmpdir}" diff --git a/scripts/specialize-patch b/scripts/specialize-patch index f3ae24262..ede8b0a19 100755 --- a/scripts/specialize-patch +++ b/scripts/specialize-patch @@ -151,7 +151,13 @@ function evaluate(stmnt, pattern, arg, op, result) { { last_stmnt = stmnt - pattern = "![[:blank:]]*([0-9]+)" + pattern = "![[:blank:]]*(-*[0-9]+)" + while (match(stmnt, pattern, op) != 0) + { + sub(pattern, op[1] == 0, stmnt) + } + + pattern = "![[:blank:]]*\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)" while (match(stmnt, pattern, op) != 0) { sub(pattern, op[1] == 0, stmnt) @@ -197,6 +203,12 @@ function evaluate(stmnt, pattern, arg, op, result) { sub(pattern, result, stmnt) } + pattern="(-*[0-9]+)[[:blank:]]*\\&\\&[[:blank:]]*\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)" + while (match(stmnt, pattern, op) != 0) + { + sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt) + } + pattern="(-*[0-9]+)[[:blank:]]*\\&\\&[[:blank:]]*(-*[0-9]+)" while (match(stmnt, pattern, op) != 0) { diff --git a/scst/README b/scst/README index 9e91bcc65..6234643f1 100644 --- a/scst/README +++ b/scst/README @@ -460,7 +460,38 @@ following entries: complete, an management tool should poll this file. If the operation hasn't yet completed, it will also return EAGAIN. But after it's completed, it will return the result of this operation (0 for success - or -errno for error). + or -errno for error). The following two shell functions show how to do + this: + +# Read the SCST sysfs attribute $1. See also scst/README for more information. +scst_sysfs_read() { + local EAGAIN val + + EAGAIN="Resource temporarily unavailable" + while true; do + if val="$(LC_ALL=C cat "$1" 2>&1)"; then + echo -n "${val%\[key\]}" + return 0 + elif [ "${val/*: }" != "$EAGAIN" ]; then + return 1 + fi + sleep 1 + done +} + +# Write $1 into the SCST sysfs attribute $2. See also scst/README for more +# information. +scst_sysfs_write() { + local EAGAIN status + + EAGAIN="Resource temporarily unavailable" + if status="$(LC_ALL=C; (echo -n "$1" > "$2") 2>&1)"; then + return 0 + elif [ "${status/*: }" != "$EAGAIN" ]; then + return 1 + fi + scst_sysfs_read /sys/kernel/scst_tgt/last_sysfs_mgmt_res >/dev/null +} "Devices" subdirectory contains subdirectories for each SCST devices. @@ -549,7 +580,7 @@ Every target should have at least the following entries: mapping to the corresponding hardware port. It isn't anyhow used by SCST. - - enabled - using this attribute you can enable or disable this target/ + - enabled - using this attribute you can enable or disable this target. It allows to finish configuring it before it starts accepting new connections. 0 by default. @@ -561,6 +592,31 @@ Every target should have at least the following entries: can assign the addressing method on per-initiator basis. See also the "Logical unit addressing (LUN)" section in SAM-5 for more information. + - black_hole - if set, all LUNs in the corresponding initiator group, + default target group in this case, start "swallowing" requests from + initiators. Possible values are: + + * 0 - disable black hole mode + + * 1 - immediately abort all coming commands + + * 2 - immediately abort all coming commands and drop all coming TM + commands + + * 3 - immediately abort all coming data transfer commands. + + * 4 - immediately abort all coming data transfer commands and drop all + coming TM commands + + Modes 3 and 4 are the most evil ones, because they are not too well + handled by many initiator OS'es, including Linux, so they may never + recover from it. + + Note, dropping TM commands, i.e. not sending response on them, + implemented not for all target drivers. If it's implemented for your + particular target driver or not, you can find out by checking traces + or the target driver's source code. + - cpu_mask - defines CPU affinity mask for threads serving this target. For threads serving LUNs it is used only for devices with threads_pool_type "per_initiator". @@ -719,7 +775,7 @@ commands by looking inside this file. Each security group's subdirectory contains 2 subdirectories: initiators and luns as well as the following attributes: addr_method, cpu_mask and -io_grouping_type. See above description of them. +io_grouping_type, black_hole. See above description of them. Each "initiators" subdirectory contains list of added to this groups initiator as well as as well as file "mgmt". This file has the following @@ -965,6 +1021,10 @@ Each vdisk_fileio's device has the following attributes in - prod_rev_lvl - PRODUCT REVISION LEVEL as reported via the INQUIRY response. The default value for this field is " 300". + - scsi_device_name - optional SCSI target device name to which this + SCST device belongs to (in SCSI terminology all SCST devices called + Logical Units). See SPC for more info. + - thin_provisioned - contains thin provisioning status of this virtual device. diff --git a/scst/README_in-tree b/scst/README_in-tree index 7ff520b93..f66ef58e6 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -322,7 +322,38 @@ following entries: complete, an management tool should poll this file. If the operation hasn't yet completed, it will also return EAGAIN. But after it's completed, it will return the result of this operation (0 for success - or -errno for error). + or -errno for error). The following two shell functions show how to do + this: + +# Read the SCST sysfs attribute $1. See also scst/README for more information. +scst_sysfs_read() { + local EAGAIN val + + EAGAIN="Resource temporarily unavailable" + while true; do + if val="$(LC_ALL=C cat "$1" 2>&1)"; then + echo -n "${val%\[key\]}" + return 0 + elif [ "${val/*: }" != "$EAGAIN" ]; then + return 1 + fi + sleep 1 + done +} + +# Write $1 into the SCST sysfs attribute $2. See also scst/README for more +# information. +scst_sysfs_write() { + local EAGAIN status + + EAGAIN="Resource temporarily unavailable" + if status="$(LC_ALL=C; (echo -n "$1" > "$2") 2>&1)"; then + return 0 + elif [ "${status/*: }" != "$EAGAIN" ]; then + return 1 + fi + scst_sysfs_read /sys/kernel/scst_tgt/last_sysfs_mgmt_res >/dev/null +} "Devices" subdirectory contains subdirectories for each SCST devices. @@ -411,7 +442,7 @@ Every target should have at least the following entries: mapping to the corresponding hardware port. It isn't anyhow used by SCST. - - enabled - using this attribute you can enable or disable this target/ + - enabled - using this attribute you can enable or disable this target. It allows to finish configuring it before it starts accepting new connections. 0 by default. @@ -423,6 +454,31 @@ Every target should have at least the following entries: can assign the addressing method on per-initiator basis. See also the "Logical unit addressing (LUN)" section in SAM-5 for more information. + - black_hole - if set, all LUNs in the corresponding initiator group, + default target group in this case, start "swallowing" requests from + initiators. Possible values are: + + * 0 - disable black hole mode + + * 1 - immediately abort all coming commands + + * 2 - immediately abort all coming commands and drop all coming TM + commands + + * 3 - immediately abort all coming data transfer commands. + + * 4 - immediately abort all coming data transfer commands and drop all + coming TM commands + + Modes 3 and 4 are the most evil ones, because they are not too well + handled by many initiator OS'es, including Linux, so they may never + recover from it. + + Note, dropping TM commands, i.e. not sending response on them, + implemented not for all target drivers. If it's implemented for your + particular target driver or not, you can find out by checking traces + or the target driver's source code. + - cpu_mask - defines CPU affinity mask for threads serving this target. For threads serving LUNs it is used only for devices with threads_pool_type "per_initiator". @@ -581,7 +637,7 @@ commands by looking inside this file. Each security group's subdirectory contains 2 subdirectories: initiators and luns as well as the following attributes: addr_method, cpu_mask and -io_grouping_type. See above description of them. +io_grouping_type, black_hole. See above description of them. Each "initiators" subdirectory contains list of added to this groups initiator as well as as well as file "mgmt". This file has the following @@ -823,6 +879,10 @@ Each vdisk_fileio's device has the following attributes in - prod_rev_lvl - PRODUCT REVISION LEVEL as reported via the INQUIRY response. The default value for this field is " 300". + - scsi_device_name - optional SCSI target device name to which this + SCST device belongs to (in SCSI terminology all SCST devices called + Logical Units). See SPC for more info. + - thin_provisioned - contains thin provisioning status of this virtual device. diff --git a/scst/include/scst.h b/scst/include/scst.h index 229720009..378dc589c 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -592,6 +592,9 @@ enum scst_exec_context { /* Set if tgt_dev has Unit Attention sense */ #define SCST_TGT_DEV_UA_PENDING 0 +/* Cache of acg->acg_black_hole_type */ +#define SCST_TGT_DEV_BLACK_HOLE 1 + /************************************************************* ** I/O grouping types. Changing them don't forget to change ** the corresponding *_STR values in scst_const.h! @@ -2286,6 +2289,7 @@ struct scst_mgmt_cmd { unsigned int cmd_sn_set:1; /* set, if cmd_sn field is valid */ /* Set if dev handler's task_mgmt_fn_received was called */ unsigned int task_mgmt_fn_received_called:1; + unsigned int mcmd_dropped:1; /* set if mcmd was dropped */ /* * Number of commands to finish before sending response, @@ -2727,6 +2731,33 @@ struct scst_acg { unsigned int tgt_acg:1; +/* Not a black hole */ +#define SCST_ACG_BLACK_HOLE_NONE 0 + +/* Immediately abort all coming commands */ +#define SCST_ACG_BLACK_HOLE_CMD 1 + +/* + * Immediately abort all coming commands and drop all coming TM commands. + * + * CAUTION! With some target drivers it can cause internal resources + * leaks, so don't abuse this option! + */ +#define SCST_ACG_BLACK_HOLE_ALL 2 + +/* Immediately abort all coming data transfer commands */ +#define SCST_ACG_BLACK_HOLE_DATA_CMD 3 + +/* + * Immediately abort all coming data transfer commands and drop all + * coming TM commands. + * + * CAUTION! With some target drivers it can cause internal resources + * leaks, so don't abuse this option! + */ +#define SCST_ACG_BLACK_HOLE_DATA_MCMD 4 + volatile int acg_black_hole_type; + /* sysfs release completion */ struct completion *acg_kobj_release_cmpl; @@ -3035,6 +3066,8 @@ int scst_get_cdb_info(struct scst_cmd *cmd); int scst_set_cmd_error_status(struct scst_cmd *cmd, int status); int scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq); +int scst_set_cmd_error_and_inf(struct scst_cmd *cmd, int key, int asc, + int ascq, uint64_t information); void scst_set_busy(struct scst_cmd *cmd); void scst_check_convert_sense(struct scst_cmd *cmd); @@ -3685,12 +3718,6 @@ static inline int scst_mgmt_cmd_get_status(struct scst_mgmt_cmd *mcmd) return mcmd->status; } -/* Returns mgmt cmd's TM fn */ -static inline int scst_mgmt_cmd_get_fn(struct scst_mgmt_cmd *mcmd) -{ - return mcmd->fn; -} - static inline void scst_mgmt_cmd_set_status(struct scst_mgmt_cmd *mcmd, int status) { @@ -3700,6 +3727,18 @@ static inline void scst_mgmt_cmd_set_status(struct scst_mgmt_cmd *mcmd, mcmd->status = status; } +/* Returns mgmt cmd's TM fn */ +static inline int scst_mgmt_cmd_get_fn(struct scst_mgmt_cmd *mcmd) +{ + return mcmd->fn; +} + +/* Returns true if mgmt cmd should be dropped, i.e. response not sent */ +static inline bool scst_mgmt_cmd_dropped(struct scst_mgmt_cmd *mcmd) +{ + return mcmd->mcmd_dropped; +} + /* * Called by dev handler's task_mgmt_fn_*() to notify SCST core that mcmd * is going to complete asynchronously. diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index d534f2739..06bf7e923 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -264,12 +264,12 @@ enum scst_cdb_flags { static inline int scst_sense_valid(const uint8_t *sense) { - return ((sense != NULL) && ((sense[0] & 0x70) == 0x70)); + return (sense != NULL) && ((sense[0] & 0x70) == 0x70); } static inline int scst_no_sense(const uint8_t *sense) { - return ((sense != NULL) && (sense[2] == 0)); + return (sense != NULL) && (sense[2] == 0); } static inline int scst_sense_response_code(const uint8_t *sense) @@ -421,6 +421,16 @@ static inline int scst_sense_response_code(const uint8_t *sense) #define UNMAP 0x42 #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) +/* + * From . See also commit + * 1c68cc1626341665a8bd1d2c7dfffd7fc852a79c. + */ +#ifndef COMPARE_AND_WRITE +#define COMPARE_AND_WRITE 0x89 +#endif +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) /* * From . See also commit @@ -550,7 +560,6 @@ enum scst_tg_sup { ** Misc SCSI constants *************************************************************/ #define SCST_SENSE_ASC_UA_RESET 0x29 -#define BYTCHK 0x02 #define POSITION_LEN_SHORT 20 #define POSITION_LEN_LONG 32 diff --git a/scst/src/Makefile b/scst/src/Makefile index 5d268577b..07b179724 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -1,15 +1,15 @@ # # SCSI target mid-level makefile -# +# # Copyright (C) 2004 - 2014 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar # Copyright (C) 2007 - 2014 Fusion-io, Inc. -# +# # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index e1ce52a4c..c00798e75 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -182,12 +182,14 @@ struct scst_vdisk_dev { unsigned int vend_specific_id_set:1; unsigned int prod_id_set:1; /* true if prod_id manually set */ unsigned int prod_rev_lvl_set:1; /* true if prod_rev_lvl manually set */ + unsigned int scsi_device_name_set:1; /* true if scsi_device_name manually set */ unsigned int t10_dev_id_set:1; /* true if t10_dev_id manually set */ unsigned int usn_set:1; /* true if usn manually set */ char t10_vend_id[8 + 1]; char vend_specific_id[32 + 1]; char prod_id[16 + 1]; char prod_rev_lvl[4 + 1]; + char scsi_device_name[256 + 1]; char t10_dev_id[16+8+2]; /* T10 device ID */ char usn[MAX_USN_LEN]; uint8_t inq_vend_specific[MAX_INQ_VEND_SPECIFIC_LEN]; @@ -259,8 +261,7 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p); static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua); static int vdisk_blockio_flush(struct block_device *bdev, gfp_t gfp_mask, bool report_error, struct scst_cmd *cmd, bool async); -static enum compl_status_e blockio_exec_verify(struct vdisk_cmd_params *p); -static enum compl_status_e fileio_exec_verify(struct vdisk_cmd_params *p); +static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p); static enum compl_status_e blockio_exec_write_verify(struct vdisk_cmd_params *p); static enum compl_status_e fileio_exec_write_verify(struct vdisk_cmd_params *p); static enum compl_status_e nullio_exec_write_verify(struct vdisk_cmd_params *p); @@ -277,7 +278,8 @@ static enum compl_status_e vdisk_exec_read_toc(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_prevent_allow_medium_removal(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_unmap(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_write_same(struct vdisk_cmd_params *p); -static int vdisk_fsync(struct vdisk_cmd_params *p, loff_t loff, +static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p); +static int vdisk_fsync(loff_t loff, loff_t len, struct scst_device *dev, gfp_t gfp_flags, struct scst_cmd *cmd, bool async); #ifdef CONFIG_SCST_PROC @@ -307,8 +309,14 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, #ifndef CONFIG_SCST_PROC +static ssize_t vdev_sysfs_size_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdev_sysfs_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_size_mb_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_size_mb_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj, @@ -347,6 +355,10 @@ static ssize_t vdev_sysfs_prod_rev_lvl_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdev_sysfs_prod_rev_lvl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_scsi_device_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_scsi_device_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj, @@ -365,8 +377,16 @@ static ssize_t vdev_zero_copy_show(struct kobject *kobj, static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); -static struct kobj_attribute vdev_size_attr = - __ATTR(size_mb, S_IRUGO, vdev_sysfs_size_show, NULL); +static struct kobj_attribute vdev_size_ro_attr = + __ATTR(size, S_IRUGO, vdev_sysfs_size_show, NULL); +static struct kobj_attribute vdev_size_rw_attr = + __ATTR(size, S_IWUSR|S_IRUGO, vdev_sysfs_size_show, + vdev_sysfs_size_store); +static struct kobj_attribute vdev_size_mb_ro_attr = + __ATTR(size_mb, S_IRUGO, vdev_sysfs_size_mb_show, NULL); +static struct kobj_attribute vdev_size_mb_rw_attr = + __ATTR(size_mb, S_IWUSR|S_IRUGO, vdev_sysfs_size_mb_show, + vdev_sysfs_size_mb_store); static struct kobj_attribute vdisk_blocksize_attr = __ATTR(blocksize, S_IRUGO, vdisk_sysfs_blocksize_show, NULL); static struct kobj_attribute vdisk_rd_only_attr = @@ -402,6 +422,9 @@ static struct kobj_attribute vdev_prod_id_attr = static struct kobj_attribute vdev_prod_rev_lvl_attr = __ATTR(prod_rev_lvl, S_IWUSR|S_IRUGO, vdev_sysfs_prod_rev_lvl_show, vdev_sysfs_prod_rev_lvl_store); +static struct kobj_attribute vdev_scsi_device_name_attr = + __ATTR(scsi_device_name, S_IWUSR|S_IRUGO, vdev_sysfs_scsi_device_name_show, + vdev_sysfs_scsi_device_name_store); static struct kobj_attribute vdev_t10_dev_id_attr = __ATTR(t10_dev_id, S_IWUSR|S_IRUGO, vdev_sysfs_t10_dev_id_show, vdev_sysfs_t10_dev_id_store); @@ -419,7 +442,8 @@ static struct kobj_attribute vcdrom_filename_attr = vcdrom_sysfs_filename_store); static const struct attribute *vdisk_fileio_attrs[] = { - &vdev_size_attr.attr, + &vdev_size_ro_attr.attr, + &vdev_size_mb_ro_attr.attr, &vdisk_blocksize_attr.attr, &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, @@ -434,6 +458,7 @@ static const struct attribute *vdisk_fileio_attrs[] = { &vdev_vend_specific_id_attr.attr, &vdev_prod_id_attr.attr, &vdev_prod_rev_lvl_attr.attr, + &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, @@ -442,7 +467,8 @@ static const struct attribute *vdisk_fileio_attrs[] = { }; static const struct attribute *vdisk_blockio_attrs[] = { - &vdev_size_attr.attr, + &vdev_size_ro_attr.attr, + &vdev_size_mb_ro_attr.attr, &vdisk_blocksize_attr.attr, &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, @@ -455,6 +481,7 @@ static const struct attribute *vdisk_blockio_attrs[] = { &vdev_vend_specific_id_attr.attr, &vdev_prod_id_attr.attr, &vdev_prod_rev_lvl_attr.attr, + &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, @@ -463,7 +490,8 @@ static const struct attribute *vdisk_blockio_attrs[] = { }; static const struct attribute *vdisk_nullio_attrs[] = { - &vdev_size_attr.attr, + &vdev_size_rw_attr.attr, + &vdev_size_mb_rw_attr.attr, &vdisk_blocksize_attr.attr, &vdisk_rd_only_attr.attr, &vdev_dummy_attr.attr, @@ -472,6 +500,7 @@ static const struct attribute *vdisk_nullio_attrs[] = { &vdev_vend_specific_id_attr.attr, &vdev_prod_id_attr.attr, &vdev_prod_rev_lvl_attr.attr, + &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, @@ -480,12 +509,14 @@ static const struct attribute *vdisk_nullio_attrs[] = { }; static const struct attribute *vcdrom_attrs[] = { - &vdev_size_attr.attr, + &vdev_size_ro_attr.attr, + &vdev_size_mb_ro_attr.attr, &vcdrom_filename_attr.attr, &vdev_t10_vend_id_attr.attr, &vdev_vend_specific_id_attr.attr, &vdev_prod_id_attr.attr, &vdev_prod_rev_lvl_attr.attr, + &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, @@ -499,7 +530,7 @@ static DEFINE_MUTEX(scst_vdisk_mutex); /* * Protects the device attributes t10_vend_id, vend_specific_id, prod_id, - * prod_rev_lvl, t10_dev_id, usn and inq_vend_specific. + * prod_rev_lvl, scsi_device_name, t10_dev_id, usn and inq_vend_specific. */ static DEFINE_RWLOCK(vdisk_serial_rwlock); @@ -632,7 +663,9 @@ static struct scst_dev_type vdisk_null_devtype = { "dummy, " "read_only, " "removable, " - "rotational", + "rotational, " + "size, " + "size_mb", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -942,17 +975,15 @@ static int vdisk_attach(struct scst_device *dev) dev->dev_rd_only = virt_dev->rd_only; if (!virt_dev->cdrom_empty) { - if (virt_dev->nullio) - err = VDISK_NULLIO_SIZE; - else { + if (!virt_dev->nullio) { res = vdisk_get_file_size(virt_dev->filename, virt_dev->blockio, &err); if (res != 0) goto out; - } - virt_dev->file_size = err; + virt_dev->file_size = err; - TRACE_DBG("size of file: %lld", (long long unsigned int)err); + TRACE_DBG("size of file: %lld", err); + } vdisk_blockio_check_flush_support(virt_dev); vdisk_check_tp_support(virt_dev); @@ -1123,12 +1154,12 @@ static enum compl_status_e vdisk_synchronize_cache(struct vdisk_cmd_params *p) cmd->completed = 1; cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); - vdisk_fsync(p, loff, data_len, dev, cmd->cmd_gfp_mask, NULL, true); + vdisk_fsync(loff, data_len, dev, cmd->cmd_gfp_mask, NULL, true); /* ToDo: vdisk_fsync() error processing */ scst_cmd_put(cmd); res = RUNNING_ASYNC; } else { - vdisk_fsync(p, loff, data_len, dev, cmd->cmd_gfp_mask, cmd, true); + vdisk_fsync(loff, data_len, dev, cmd->cmd_gfp_mask, cmd, true); res = RUNNING_ASYNC; } @@ -1144,7 +1175,7 @@ static enum compl_status_e vdisk_exec_start_stop(struct vdisk_cmd_params *p) TRACE_ENTRY(); - vdisk_fsync(p, 0, virt_dev->file_size, dev, cmd->cmd_gfp_mask, cmd, false); + vdisk_fsync(0, virt_dev->file_size, dev, cmd->cmd_gfp_mask, cmd, false); TRACE_EXIT(); return CMD_SUCCEEDED; @@ -1405,9 +1436,9 @@ static vdisk_op_fn blockio_ops[256] = { [WRITE_VERIFY] = blockio_exec_write_verify, [WRITE_VERIFY_12] = blockio_exec_write_verify, [WRITE_VERIFY_16] = blockio_exec_write_verify, - [VERIFY] = blockio_exec_verify, - [VERIFY_12] = blockio_exec_verify, - [VERIFY_16] = blockio_exec_verify, + [VERIFY] = vdev_exec_verify, + [VERIFY_12] = vdev_exec_verify, + [VERIFY_16] = vdev_exec_verify, SHARED_OPS }; @@ -1420,12 +1451,13 @@ static vdisk_op_fn fileio_ops[256] = { [WRITE_10] = fileio_exec_write, [WRITE_12] = fileio_exec_write, [WRITE_16] = fileio_exec_write, + [COMPARE_AND_WRITE] = vdisk_exec_caw, [WRITE_VERIFY] = fileio_exec_write_verify, [WRITE_VERIFY_12] = fileio_exec_write_verify, [WRITE_VERIFY_16] = fileio_exec_write_verify, - [VERIFY] = fileio_exec_verify, - [VERIFY_12] = fileio_exec_verify, - [VERIFY_16] = fileio_exec_verify, + [VERIFY] = vdev_exec_verify, + [VERIFY_12] = vdev_exec_verify, + [VERIFY_16] = vdev_exec_verify, SHARED_OPS }; @@ -1526,6 +1558,7 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) case WRITE_10: case WRITE_12: case WRITE_16: + case COMPARE_AND_WRITE: fua = (cdb[1] & 0x8); if (fua) { TRACE(TRACE_ORDER, "FUA: loff=%lld, " @@ -2536,6 +2569,23 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) int num = 4; buf[1] = 0x83; + + read_lock(&vdisk_serial_rwlock); + i = strlen(virt_dev->scsi_device_name); + if (i > 0) { + /* SCSI target device name */ + buf[num + 0] = 0x3; /* ASCII */ + buf[num + 1] = 0x20 | 0x8; /* Target device SCSI name */ + i += 4 - i % 4; /* align to required 4 bytes */ + scst_copy_and_fill_b(&buf[num + 4], virt_dev->scsi_device_name, i, '\0'); + + buf[num + 3] = i; + num += buf[num + 3]; + + num += 4; + } + read_unlock(&vdisk_serial_rwlock); + /* T10 vendor identifier field format (faked) */ buf[num + 0] = 0x2; /* ASCII */ buf[num + 1] = 0x1; /* Vendor ID */ @@ -2612,6 +2662,7 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) buf[1] = 0xB0; buf[3] = 0x3C; buf[4] = 1; /* WSNZ set */ + buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */ /* Optimal transfer granuality is PAGE_SIZE */ put_unaligned_be16(max_t(int, PAGE_SIZE/dev->block_size, 1), &buf[6]); @@ -3354,7 +3405,7 @@ static enum compl_status_e vdisk_exec_read_capacity(struct vdisk_cmd_params *p) nblocks = virt_dev->nblocks; if ((cmd->cdb[8] & 1) == 0) { - uint64_t lba = get_unaligned_be64(&cmd->cdb[2]); + uint32_t lba = get_unaligned_be32(&cmd->cdb[2]); if (lba != 0) { TRACE_DBG("PMI zero and LBA not zero (cmd %p)", cmd); scst_set_cmd_error(cmd, @@ -3623,7 +3674,7 @@ static enum compl_status_e vdisk_exec_prevent_allow_medium_removal(struct vdisk_ return CMD_SUCCEEDED; } -static int vdisk_fsync_blockio(struct vdisk_cmd_params *p, loff_t loff, +static int vdisk_fsync_blockio(loff_t loff, loff_t len, struct scst_device *dev, gfp_t gfp_flags, struct scst_cmd *cmd, bool async) { @@ -3646,7 +3697,7 @@ static int vdisk_fsync_blockio(struct vdisk_cmd_params *p, loff_t loff, return res; } -static int vdisk_fsync_fileio(struct vdisk_cmd_params *p, loff_t loff, +static int vdisk_fsync_fileio(loff_t loff, loff_t len, struct scst_device *dev, struct scst_cmd *cmd, bool async) { int res; @@ -3697,7 +3748,7 @@ static int vdisk_fsync_fileio(struct vdisk_cmd_params *p, loff_t loff, return res; } -static int vdisk_fsync(struct vdisk_cmd_params *p, loff_t loff, +static int vdisk_fsync(loff_t loff, loff_t len, struct scst_device *dev, gfp_t gfp_flags, struct scst_cmd *cmd, bool async) { @@ -3722,10 +3773,12 @@ static int vdisk_fsync(struct vdisk_cmd_params *p, loff_t loff, goto out; } - if (virt_dev->blockio) - res = vdisk_fsync_blockio(p, loff, len, dev, gfp_flags, cmd, async); + if (virt_dev->nullio) + ; + else if (virt_dev->blockio) + res = vdisk_fsync_blockio(loff, len, dev, gfp_flags, cmd, async); else - res = vdisk_fsync_fileio(p, loff, len, dev, cmd, async); + res = vdisk_fsync_fileio(loff, len, dev, cmd, async); out: TRACE_EXIT_RES(res); @@ -4016,7 +4069,7 @@ restart: out_sync: /* O_DSYNC flag is used for WT devices */ if (p->fua) - vdisk_fsync(p, loff, scst_cmd_get_data_len(cmd), cmd->dev, + vdisk_fsync(loff, scst_cmd_get_data_len(cmd), cmd->dev, cmd->cmd_gfp_mask, cmd, false); out: TRACE_EXIT(); @@ -4382,65 +4435,198 @@ out: return res; } -static enum compl_status_e fileio_exec_verify(struct vdisk_cmd_params *p) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) +static int blockio_end_sync_io(struct bio *bio, unsigned int bytes_done, + int error) +#else +static void blockio_end_sync_io(struct bio *bio, int error) +#endif +{ + struct completion *c = bio->bi_private; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + if (bio->bi_size) + return 1; +#endif + + if (!bio_flagged(bio, BIO_UPTODATE) && error == 0) { + PRINT_ERROR("Not up to date bio with error 0; returning -EIO"); + error = -EIO; + } + + bio->bi_private = (void *)(unsigned long)error; + complete(c); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + return 0; +#else + return; +#endif +} + +static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, + size_t len, loff_t *loff, unsigned rw) +{ + DECLARE_COMPLETION_ONSTACK(c); + struct block_device *bdev = virt_dev->bdev; + struct bio *bio; + void *p; + int max_nr_vecs, rc; + unsigned bytes, off; + ssize_t ret = -ENOMEM; + + max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_PAGES); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + bio = bio_kmalloc(GFP_KERNEL, max_nr_vecs); +#else + bio = bio_alloc(GFP_KERNEL, max_nr_vecs); +#endif + + if (!bio) + goto out; + + bio->bi_rw = rw; + bio->bi_bdev = bdev; + bio->bi_end_io = blockio_end_sync_io; + bio->bi_private = &c; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) + bio->bi_sector = *loff >> 9; +#else + bio->bi_iter.bi_sector = *loff >> 9; +#endif + for (p = buf; p < buf + len; p += bytes) { + off = offset_in_page(p); + bytes = PAGE_SIZE - off; + rc = bio_add_page(bio, virt_to_page(p), bytes, off); + if (WARN_ON_ONCE(rc < bytes)) + goto free; + } + submit_bio(rw, bio); + wait_for_completion(&c); + ret = (unsigned long)bio->bi_private ? : len; + +free: + bio_put(bio); + +out: + return ret; +} + +static ssize_t fileio_read_sync(struct file *fd, void *buf, size_t len, + loff_t *loff) +{ + mm_segment_t old_fs; + ssize_t ret; + + old_fs = get_fs(); + set_fs(get_ds()); + + if (fd->f_op->llseek) + ret = fd->f_op->llseek(fd, *loff, 0/*SEEK_SET*/); + else + ret = default_llseek(fd, *loff, 0/*SEEK_SET*/); + if (ret < 0) + goto out; + + ret = vfs_read(fd, (char __force __user *)buf, len, loff); + +out: + set_fs(old_fs); + + return ret; +} + +static ssize_t fileio_write_sync(struct file *fd, void *buf, size_t len, + loff_t *loff) +{ + mm_segment_t old_fs; + ssize_t ret; + + old_fs = get_fs(); + set_fs(get_ds()); + + if (fd->f_op->llseek) + ret = fd->f_op->llseek(fd, *loff, 0/*SEEK_SET*/); + else + ret = default_llseek(fd, *loff, 0/*SEEK_SET*/); + if (ret < 0) + goto out; + + ret = vfs_write(fd, (char __force __user *)buf, len, loff); + +out: + set_fs(old_fs); + + return ret; +} +static ssize_t vdev_read_sync(struct scst_vdisk_dev *virt_dev, void *buf, + size_t len, loff_t *loff) +{ + if (virt_dev->nullio) + return len; + else if (virt_dev->blockio) + return blockio_rw_sync(virt_dev, buf, len, loff, 0/*read*/); + else + return fileio_read_sync(virt_dev->fd, buf, len, loff); +} + +static ssize_t vdev_write_sync(struct scst_vdisk_dev *virt_dev, void *buf, + size_t len, loff_t *loff) +{ + int rw; + + if (virt_dev->nullio) { + return len; + } else if (virt_dev->blockio) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + rw = REQ_WRITE; +#else + rw = 1 << BIO_RW; +#endif + + return blockio_rw_sync(virt_dev, buf, len, loff, rw); + } else { + return fileio_write_sync(virt_dev->fd, buf, len, loff); + } +} + +static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) { struct scst_cmd *cmd = p->cmd; loff_t loff = p->loff; - mm_segment_t old_fs; loff_t err; ssize_t length, len_mem = 0; uint8_t *address_sav, *address = NULL; int compare; struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; - struct file *fd = virt_dev->fd; uint8_t *mem_verify = NULL; int64_t data_len = scst_cmd_get_data_len(cmd); TRACE_ENTRY(); - sBUG_ON(virt_dev->blockio); - - if (vdisk_fsync(p, loff, data_len, cmd->dev, + if (vdisk_fsync(loff, data_len, cmd->dev, cmd->cmd_gfp_mask, cmd, false) != 0) goto out; /* - * Until the cache is cleared prior the verifying, there is not - * much point in this code. ToDo. + * For file I/O, unless the cache is cleared prior the verifying, + * there is not much point in this code. ToDo. * * Nevertherless, this code is valuable if the data have not been read * from the file/disk yet. */ compare = scst_cmd_get_data_direction(cmd) == SCST_DATA_WRITE; - TRACE_DBG("VERIFY with BYTCHK=%d at offset %lld and len %lld\n", + TRACE_DBG("VERIFY with compare %d at offset %lld and len %lld\n", compare, loff, (long long)data_len); - /* SEEK */ - old_fs = get_fs(); - set_fs(get_ds()); - - if (!virt_dev->nullio) { - if (fd->f_op->llseek) - err = fd->f_op->llseek(fd, loff, 0/*SEEK_SET*/); - else - err = default_llseek(fd, loff, 0/*SEEK_SET*/); - if (err != loff) { - PRINT_ERROR("lseek trouble %lld != %lld", - (long long unsigned int)err, - (long long unsigned int)loff); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_read_error)); - goto out_set_fs; - } - } - mem_verify = vmalloc(LEN_MEM); if (mem_verify == NULL) { PRINT_ERROR("Unable to allocate memory %d for verify", LEN_MEM); scst_set_busy(cmd); - goto out_set_fs; + goto out; } if (compare) { @@ -4453,11 +4639,7 @@ static enum compl_status_e fileio_exec_verify(struct vdisk_cmd_params *p) len_mem = (length > LEN_MEM) ? LEN_MEM : length; TRACE_DBG("Verify: length %zd - len_mem %zd", length, len_mem); - if (!virt_dev->nullio) - err = vfs_read(fd, (char __force __user *)mem_verify, - len_mem, &loff); - else - err = len_mem; + err = vdev_read_sync(virt_dev, mem_verify, len_mem, &loff); if ((err < 0) || (err < len_mem)) { PRINT_ERROR("verify() returned %lld from %zd", (long long unsigned int)err, len_mem); @@ -4469,14 +4651,14 @@ static enum compl_status_e fileio_exec_verify(struct vdisk_cmd_params *p) } if (compare) scst_put_buf(cmd, address_sav); - goto out_set_fs; + goto out_free; } if (compare && memcmp(address, mem_verify, len_mem) != 0) { TRACE_DBG("Verify: error memcmp length %zd", length); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_miscompare_error)); scst_put_buf(cmd, address_sav); - goto out_set_fs; + goto out_free; } length -= len_mem; if (compare) @@ -4494,8 +4676,7 @@ static enum compl_status_e fileio_exec_verify(struct vdisk_cmd_params *p) SCST_LOAD_SENSE(scst_sense_hardw_error)); } -out_set_fs: - set_fs(old_fs); +out_free: if (mem_verify) vfree(mem_verify); @@ -4504,6 +4685,106 @@ out: return CMD_SUCCEEDED; } +/* COMPARE AND WRITE */ +static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) +{ + struct scst_cmd *cmd = p->cmd; + struct scst_device *dev = cmd->dev; + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + uint32_t data_len = scst_cmd_get_data_len(cmd); + int length, i; + uint8_t *caw_buf = NULL, *read_buf = NULL; + loff_t loff, read, written; + + if (unlikely(cmd->cdb[1] & 0xE0)) { + TRACE_DBG("%s", "WRPROTECT not supported"); + scst_set_invalid_field_in_cdb(cmd, 1, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 5); + goto out; + } + + /* + * A NUMBER OF LOGICAL BLOCKS field set to zero specifies that no read + * operations shall be performed, no logical block data shall be + * transferred from the Data-Out Buffer, no compare operations shall + * be performed, and no write operations shall be performed. This + * condition shall not be considered an error. + */ + if (data_len == 0) + goto out; + + length = scst_get_buf_full(cmd, &caw_buf); + read_buf = vmalloc(data_len); + if (length < 0 || !read_buf) { + PRINT_ERROR("scst_get_buf_full() failed: %d", length); + if (length == -ENOMEM || !read_buf) + scst_set_busy(cmd); + else + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + goto out; + } + + WARN_ON_ONCE(length != 2 * data_len); + + loff = p->loff; + read = vdev_read_sync(virt_dev, read_buf, data_len, &loff); + if (read < data_len) { + PRINT_ERROR("COMPARE AND WRITE / READ returned %lld from %d", + read, data_len); + if (read == -EAGAIN) + scst_set_busy(cmd); + else + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_read_error)); + goto out; + } + + if (memcmp(caw_buf, read_buf, data_len) != 0) { + for (i = 0; i < data_len && caw_buf[i] == read_buf[i]; i++) + ; + /* + * SBC-3 $5.2: if the compare operation does not indicate a + * match, then terminate the command with CHECK CONDITION + * status with the sense key set to MISCOMPARE and the + * additional sense code set to MISCOMPARE DURING VERIFY + * OPERATION. In the sense data (see 4.18 and SPC-4) the + * offset from the start of the Data-Out Buffer to the first + * byte of data that was not equal shall be reported in the + * INFORMATION field. + */ + scst_set_cmd_error_and_inf(cmd, + SCST_LOAD_SENSE(scst_sense_miscompare_error), + p->loff + i); + goto out; + } + + loff = p->loff; + written = vdev_write_sync(virt_dev, caw_buf + data_len, data_len, + &loff); + if (written < data_len) { + PRINT_ERROR("COMPARE AND WRITE / WRITE wrote %lld / %d", + written, data_len); + if (written == -EAGAIN) + scst_set_busy(cmd); + else + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_write_error)); + goto out; + } + if (p->fua) + vdisk_fsync(loff, scst_cmd_get_data_len(cmd), cmd->dev, + cmd->cmd_gfp_mask, cmd, false); + +out: + if (read_buf) + vfree(read_buf); + if (caw_buf) + scst_put_buf_full(cmd, caw_buf); + + return CMD_SUCCEEDED; +} + static enum compl_status_e blockio_exec_write_verify(struct vdisk_cmd_params *p) { /* Not yet implemented */ @@ -4511,18 +4792,12 @@ static enum compl_status_e blockio_exec_write_verify(struct vdisk_cmd_params *p) return blockio_exec_write(p); } -static enum compl_status_e blockio_exec_verify(struct vdisk_cmd_params *p) -{ - /* Not yet implemented */ - return CMD_SUCCEEDED; -} - static enum compl_status_e fileio_exec_write_verify(struct vdisk_cmd_params *p) { fileio_exec_write(p); /* O_DSYNC flag is used for WT devices */ if (scsi_status_is_good(p->cmd->status)) - fileio_exec_verify(p); + vdev_exec_verify(p); return CMD_SUCCEEDED; } @@ -4723,8 +4998,7 @@ static int vdev_create(struct scst_dev_type *devt, TRACE_DBG("t10_dev_id %s", virt_dev->t10_dev_id); sprintf(virt_dev->t10_vend_id, "%.*s", - (int)(sizeof(virt_dev->t10_vend_id) - 1), - virt_dev->blockio ? SCST_BIO_VENDOR : SCST_FIO_VENDOR); + (int)sizeof(virt_dev->t10_vend_id) - 1, SCST_FIO_VENDOR); sprintf(virt_dev->vend_specific_id, "%.*s", (int)(sizeof(virt_dev->vend_specific_id) - 1), @@ -4736,6 +5010,9 @@ static int vdev_create(struct scst_dev_type *devt, sprintf(virt_dev->prod_rev_lvl, "%.*s", (int)(sizeof(virt_dev->prod_rev_lvl) - 1), SCST_FIO_REV); + sprintf(virt_dev->scsi_device_name, "%.*s", + (int)(sizeof(virt_dev->scsi_device_name) - 1), ""); + scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", dev_id_num); TRACE_DBG("usn %s", virt_dev->usn); @@ -4898,6 +5175,10 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, virt_dev->thin_provisioned); } else if (!strcasecmp("zero_copy", p)) { virt_dev->zero_copy = !!val; + } else if (!strcasecmp("size", p)) { + virt_dev->file_size = val; + } else if (!strcasecmp("size_mb", p)) { + virt_dev->file_size = val * 1024 * 1024; } else if (!strcasecmp("blocksize", p)) { virt_dev->blk_shift = scst_calc_block_shift(val); if (virt_dev->blk_shift < 9) { @@ -4914,6 +5195,12 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } } + if (virt_dev->file_size % (1 << virt_dev->blk_shift) != 0) { + PRINT_ERROR("Device size %lld is not a multiple of the block" + " size %d", virt_dev->file_size, + 1 << virt_dev->blk_shift); + res = -EINVAL; + } out: TRACE_EXIT_RES(res); return res; @@ -4999,6 +5286,8 @@ static int vdev_blockio_add_device(const char *device_name, char *params) virt_dev->blockio = 1; virt_dev->wt_flag = DEF_WRITE_THROUGH; + sprintf(virt_dev->t10_vend_id, "%.*s", + (int)sizeof(virt_dev->t10_vend_id) - 1, SCST_BIO_VENDOR); res = vdev_parse_add_dev_params(virt_dev, params, allowed_params); if (res != 0) @@ -5042,7 +5331,7 @@ static int vdev_nullio_add_device(const char *device_name, char *params) int res = 0; static const char *const allowed_params[] = { "read_only", "dummy", "removable", "blocksize", "rotational", - NULL + "size", "size_mb", NULL }; struct scst_vdisk_dev *virt_dev; @@ -5055,6 +5344,7 @@ static int vdev_nullio_add_device(const char *device_name, char *params) virt_dev->command_set_version = 0x04C0; /* SBC-3 */ virt_dev->nullio = 1; + virt_dev->file_size = VDISK_NULLIO_SIZE; res = vdev_parse_add_dev_params(virt_dev, params, allowed_params); if (res != 0) @@ -5492,22 +5782,128 @@ out_free: goto out; } -static ssize_t vdev_sysfs_size_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) +static int vdev_size_process_store(struct scst_sysfs_work_item *work) +{ + struct scst_device *dev = work->dev; + struct scst_vdisk_dev *virt_dev; + unsigned long long new_size; + int size_shift, res = -EINVAL; + + if (sscanf(work->buf, "%d %lld", &size_shift, &new_size) != 2 || + new_size > (ULONG_MAX >> size_shift)) + goto put; + + new_size <<= size_shift; + + res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER); + if (res) + goto put; + + /* To sync with detach*() functions */ + res = mutex_lock_interruptible(&scst_mutex); + if (res) + goto resume; + + virt_dev = dev->dh_priv; + if (!virt_dev->nullio) { + res = -EPERM; + sBUG(); + } else if (new_size % (1 << virt_dev->blk_shift) == 0) { + virt_dev->file_size = new_size; + virt_dev->nblocks = virt_dev->file_size >> dev->block_shift; + } else { + res = -EINVAL; + } + + mutex_unlock(&scst_mutex); + + if (res == 0) + scst_capacity_data_changed(dev); + +resume: + scst_resume_activity(); + +put: + kobject_put(&dev->dev_kobj); + return res; +} + +static ssize_t vdev_size_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t count, int size_shift) +{ + struct scst_device *dev = container_of(kobj, struct scst_device, + dev_kobj); + struct scst_sysfs_work_item *work; + char *new_size; + int res = -ENOMEM; + + + new_size = kasprintf(GFP_KERNEL, "%d %.*s", size_shift, (int)count, + buf); + if (!new_size) + goto out; + + res = scst_alloc_sysfs_work(vdev_size_process_store, false, &work); + if (res) + goto out_free; + + work->buf = new_size; + work->dev = dev; + + SCST_SET_DEP_MAP(work, &scst_dev_dep_map); + kobject_get(&dev->dev_kobj); + + res = scst_sysfs_queue_wait_work(work); + if (res == 0) + res = count; + +out: + return res; + +out_free: + kfree(buf); + goto out; +} + +static ssize_t vdev_sysfs_size_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + return vdev_size_store(kobj, attr, buf, count, 0); +} + +static ssize_t vdev_size_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf, int size_shift) { - int pos = 0; struct scst_device *dev; struct scst_vdisk_dev *virt_dev; - - TRACE_ENTRY(); + unsigned long long size; dev = container_of(kobj, struct scst_device, dev_kobj); virt_dev = dev->dh_priv; + size = ACCESS_ONCE(virt_dev->file_size); - pos = sprintf(buf, "%lld\n", virt_dev->file_size / 1024 / 1024); + return sprintf(buf, "%llu\n%s", size >> size_shift, + virt_dev->nullio && size != VDISK_NULLIO_SIZE ? + SCST_SYSFS_KEY_MARK "\n" : ""); +} - TRACE_EXIT_RES(pos); - return pos; +static ssize_t vdev_sysfs_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return vdev_size_show(kobj, attr, buf, 0); +} + +static ssize_t vdev_sysfs_size_mb_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + return vdev_size_store(kobj, attr, buf, count, 20); +} + +static ssize_t vdev_sysfs_size_mb_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return vdev_size_show(kobj, attr, buf, 20); } static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj, @@ -6068,6 +6464,66 @@ static ssize_t vdev_sysfs_prod_rev_lvl_show(struct kobject *kobj, return pos; } +static ssize_t vdev_sysfs_scsi_device_name_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + char *p; + int res, len; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + p = memchr(buf, '\n', count); + len = p ? p - buf : count; + + if (len >= sizeof(virt_dev->scsi_device_name)) { + PRINT_ERROR("SCSI device namel is too long (max %zd characters)", + sizeof(virt_dev->scsi_device_name)); + res = -EINVAL; + goto out; + } + + write_lock(&vdisk_serial_rwlock); + sprintf(virt_dev->scsi_device_name, "%.*s", len, buf); + if (strlen(virt_dev->scsi_device_name) > 0) + virt_dev->scsi_device_name_set = 1; + else + virt_dev->scsi_device_name_set = 0; + write_unlock(&vdisk_serial_rwlock); + + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t vdev_sysfs_scsi_device_name_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int pos; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + pos = sprintf(buf, "%s\n%s", virt_dev->scsi_device_name, + virt_dev->scsi_device_name_set ? SCST_SYSFS_KEY_MARK "\n" : ""); + read_unlock(&vdisk_serial_rwlock); + + TRACE_EXIT_RES(pos); + return pos; +} + static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { @@ -6502,6 +6958,9 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, virt_dev->blockio = 1; /* Bad hack for anyway going out procfs */ virt_dev->vdev_devt = &vdisk_blk_devtype; + sprintf(virt_dev->t10_vend_id, "%.*s", + (int)sizeof(virt_dev->t10_vend_id) - 1, + SCST_BIO_VENDOR); TRACE_DBG("%s", "BLOCKIO"); } else if (!strncmp("REMOVABLE", p, 9)) { p += 9; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 6c86a813f..da49e0c15 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -150,6 +150,8 @@ static int get_cdb_info_write_same10(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_write_same16(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); +static int get_cdb_info_compare_and_write(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops); static int get_cdb_info_apt(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_min(struct scst_cmd *cmd, @@ -966,6 +968,14 @@ static const struct scst_sdbops scst_scsi_op_table[] = { .info_lba_off = 2, .info_lba_len = 8, .info_len_off = 10, .info_len_len = 4, .get_cdb_info = get_cdb_info_lba_8_len_4}, + {.ops = 0x89, .devkey = "O ", + .info_op_name = "COMPARE AND WRITE", + .info_data_direction = SCST_DATA_WRITE, + .info_op_flags = SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM| + SCST_SERIALIZED, + .info_lba_off = 2, .info_lba_len = 8, + .info_len_off = 13, .info_len_len = 1, + .get_cdb_info = get_cdb_info_compare_and_write}, {.ops = 0x8A, .devkey = "O OO O ", .info_op_name = "WRITE(16)", .info_data_direction = SCST_DATA_WRITE, @@ -1656,6 +1666,38 @@ out: } EXPORT_SYMBOL(scst_set_cmd_error); +int scst_set_cmd_error_and_inf(struct scst_cmd *cmd, int key, int asc, + int ascq, uint64_t information) +{ + int res; + + res = scst_set_cmd_error(cmd, key, asc, ascq); + if (res) + goto out; + + switch (cmd->sense[0] & 0x7f) { + case 0x70: + /* Fixed format */ + cmd->sense[0] |= 0x80; /* Information field is valid */ + put_unaligned_be32(information, &cmd->sense[3]); + break; + case 0x72: + /* Descriptor format */ + cmd->sense[7] = 12; /* additional sense length */ + cmd->sense[8 + 0] = 0; /* descriptor type: Information */ + cmd->sense[8 + 1] = 10; /* Additional length */ + cmd->sense[8 + 2] = 0x80; /* VALID */ + put_unaligned_be64(information, &cmd->sense[8 + 4]); + break; + default: + sBUG(); + } + +out: + return res; +} +EXPORT_SYMBOL(scst_set_cmd_error_and_inf); + static void scst_fill_field_pointer_sense(uint8_t *fp_sense, int field_offs, int bit_offs, bool cdb) { @@ -3831,7 +3873,7 @@ found: t->acg_dev->acg->acg_io_grouping_type); } else { res = t; - if (!*(volatile bool*)&res->active_cmd_threads->io_context_ready) { + if (!*(volatile bool *)&res->active_cmd_threads->io_context_ready) { TRACE_DBG("IO context for t %p not yet " "initialized, waiting...", t); msleep(100); @@ -4110,6 +4152,10 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev->tgt_dev_rd_only = acg_dev->acg_dev_rd_only || dev->dev_rd_only; tgt_dev->sess = sess; atomic_set(&tgt_dev->tgt_dev_cmd_count, 0); + if (sess->acg->acg_black_hole_type != SCST_ACG_BLACK_HOLE_NONE) + set_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); + else + clear_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); scst_sgv_pool_use_norm(tgt_dev); @@ -4233,7 +4279,7 @@ out_free: goto out; } -/* No locks supposed to be held, scst_mutex - held */ +/* scst_mutex supposed to be held */ void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA) { TRACE_ENTRY(); @@ -4531,6 +4577,24 @@ out: return res; } +static void scst_prelim_finish_internal_cmd(struct scst_cmd *cmd) +{ + unsigned long flags; + + TRACE_ENTRY(); + + sBUG_ON(!cmd->internal); + + spin_lock_irqsave(&cmd->sess->sess_list_lock, flags); + list_del(&cmd->sess_cmd_list_entry); + spin_unlock_irqrestore(&cmd->sess->sess_list_lock, flags); + + __scst_cmd_put(cmd); + + TRACE_EXIT(); + return; +} + int scst_prepare_request_sense(struct scst_cmd *orig_cmd) { int res = 0; @@ -4734,7 +4798,7 @@ out: return res; out_free_cmd: - __scst_cmd_put(cmd); + scst_prelim_finish_internal_cmd(cmd); out_busy: scst_set_busy(ws_cmd); @@ -6436,8 +6500,16 @@ static int get_cdb_info_fmt(struct scst_cmd *cmd, static int get_cdb_info_verify10(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { + if (unlikely(cmd->cdb[1] & 4)) { + PRINT_ERROR("VERIFY(10): BYTCHK 1x not supported (dev %s)", + cmd->dev ? cmd->dev->virt_name : NULL); + scst_set_invalid_field_in_cdb(cmd, 1, + 2 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return 1; + } + cmd->lba = get_unaligned_be32(cmd->cdb + sdbops->info_lba_off); - if (cmd->cdb[1] & BYTCHK) { + if (cmd->cdb[1] & 2) { cmd->bufflen = get_unaligned_be16(cmd->cdb + sdbops->info_len_off); cmd->data_len = cmd->bufflen; cmd->data_direction = SCST_DATA_WRITE; @@ -6455,7 +6527,15 @@ static int get_cdb_info_verify6(struct scst_cmd *cmd, cmd->op_flags |= SCST_LBA_NOT_VALID; cmd->lba = 0; - if (cmd->cdb[1] & BYTCHK) { + if (unlikely(cmd->cdb[1] & 4)) { + PRINT_ERROR("VERIFY(6): BYTCHK 1x not supported (dev %s)", + cmd->dev ? cmd->dev->virt_name : NULL); + scst_set_invalid_field_in_cdb(cmd, 1, + 2 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return 1; + } + + if (cmd->cdb[1] & 2) { /* BYTCHK 01 */ cmd->bufflen = get_unaligned_be24(cmd->cdb + sdbops->info_len_off); cmd->data_len = cmd->bufflen; cmd->data_direction = SCST_DATA_WRITE; @@ -6470,8 +6550,16 @@ static int get_cdb_info_verify6(struct scst_cmd *cmd, static int get_cdb_info_verify12(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { + if (unlikely(cmd->cdb[1] & 4)) { + PRINT_ERROR("VERIFY(12): BYTCHK 1x not supported (dev %s)", + cmd->dev ? cmd->dev->virt_name : NULL); + scst_set_invalid_field_in_cdb(cmd, 1, + 2 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return 1; + } + cmd->lba = get_unaligned_be32(cmd->cdb + sdbops->info_lba_off); - if (cmd->cdb[1] & BYTCHK) { + if (cmd->cdb[1] & 2) { /* BYTCHK 01 */ cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { PRINT_ERROR("Too big bufflen %d (op %x)", @@ -6492,8 +6580,16 @@ static int get_cdb_info_verify12(struct scst_cmd *cmd, static int get_cdb_info_verify16(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { + if (unlikely(cmd->cdb[1] & 4)) { + PRINT_ERROR("VERIFY(16): BYTCHK 1x not supported (dev %s)", + cmd->dev ? cmd->dev->virt_name : NULL); + scst_set_invalid_field_in_cdb(cmd, 1, + 2 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return 1; + } + cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); - if (cmd->cdb[1] & BYTCHK) { + if (cmd->cdb[1] & 2) { /* BYTCHK 01 */ cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { PRINT_ERROR("Too big bufflen %d (op %x)", @@ -6684,6 +6780,15 @@ static int get_cdb_info_write_same16(struct scst_cmd *cmd, return 0; } +static int get_cdb_info_compare_and_write(struct scst_cmd *cmd, + const struct scst_sdbops *sdbops) +{ + cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); + cmd->data_len = cmd->cdb[sdbops->info_len_off]; + cmd->bufflen = 2 * cmd->data_len; + return 0; +} + /** * get_cdb_info_apt() - Parse ATA PASS-THROUGH CDB. * @@ -6799,7 +6904,8 @@ static int get_cdb_info_min(struct scst_cmd *cmd, break; case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: cmd->op_name = "REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS"; - cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED; + cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED | + SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; break; default: break; diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 2be0ab797..d3e99cbac 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1925,7 +1925,7 @@ out_wait: * Wait for io_context gets initialized to avoid possible races * for it from the sharing it tgt_devs. */ - while (!*(volatile bool*)&cmd_threads->io_context_ready) { + while (!*(volatile bool *)&cmd_threads->io_context_ready) { TRACE_DBG("Waiting for io_context for cmd_threads %p " "initialized", cmd_threads); msleep(50); diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index e7210d433..5ab3b6eb1 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -2457,7 +2457,7 @@ void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, if (buffer_size < 8) { TRACE_PR("buffer_size too small: %d. expected >= 8 " "(buffer %p)", buffer_size, buffer); - goto skip; + goto out; } memset(b, 0, sizeof(b)); @@ -2491,10 +2491,10 @@ void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, size = 24; } - memset(buffer, 0, buffer_size); - memcpy(buffer, b, min(size, buffer_size)); - -skip: +out: + size = min(size, buffer_size); + memcpy(buffer, b, size); + memset(buffer + size, 0, buffer_size - size); scst_set_resp_data_len(cmd, size); TRACE_EXIT(); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index c7e08ab7d..8ca303484 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -1756,6 +1756,124 @@ static struct kobj_attribute scst_tgt_io_grouping_type = scst_tgt_io_grouping_type_show, scst_tgt_io_grouping_type_store); +static ssize_t __scst_acg_black_hole_show(struct scst_acg *acg, char *buf) +{ + int res, t = acg->acg_black_hole_type; + + res = sprintf(buf, "%d\n", t); + + return res; +} + +static ssize_t __scst_acg_black_hole_store(struct scst_acg *acg, + const char *buf, size_t count) +{ + int res = 0; + int prev, t; + struct scst_session *sess; + + prev = acg->acg_black_hole_type; + + if ((buf == NULL) || (count == 0)) { + res = 0; + goto out; + } + + mutex_lock(&scst_mutex); + + BUILD_BUG_ON((SCST_ACG_BLACK_HOLE_NONE != 0) || + (SCST_ACG_BLACK_HOLE_CMD != 1) || + (SCST_ACG_BLACK_HOLE_ALL != 2) || + (SCST_ACG_BLACK_HOLE_DATA_CMD != 3) || + (SCST_ACG_BLACK_HOLE_DATA_MCMD != 4)); + switch (buf[0]) { + case '0': + acg->acg_black_hole_type = SCST_ACG_BLACK_HOLE_NONE; + break; + case '1': + acg->acg_black_hole_type = SCST_ACG_BLACK_HOLE_CMD; + break; + case '2': + acg->acg_black_hole_type = SCST_ACG_BLACK_HOLE_ALL; + break; + case '3': + acg->acg_black_hole_type = SCST_ACG_BLACK_HOLE_DATA_CMD; + break; + case '4': + acg->acg_black_hole_type = SCST_ACG_BLACK_HOLE_DATA_MCMD; + break; + default: + PRINT_ERROR("%s: Requested action not understood: %s", + __func__, buf); + res = -EINVAL; + goto out_unlock; + } + + t = acg->acg_black_hole_type; + + if (prev == t) + goto out_unlock; + + list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) { + int i; + for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { + struct list_head *head = &sess->sess_tgt_dev_list[i]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + if (t != SCST_ACG_BLACK_HOLE_NONE) + set_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); + else + clear_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); + } + } + } + + PRINT_INFO("Black hole set to %d for ACG %s", t, acg->acg_name); + +out_unlock: + mutex_unlock(&scst_mutex); + +out: + return res; +} + +static ssize_t scst_tgt_black_hole_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_acg *acg; + struct scst_tgt *tgt; + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + acg = tgt->default_acg; + + return __scst_acg_black_hole_show(acg, buf); +} + +static ssize_t scst_tgt_black_hole_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_acg *acg; + struct scst_tgt *tgt; + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + acg = tgt->default_acg; + + res = __scst_acg_black_hole_store(acg, buf, count); + if (res != 0) + goto out; + + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static struct kobj_attribute scst_tgt_black_hole = + __ATTR(black_hole, S_IRUGO | S_IWUSR, + scst_tgt_black_hole_show, scst_tgt_black_hole_store); + static ssize_t __scst_acg_cpu_mask_show(struct scst_acg *acg, char *buf) { int res; @@ -2473,6 +2591,7 @@ static struct attribute *scst_tgt_attrs[] = { &scst_tgt_comment.attr, &scst_tgt_addr_method.attr, &scst_tgt_io_grouping_type.attr, + &scst_tgt_black_hole.attr, &scst_tgt_cpu_mask.attr, &scst_tgt_unknown_cmd_count_attr.attr, &scst_tgt_write_cmd_count_attr.attr, @@ -4285,6 +4404,39 @@ static struct kobj_attribute scst_acg_io_grouping_type = scst_acg_io_grouping_type_show, scst_acg_io_grouping_type_store); +static ssize_t scst_acg_black_hole_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_acg *acg; + + acg = container_of(kobj, struct scst_acg, acg_kobj); + + return __scst_acg_black_hole_show(acg, buf); +} + +static ssize_t scst_acg_black_hole_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_acg *acg; + + acg = container_of(kobj, struct scst_acg, acg_kobj); + + res = __scst_acg_black_hole_store(acg, buf, count); + if (res != 0) + goto out; + + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static struct kobj_attribute scst_acg_black_hole = + __ATTR(black_hole, S_IRUGO | S_IWUSR, + scst_acg_black_hole_show, scst_acg_black_hole_store); + static ssize_t scst_acg_cpu_mask_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -4408,6 +4560,13 @@ int scst_acg_sysfs_create(struct scst_tgt *tgt, goto out_del; } + res = sysfs_create_file(&acg->acg_kobj, &scst_acg_black_hole.attr); + if (res != 0) { + PRINT_ERROR("Can't add tgt attr %s for tgt %s", + scst_acg_black_hole.attr.name, tgt->tgt_name); + goto out_del; + } + res = sysfs_create_file(&acg->acg_kobj, &scst_acg_cpu_mask.attr); if (res != 0) { PRINT_ERROR("Can't add tgt attr %s for tgt %s", diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 697083bf6..705b24263 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -972,6 +972,32 @@ out: } #endif + if (unlikely(test_bit(SCST_TGT_DEV_BLACK_HOLE, &cmd->tgt_dev->tgt_dev_flags))) { + struct scst_session *sess = cmd->sess; + bool abort = false; + switch (sess->acg->acg_black_hole_type) { + case SCST_ACG_BLACK_HOLE_CMD: + case SCST_ACG_BLACK_HOLE_ALL: + abort = true; + break; + case SCST_ACG_BLACK_HOLE_DATA_CMD: + case SCST_ACG_BLACK_HOLE_DATA_MCMD: + if (cmd->data_direction != SCST_DATA_NONE) + abort = true; + break; + default: + break; + } + if (abort) { + TRACE_MGMT_DBG("Black hole: aborting cmd %p (op %x, " + "initiator %s)", cmd, cmd->cdb[0], + sess->initiator_name); + spin_lock_irq(&sess->sess_list_lock); + scst_abort_cmd(cmd, NULL, false, false); + spin_unlock_irq(&sess->sess_list_lock); + } + } + TRACE_EXIT_HRES(res); return res; @@ -2036,6 +2062,74 @@ out_unlock_put_not_completed: goto out; } +static int scst_report_supported_tm_fns(struct scst_cmd *cmd) +{ + int res = SCST_EXEC_COMPLETED; + int length, resp_len = 0; + uint8_t *address; + uint8_t buf[16]; + + TRACE_ENTRY(); + + length = scst_get_buf_full_sense(cmd, &address); + TRACE_DBG("length %d", length); + if (unlikely(length <= 0)) + goto out_compl; + + memset(buf, 0, sizeof(buf)); + + buf[0] = 0xD8; /* ATS, ATSS, CTSS, LURS */ + buf[1] = 0; + if ((cmd->cdb[2] & 0x80) == 0) + resp_len = 4; + else { + buf[3] = 0x0C; +#if 1 + buf[4] = 1; /* TMFTMOV */ + buf[6] = 0x80; /* ATTS */ + put_unaligned_be32(300, &buf[8]); /* long timeout - 30 sec. */ + put_unaligned_be32(150, &buf[12]); /* short timeout - 15 sec. */ +#endif + resp_len = 16; + } + + if (length > resp_len) + length = resp_len; + memcpy(address, buf, length); + + scst_put_buf_full(cmd, address); + if (length < cmd->resp_data_len) + scst_set_resp_data_len(cmd, length); + +out_compl: + cmd->completed = 1; + + /* Report the result */ + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_maintenance_in(struct scst_cmd *cmd) +{ + int res; + + TRACE_ENTRY(); + + switch (cmd->cdb[1] & 0x1f) { + case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: + res = scst_report_supported_tm_fns(cmd); + break; + default: + res = SCST_EXEC_NOT_COMPLETED; + break; + } + + TRACE_EXIT_RES(res); + return res; +} + static int scst_reserve_local(struct scst_cmd *cmd) { int res = SCST_EXEC_NOT_COMPLETED; @@ -2777,6 +2871,7 @@ static scst_local_exec_fn scst_local_fns[256] = { [PERSISTENT_RESERVE_OUT] = scst_persistent_reserve_out_local, [REPORT_LUNS] = scst_report_luns_local, [REQUEST_SENSE] = scst_request_sense_local, + [MAINTENANCE_IN] = scst_maintenance_in, }; static int scst_do_local_exec(struct scst_cmd *cmd) @@ -5397,10 +5492,18 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd) * >0, if it should be requeued, <0 otherwise */ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) { - int res = 0, rc; + int res = 0, rc, t; TRACE_ENTRY(); + t = mcmd->sess->acg->acg_black_hole_type; + if (unlikely((t == SCST_ACG_BLACK_HOLE_ALL) || + (t == SCST_ACG_BLACK_HOLE_DATA_MCMD))) { + TRACE_MGMT_DBG("Dropping mcmd %p (fn %d, initiator %s)", mcmd, + mcmd->fn, mcmd->sess->initiator_name); + mcmd->mcmd_dropped = 1; + } + switch (mcmd->fn) { case SCST_ABORT_TASK: { diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 5953ae38e..e872bdeaa 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -1657,11 +1657,6 @@ static int scst_local_driver_remove(struct device *dev) TRACE_ENTRY(); sess = to_scst_lcl_sess(dev); - if (!sess) { - PRINT_ERROR("%s", "Unable to locate sess info"); - return -ENODEV; - } - scsi_remove_host(sess->shost); scsi_host_put(sess->shost); @@ -1726,8 +1721,6 @@ static void scst_local_release_adapter(struct device *dev) TRACE_ENTRY(); sess = to_scst_lcl_sess(dev); - if (sess == NULL) - goto out; /* * At this point the SCSI device is almost gone because the SCSI @@ -1760,7 +1753,6 @@ static void scst_local_release_adapter(struct device *dev) scst_unregister_session(sess->scst_sess, false, scst_local_free_sess); -out: TRACE_EXIT(); return; } diff --git a/srpt/Makefile b/srpt/Makefile index 3a5c1c8c7..2f0e28c96 100644 --- a/srpt/Makefile +++ b/srpt/Makefile @@ -40,6 +40,8 @@ INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra set_var = $(shell { if [ -e "$(1)" ]; then grep -v '^$(2)=' "$(1)"; fi; echo "$(2)=$(3)"; } >/tmp/$(1)-$$$$.tmp && mv /tmp/$(1)-$$$$.tmp $(1)) +SRC_FILES=$(wildcard */*.[ch]) + # The file Modules.symvers has been renamed in the 2.6.18 kernel to # Module.symvers. Find out which name to use by looking in $(KDIR). MODULE_SYMVERS:=$(shell if [ -e $(KDIR)/Module.symvers ]; then \ @@ -113,7 +115,7 @@ src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) "installed."; \ false; \ else \ - echo " Building against non-OFED InfiniBand kernel headers."; \ + echo " Building against in-tree InfiniBand kernel headers."; \ cp $< $@; \ fi; \ fi @@ -141,4 +143,7 @@ extraclean: clean release-archive: ../scripts/generate-release-archive srpt "$$(sed -n 's/^#define[[:blank:]]DRV_VERSION[[:blank:]]*\"\([^\"]*\)\".*/\1/p' src/ib_srpt.c)" +kerneldoc.html: $(SRC_FILES) + $(KDIR)/scripts/kernel-doc -html $(SRC_FILES) >$@ + .PHONY: all install clean extraclean 2debug 2release 2perf diff --git a/srpt/README b/srpt/README index e15498f84..01297583d 100644 --- a/srpt/README +++ b/srpt/README @@ -411,7 +411,7 @@ Q: Loading the kernel module ib_srpt triggers a kernel panic with a call trace [] system_call_fastpath+0x16/0x1b A: This means that you are using a system on which OFED has been installed but - that ib_srpt has been compiled against the non-OFED kernel headers instead + that ib_srpt has been compiled against the in-tree kernel headers instead of the OFED kernel headers. You can fix this by rebuilding ib_srpt against the OFED kernel headers. The ib_srpt makefile should detect the OFED kernel headers automatically - at least if ib_srpt is built after OFED has been diff --git a/srpt/session-management.txt b/srpt/session-management.txt new file mode 100644 index 000000000..752218787 --- /dev/null +++ b/srpt/session-management.txt @@ -0,0 +1,45 @@ + ib_srpt and session management + ============================== + +The following actions related to SRP sessions can all occur concurrently: +* IB communication manager (CM) invokes srpt_cm_handler(). +* HCA driver invokes the queue pair (QP) completion handler srpt_completion(). +* HCA driver invokes the QP async event handler srpt_qp_event(). +* HCA transfers data between initiator and target via RDMA. +* srpt_compl_thread() polls the QP. +* SCST core invokes one of the callback functions defined in srpt_template(). + +The actions that occur over the lifetime of a session are as follows: +- A REQ message is received from the initiator. +- srpt_cm_req_recv() is invoked and allocates a queue pair and creates a + completion thread. +- If the connection request is not accepted, a REJ message is sent and + srpt_close_ch() is invoked. The srpt_close_ch() call causes the completion + thread to stop and the allocated resources to be freed asynchronously. +- If the connection request is accepted a REP message is sent to the initiator. +- Once an RTU message is received from the initiator, srpt_cm_rtu_recv() is + invoked. That function changes the queue pair state into RTS, the channel + state into CH_LIVE and wakes up the completion thread. +- RDMA communication starts and continues until either a DREQ message is + received or sent. The function ib_send_cm_dreq() can get invoked + either because a target port is disabled or from inside the + srpt_close_session() function. +- After a DREQ has been sent either a DREP will be received + (srpt_cm_drep_recv()) or the TimeWait state will be reached and will be left + (srpt_cm_timewait_exit()). +- srpt_cm_dre[pq]_recv() and srpt_cm_timewait_exit() all invoke + srpt_close_ch(). +- srpt_close_ch() changes the channel state into CH_DISCONNECTING, the + queue pair state into IB_QPS_ERR and queues a zero-length write. +- Upon receipt of the zero-length write completion the channel state is + changed into CH_DISCONNECTED. This is the last work completion so once the + channel state has reached CH_DISCONNECTED it is guaranteed that the queue + pair completion handler won't be invoked again and also that the completion + handler won't call wake_up_process(ch->thread) anymore. +- That channel state change causes the polling loop in the completion thread + to stop and also triggers a call of scst_unregister_session(). +- Once scst_unregister_session() has finished srpt_unreg_sess() is invoked. +- srpt_unreg_sess() destroys the CM ID and decrements the channel refcount. +- Once the channel refcount drops to zero srpt_free_ch() is invoked which + frees the queue pair and other IB resources. + diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 6b9b54fa8..05bcbc4b5 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -181,10 +181,9 @@ static void srpt_unregister_procfs_entry(struct scst_tgt_template *tgt); #endif /*CONFIG_SCST_PROC*/ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch, struct srpt_send_ioctx *ioctx); -static void srpt_drain_channel(struct ib_cm_id *cm_id); static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch); -static enum rdma_ch_state srpt_set_ch_state_to_disc(struct srpt_rdma_ch *ch) +static bool srpt_set_ch_state(struct srpt_rdma_ch *ch, enum rdma_ch_state new) { unsigned long flags; enum rdma_ch_state prev; @@ -192,57 +191,7 @@ static enum rdma_ch_state srpt_set_ch_state_to_disc(struct srpt_rdma_ch *ch) spin_lock_irqsave(&ch->spinlock, flags); prev = ch->state; - switch (prev) { - case CH_CONNECTING: - case CH_LIVE: - ch->state = CH_DISCONNECTING; - wake_up_process(ch->thread); - changed = true; - break; - default: - break; - } - spin_unlock_irqrestore(&ch->spinlock, flags); - - return prev; -} - -static bool srpt_set_ch_state_to_draining(struct srpt_rdma_ch *ch) -{ - unsigned long flags; - bool changed = false; - - spin_lock_irqsave(&ch->spinlock, flags); - switch (ch->state) { - case CH_CONNECTING: - case CH_LIVE: - case CH_DISCONNECTING: - ch->state = CH_DRAINING; - wake_up_process(ch->thread); - changed = true; - break; - default: - break; - } - spin_unlock_irqrestore(&ch->spinlock, flags); - - return changed; -} - -/** - * srpt_test_and_set_ch_state() - Test and set the channel state. - * - * Returns true if and only if the channel state has been set to the new state. - */ -static bool srpt_test_and_set_ch_state(struct srpt_rdma_ch *ch, - enum rdma_ch_state old, - enum rdma_ch_state new) -{ - unsigned long flags; - bool changed = false; - - spin_lock_irqsave(&ch->spinlock, flags); - if (ch->state == old) { + if (new > prev) { ch->state = new; wake_up_process(ch->thread); changed = true; @@ -341,6 +290,7 @@ static void srpt_event_handler(struct ib_event_handler *handler, case IB_EVENT_PKEY_CHANGE: case IB_EVENT_SM_CHANGE: case IB_EVENT_CLIENT_REREGISTER: + case IB_EVENT_GID_CHANGE: /* Refresh port data asynchronously. */ port_num = event->element.port_num - 1; if (port_num < sdev->device->phys_port_cnt) { @@ -378,8 +328,8 @@ static const char *get_ch_state_name(enum rdma_ch_state s) return "live"; case CH_DISCONNECTING: return "disconnecting"; - case CH_DRAINING: - return "draining"; + case CH_DISCONNECTED: + return "disconnected"; } return "???"; } @@ -389,8 +339,6 @@ static const char *get_ch_state_name(enum rdma_ch_state s) */ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) { - unsigned long flags; - TRACE_DBG("QP event %d on cm_id=%p sess_name=%s state=%s", event->event, ch->cm_id, ch->sess_name, get_ch_state_name(ch->state)); @@ -408,11 +356,6 @@ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) case IB_EVENT_QP_LAST_WQE_REACHED: TRACE_DBG("%s, state %s: received Last WQE event.", ch->sess_name, get_ch_state_name(ch->state)); - BUG_ON(!ch->thread); - spin_lock_irqsave(&ch->spinlock, flags); - ch->last_wqe_received = true; - wake_up_process(ch->thread); - spin_unlock_irqrestore(&ch->spinlock, flags); break; default: PRINT_ERROR("received unrecognized IB QP event %d", @@ -926,13 +869,11 @@ static enum srpt_command_state srpt_set_cmd_state(struct srpt_send_ioctx *ioctx, { enum srpt_command_state previous; - BUG_ON(!ioctx); + EXTRACHECKS_BUG_ON(!ioctx); - spin_lock(&ioctx->spinlock); previous = ioctx->state; if (previous != SRPT_STATE_DONE) ioctx->state = new; - spin_unlock(&ioctx->spinlock); return previous; } @@ -948,15 +889,13 @@ static bool srpt_test_and_set_cmd_state(struct srpt_send_ioctx *ioctx, { enum srpt_command_state previous; - WARN_ON(!ioctx); - WARN_ON(old == SRPT_STATE_DONE); - WARN_ON(new == SRPT_STATE_NEW); + EXTRACHECKS_BUG_ON(!ioctx); + EXTRACHECKS_BUG_ON(old == SRPT_STATE_DONE); + EXTRACHECKS_BUG_ON(new == SRPT_STATE_NEW); - spin_lock(&ioctx->spinlock); previous = ioctx->state; if (previous == old) ioctx->state = new; - spin_unlock(&ioctx->spinlock); return previous == old; } @@ -986,15 +925,7 @@ static int srpt_post_recv(struct srpt_device *sdev, static int srpt_adjust_srq_wr_avail(struct srpt_rdma_ch *ch, int delta) { - int res; - unsigned long flags; - - spin_lock_irqsave(&ch->spinlock, flags); - ch->sq_wr_avail += delta; - res = ch->sq_wr_avail; - spin_unlock_irqrestore(&ch->spinlock, flags); - - return res; + return atomic_add_return(delta, &ch->sq_wr_avail); } /** @@ -1038,6 +969,25 @@ out: return ret; } +/** + * srpt_zerolength_write() - Perform a zero-length RDMA write. + * + * A quote from the InfiniBand specification: C9-88: For an HCA responder + * using Reliable Connection service, for each zero-length RDMA READ or WRITE + * request, the R_Key shall not be validated, even if the request includes + * Immediate data. + */ +static int srpt_zerolength_write(struct srpt_rdma_ch *ch) +{ + struct ib_send_wr wr, *bad_wr; + + memset(&wr, 0, sizeof(wr)); + wr.opcode = IB_WR_RDMA_WRITE; + wr.wr_id = encode_wr_id(SRPT_RDMA_ZEROLENGTH_WRITE, 0xffffffffUL); + wr.send_flags = IB_SEND_SIGNALED; + return ib_post_send(ch->qp, &wr, &bad_wr); +} + /** * srpt_get_desc_tbl() - Parse the data descriptors of an SRP_CMD request. * @ioctx: Pointer to the I/O context associated with the request. @@ -1060,6 +1010,7 @@ static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx, struct srp_direct_buf *db; unsigned add_cdb_offset; int ret; + u8 fmt; /* * The pointer computations below will only be compiled correctly @@ -1084,13 +1035,18 @@ static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx, * buffer descriptor format, and the highest four bits contain the * DATA-OUT buffer descriptor format. */ - *dir = SCST_DATA_NONE; - if (srp_cmd->buf_fmt & 0xf) + fmt = srp_cmd->buf_fmt; + if (fmt & 0xf) { /* DATA-IN: transfer data from target to initiator (read). */ *dir = SCST_DATA_READ; - else if (srp_cmd->buf_fmt >> 4) + fmt = fmt & 0xf; + } else if (fmt >> 4) { /* DATA-OUT: transfer data from initiator to target (write). */ *dir = SCST_DATA_WRITE; + fmt = fmt >> 4; + } else { + *dir = SCST_DATA_NONE; + } /* * According to the SRP spec, the lower two bits of the 'ADDITIONAL @@ -1098,8 +1054,7 @@ static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx, * is four times the value specified in bits 3..7. Hence the "& ~3". */ add_cdb_offset = srp_cmd->add_cdb_len & ~3; - if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_DIRECT) || - ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_DIRECT)) { + if (fmt == SRP_DATA_DESC_DIRECT) { ioctx->n_rbuf = 1; ioctx->rbufs = &ioctx->single_rbuf; @@ -1107,8 +1062,7 @@ static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx, + add_cdb_offset); memcpy(ioctx->rbufs, db, sizeof(*db)); *data_len = be32_to_cpu(db->len); - } else if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_INDIRECT) || - ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_INDIRECT)) { + } else if (fmt == SRP_DATA_DESC_INDIRECT) { idb = (struct srp_indirect_buf *)(srp_cmd->add_data + add_cdb_offset); @@ -1142,7 +1096,11 @@ static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx, db = idb->desc_list; memcpy(ioctx->rbufs, db, ioctx->n_rbuf * sizeof(*db)); *data_len = be32_to_cpu(idb->len); + } else if (fmt != 0) { + PRINT_ERROR("Unsupported data format %d\n", fmt); + ret = -EINVAL; } + out: return ret; } @@ -1353,44 +1311,31 @@ static void srpt_put_send_ioctx(struct srpt_send_ioctx *ioctx) * srpt_abort_cmd() - Make SCST stop processing a SCSI command. * @ioctx: I/O context associated with the SCSI command. * @context: Preferred execution context. + * + * Must only be called when the I/O context is in a state where it is waiting + * for the HCA. */ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, enum scst_exec_context context) { - struct scst_cmd *scmnd; - enum srpt_command_state state; + struct scst_cmd *scmnd = &ioctx->scmnd; + enum srpt_command_state state = ioctx->state; TRACE_ENTRY(); - BUG_ON(!ioctx); - - /* - * If the command is in a state where the target core is waiting for - * the ib_srpt driver, change the state to the next state. Changing - * the state of the command from SRPT_STATE_NEED_DATA to - * SRPT_STATE_DATA_IN ensures that srpt_xmit_response() will call this - * function a second time. - */ - spin_lock(&ioctx->spinlock); - state = ioctx->state; switch (state) { case SRPT_STATE_NEED_DATA: ioctx->state = SRPT_STATE_DATA_IN; break; - case SRPT_STATE_DATA_IN: case SRPT_STATE_CMD_RSP_SENT: case SRPT_STATE_MGMT_RSP_SENT: ioctx->state = SRPT_STATE_DONE; break; default: + WARN_ONCE(true, "%s: unexpected I/O context state %d\n", + __func__, state); break; } - spin_unlock(&ioctx->spinlock); - - if (state == SRPT_STATE_DONE) - goto out; - - scmnd = &ioctx->scmnd; WARN_ON(ioctx != scst_cmd_get_tgt_priv(scmnd)); @@ -1401,11 +1346,7 @@ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, case SRPT_STATE_NEW: case SRPT_STATE_DATA_IN: case SRPT_STATE_MGMT: - /* - * Do nothing - defer abort processing until - * srpt_xmit_response() is invoked. - */ - WARN_ON(!scst_cmd_aborted_on_xmit(scmnd)); + case SRPT_STATE_DONE: break; case SRPT_STATE_NEED_DATA: /* SCST_DATA_WRITE - RDMA read error or RDMA read timeout. */ @@ -1429,60 +1370,74 @@ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, * management commands. Note: the SCST core frees these * commands immediately after srpt_tsk_mgmt_done() returned. */ - WARN(true, "Unexpected command state %d", state); - break; - default: - WARN(true, "Unexpected command state %d", state); + WARN(true, "Unexpected command state %d\n", state); break; } -out: - ; - TRACE_EXIT(); } +void srpt_on_abort_cmd(struct scst_cmd *cmd) +{ + struct srpt_send_ioctx *ioctx = scst_cmd_get_tgt_priv(cmd); + struct srpt_rdma_ch *ch = ioctx->ch; + + if (ch->state >= CH_DISCONNECTED) { + switch (ioctx->state) { + case SRPT_STATE_NEW: + case SRPT_STATE_DATA_IN: + case SRPT_STATE_MGMT: + case SRPT_STATE_DONE: + /* + * An SCST command thread is busy processing the + * command associated with the I/O context, so wait + * until that processing has finished. + */ + break; + case SRPT_STATE_NEED_DATA: + case SRPT_STATE_CMD_RSP_SENT: + case SRPT_STATE_MGMT_RSP_SENT: + PRINT_ERROR("Cmd %p: IB completion for idx %u has not" + " been received in time (SRPT command state" + " %d)", cmd, ioctx->ioctx.index, + ioctx->state); + srpt_abort_cmd(ioctx, SCST_CONTEXT_THREAD); + break; + } + } +} + /** * srpt_handle_send_err_comp() - Process an IB_WC_SEND error completion. */ static void srpt_handle_send_err_comp(struct srpt_rdma_ch *ch, u64 wr_id, enum scst_exec_context context) { - struct srpt_send_ioctx *ioctx; - enum srpt_command_state state; - struct scst_cmd *scmnd; - u32 index; + u32 index = idx_from_wr_id(wr_id); + struct srpt_send_ioctx *ioctx = ch->ioctx_ring[index]; + enum srpt_command_state state = ioctx->state; srpt_adjust_srq_wr_avail(ch, 1); - index = idx_from_wr_id(wr_id); - ioctx = ch->ioctx_ring[index]; - state = ioctx->state; - scmnd = &ioctx->scmnd; - - EXTRACHECKS_WARN_ON(state != SRPT_STATE_CMD_RSP_SENT - && state != SRPT_STATE_MGMT_RSP_SENT - && state != SRPT_STATE_NEED_DATA - && state != SRPT_STATE_DONE); - - /* - * If SRP_RSP sending failed, undo the ch->req_lim and ch->req_lim_delta - * changes. - */ - if (state == SRPT_STATE_CMD_RSP_SENT - || state == SRPT_STATE_MGMT_RSP_SENT) - srpt_undo_inc_req_lim(ch, ioctx->req_lim_delta); switch (state) { - default: + case SRPT_STATE_NEED_DATA: + srpt_abort_cmd(ioctx, context); + break; + case SRPT_STATE_CMD_RSP_SENT: + srpt_undo_inc_req_lim(ch, ioctx->req_lim_delta); srpt_abort_cmd(ioctx, context); break; case SRPT_STATE_MGMT_RSP_SENT: + srpt_undo_inc_req_lim(ch, ioctx->req_lim_delta); srpt_put_send_ioctx(ioctx); break; case SRPT_STATE_DONE: PRINT_ERROR("Received more than one IB error completion" " for wr_id = %u.", (unsigned)index); break; + default: + EXTRACHECKS_WARN_ON(true); + break; } } @@ -1520,12 +1475,11 @@ static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch, enum srpt_opcode opcode, enum scst_exec_context context) { - struct scst_cmd *scmnd; + struct scst_cmd *scmnd = &ioctx->scmnd; EXTRACHECKS_WARN_ON(ioctx->n_rdma <= 0); srpt_adjust_srq_wr_avail(ch, ioctx->n_rdma); - scmnd = &ioctx->scmnd; if (opcode == SRPT_RDMA_READ_LAST && scmnd) { if (srpt_test_and_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA, SRPT_STATE_DATA_IN)) @@ -1536,7 +1490,7 @@ static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch, } else if (opcode == SRPT_RDMA_ABORT) { ioctx->rdma_aborted = true; } else { - WARN(true, "scmnd == NULL (opcode %d)", opcode); + WARN(true, "scmnd == NULL (opcode %d)\n", opcode); } } @@ -1548,11 +1502,9 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch, enum srpt_opcode opcode, enum scst_exec_context context) { - struct scst_cmd *scmnd; - enum srpt_command_state state; + struct scst_cmd *scmnd = &ioctx->scmnd; + enum srpt_command_state state = ioctx->state; - scmnd = &ioctx->scmnd; - state = ioctx->state; switch (opcode) { case SRPT_RDMA_READ_LAST: if (ioctx->n_rdma <= 0) { @@ -1569,6 +1521,12 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch, __LINE__, state); break; case SRPT_RDMA_WRITE_LAST: + /* + * Note: if an RDMA write error completion is received that + * means that a SEND has also been posted. Defer further + * processing of the associated command until the send error + * completion has been received. + */ scst_set_delivery_status(scmnd, SCST_CMD_DELIVERY_ABORTED); break; default: @@ -1688,18 +1646,15 @@ static int srpt_handle_cmd(struct srpt_rdma_ch *ch, scst_data_direction dir; u64 data_len; int ret; - int atomic; BUG_ON(!send_ioctx); srp_cmd = recv_ioctx->ioctx.buf; - atomic = context == SCST_CONTEXT_TASKLET ? SCST_ATOMIC - : SCST_NON_ATOMIC; scmnd = &send_ioctx->scmnd; ret = scst_rx_cmd_prealloced(scmnd, ch->scst_sess, (u8 *) &srp_cmd->lun, sizeof(srp_cmd->lun), srp_cmd->cdb, - sizeof(srp_cmd->cdb), atomic); + sizeof(srp_cmd->cdb), in_interrupt()); if (ret) { PRINT_ERROR("tag 0x%llx: SCST command initialization failed", srp_cmd->tag); @@ -1842,40 +1797,41 @@ static u8 scst_to_srp_tsk_mgmt_status(const int scst_mgmt_status) /** * srpt_handle_new_iu() - Process a newly received information unit. - * @ch: RDMA channel through which the information unit has been received. - * @ioctx: SRPT I/O context associated with the information unit. + * @ch: RDMA channel through which the information unit has been received. + * @recv_ioctx: SRPT I/O context associated with the information unit. + * @context: SCST command processing context. */ -static void srpt_handle_new_iu(struct srpt_rdma_ch *ch, - struct srpt_recv_ioctx *recv_ioctx, - struct srpt_send_ioctx *send_ioctx, - enum scst_exec_context context) +static struct srpt_send_ioctx * +srpt_handle_new_iu(struct srpt_rdma_ch *ch, + struct srpt_recv_ioctx *recv_ioctx, + enum scst_exec_context context) { + struct srpt_send_ioctx *send_ioctx = NULL; struct srp_cmd *srp_cmd; + u8 opcode; BUG_ON(!ch); BUG_ON(!recv_ioctx); + if (unlikely(ch->state == CH_CONNECTING)) + goto push; + ib_dma_sync_single_for_cpu(ch->sport->sdev->device, recv_ioctx->ioctx.dma, srp_max_req_size, DMA_FROM_DEVICE); srp_cmd = recv_ioctx->ioctx.buf; - if (unlikely(ch->state == CH_CONNECTING)) { - list_add_tail(&recv_ioctx->wait_list, &ch->cmd_wait_list); - goto out; + opcode = srp_cmd->opcode; + if (opcode == SRP_CMD || opcode == SRP_TSK_MGMT) { + send_ioctx = srpt_get_send_ioctx(ch); + if (unlikely(!send_ioctx)) + goto push; } - if (srp_cmd->opcode == SRP_CMD || srp_cmd->opcode == SRP_TSK_MGMT) { - if (!send_ioctx) - send_ioctx = srpt_get_send_ioctx(ch); - if (unlikely(!send_ioctx)) { - list_add_tail(&recv_ioctx->wait_list, - &ch->cmd_wait_list); - goto out; - } - } + if (!list_empty(&recv_ioctx->wait_list)) + list_del_init(&recv_ioctx->wait_list); - switch (srp_cmd->opcode) { + switch (opcode) { case SRP_CMD: srpt_handle_cmd(ch, recv_ioctx, send_ioctx, context); break; @@ -1895,14 +1851,20 @@ static void srpt_handle_new_iu(struct srpt_rdma_ch *ch, PRINT_ERROR("Received SRP_RSP"); break; default: - PRINT_ERROR("received IU with unknown opcode 0x%x", - srp_cmd->opcode); + PRINT_ERROR("received IU with unknown opcode 0x%x", opcode); break; } srpt_post_recv(ch->sport->sdev, recv_ioctx); + out: - return; + return send_ioctx; + +push: + if (list_empty(&recv_ioctx->wait_list)) + list_add_tail(&recv_ioctx->wait_list, + &ch->cmd_wait_list); + goto out; } static void srpt_process_rcv_completion(struct ib_cq *cq, @@ -1921,7 +1883,7 @@ static void srpt_process_rcv_completion(struct ib_cq *cq, if (unlikely(req_lim < 0)) PRINT_ERROR("req_lim = %d < 0", req_lim); ioctx = sdev->ioctx_ring[index]; - srpt_handle_new_iu(ch, ioctx, NULL, srpt_new_iu_context); + srpt_handle_new_iu(ch, ioctx, srpt_new_iu_context); } else { PRINT_INFO("receiving failed for idx %u with status %d", index, wc->status); @@ -1931,17 +1893,16 @@ static void srpt_process_rcv_completion(struct ib_cq *cq, static void srpt_process_wait_list(struct srpt_rdma_ch *ch) { struct srpt_recv_ioctx *recv_ioctx, *tmp; - struct srpt_send_ioctx *send_ioctx; + + ch->processing_wait_list = true; list_for_each_entry_safe(recv_ioctx, tmp, &ch->cmd_wait_list, wait_list) { - send_ioctx = srpt_get_send_ioctx(ch); - if (!send_ioctx) + if (!srpt_handle_new_iu(ch, recv_ioctx, srpt_new_iu_context)) break; - list_del(&recv_ioctx->wait_list); - srpt_handle_new_iu(ch, recv_ioctx, send_ioctx, - srpt_new_iu_context); } + + ch->processing_wait_list = false; } /** @@ -1976,8 +1937,12 @@ static void srpt_process_send_completion(struct ib_cq *cq, opcode == SRPT_RDMA_ABORT) { srpt_handle_rdma_comp(ch, ch->ioctx_ring[index], opcode, srpt_xmt_rsp_context); + } else if (opcode == SRPT_RDMA_ZEROLENGTH_WRITE) { + WARN_ONCE(true, "%s: QP not in error state\n", + ch->sess_name); + WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); } else { - WARN(true, "unexpected opcode %d", opcode); + WARN(true, "unexpected opcode %d\n", opcode); } } else { if (opcode == SRPT_SEND) { @@ -1993,40 +1958,58 @@ static void srpt_process_send_completion(struct ib_cq *cq, " and cables.", opcode, index, wc->status); srpt_handle_rdma_err_comp(ch, ch->ioctx_ring[index], opcode, srpt_xmt_rsp_context); + } else if (opcode == SRPT_RDMA_ZEROLENGTH_WRITE) { + WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); } else if (opcode != SRPT_RDMA_MID) { - WARN(true, "unexpected opcode %d", opcode); + WARN(true, "unexpected opcode %d\n", opcode); } } if (unlikely(!list_empty(&ch->cmd_wait_list) && - ch->state != CH_CONNECTING)) + ch->state != CH_CONNECTING && + !ch->processing_wait_list)) srpt_process_wait_list(ch); } -static void srpt_poll(struct srpt_rdma_ch *ch) +static void srpt_process_one_compl(struct srpt_rdma_ch *ch, struct ib_wc *wc) +{ + struct ib_cq *const cq = ch->cq; + + if (opcode_from_wr_id(wc->wr_id) == SRPT_RECV) + srpt_process_rcv_completion(cq, ch, wc); + else + srpt_process_send_completion(cq, ch, wc); +} + +static int srpt_poll(struct srpt_rdma_ch *ch, int budget) { struct ib_cq *const cq = ch->cq; struct ib_wc *const wc = ch->wc; - int i, n; + int i, n, processed = 0; - while ((n = ib_poll_cq(cq, ARRAY_SIZE(ch->wc), wc)) > 0) { - for (i = 0; i < n; i++) { - if (opcode_from_wr_id(wc[i].wr_id) == SRPT_RECV) - srpt_process_rcv_completion(cq, ch, &wc[i]); - else - srpt_process_send_completion(cq, ch, &wc[i]); - } + while ((n = ib_poll_cq(cq, min_t(int, ARRAY_SIZE(ch->wc), budget), + wc)) > 0) { + for (i = 0; i < n; i++) + srpt_process_one_compl(ch, &wc[i]); + budget -= n; + processed += n; } + + return processed; } -static void srpt_process_completion(struct srpt_rdma_ch *ch) +static int srpt_process_completion(struct srpt_rdma_ch *ch, int budget) { struct ib_cq *const cq = ch->cq; + int processed = 0, n = budget; do { - srpt_poll(ch); - } while (ib_req_notify_cq(cq, IB_CQ_NEXT_COMP | - IB_CQ_REPORT_MISSED_EVENTS) > 0); + processed += srpt_poll(ch, n); + n = ib_req_notify_cq(cq, IB_CQ_NEXT_COMP | + IB_CQ_REPORT_MISSED_EVENTS); + } while (n > 0); + + return processed; } /** @@ -2036,7 +2019,6 @@ static void srpt_completion(struct ib_cq *cq, void *ctx) { struct srpt_rdma_ch *ch = ctx; - BUG_ON(!ch->thread); wake_up_process(ch->thread); } @@ -2044,11 +2026,6 @@ static void srpt_free_ch(struct kref *kref) { struct srpt_rdma_ch *ch = container_of(kref, struct srpt_rdma_ch, kref); - /* - * The function call below will wait for the completion handler - * callback to finish and hence ensures that wake_up_process() won't - * be invoked anymore from that callback for the current thread. - */ srpt_destroy_ch_ib(ch); kfree(ch); @@ -2066,19 +2043,6 @@ static void srpt_unreg_sess(struct scst_session *scst_sess) sdev, ch->rq_size, ch->max_rsp_size, DMA_TO_DEVICE); - /* - * Note: if a DREQ is received after ch->dreq_received has been read, - * ib_destroy_cm_id() will send a DREP. - * - */ - if (ch->dreq_received) { - if (ib_send_cm_drep(ch->cm_id, NULL, 0) >= 0) - PRINT_INFO("Received DREQ and sent DREP for session %s", - ch->sess_name); - else - PRINT_ERROR("Sending DREP failed"); - } - /* * If the connection is still established, ib_destroy_cm_id() will * send a DREQ. @@ -2099,6 +2063,7 @@ static void srpt_unreg_sess(struct scst_session *scst_sess) static int srpt_compl_thread(void *arg) { + enum { poll_budget = 65536 }; struct srpt_rdma_ch *ch; /* Hibernation / freezing of the SRPT kernel thread is not supported. */ @@ -2107,30 +2072,12 @@ static int srpt_compl_thread(void *arg) ch = arg; BUG_ON(!ch); - set_current_state(TASK_INTERRUPTIBLE); -#if defined(__GNUC__) -#if (__GNUC__ * 100 + __GNUC_MINOR__) <= 406 - /* See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52925. */ - barrier(); -#endif -#endif - while (!ch->last_wqe_received && ch->state <= CH_LIVE) { - srpt_process_completion(ch); - schedule(); + while (ch->state < CH_DISCONNECTED) { set_current_state(TASK_INTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - - /* - * Process all IB (error) completions before invoking - * scst_unregister_session(). - */ - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - srpt_process_completion(ch); - if (atomic_read(&ch->scst_sess->sess_cmd_count) == 0) - break; - schedule_timeout(HZ / 10); + if (srpt_process_completion(ch, poll_budget) >= poll_budget) + cond_resched(); + else + schedule(); } set_current_state(TASK_RUNNING); @@ -2208,7 +2155,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) TRACE_DBG("qp_num = %#x", ch->qp->qp_num); - ch->sq_wr_avail = qp_init->cap.max_send_wr; + atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr); TRACE_DBG("%s: max_cqe= %d max_sge= %d sq_size = %d" " cm_id= %p", __func__, ch->cq->cqe, @@ -2235,15 +2182,8 @@ err_destroy_cq: static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) { - TRACE_ENTRY(); - - while (ib_poll_cq(ch->cq, ARRAY_SIZE(ch->wc), ch->wc) > 0) - ; - ib_destroy_qp(ch->qp); ib_destroy_cq(ch->cq); - - TRACE_EXIT(); } /** @@ -2260,7 +2200,6 @@ static bool __srpt_close_ch(struct srpt_rdma_ch *ch) __acquires(&ch->srpt_tgt->spinlock) { struct srpt_tgt *srpt_tgt = ch->srpt_tgt; - enum rdma_ch_state prev_state; int ret; bool was_live; @@ -2268,28 +2207,23 @@ static bool __srpt_close_ch(struct srpt_rdma_ch *ch) lockdep_assert_held(&srpt_tgt->spinlock); #endif - was_live = false; - - prev_state = srpt_set_ch_state_to_disc(ch); - - switch (prev_state) { - case CH_CONNECTING: - case CH_LIVE: - was_live = true; - break; - case CH_DISCONNECTING: - case CH_DRAINING: - break; - } - + was_live = srpt_set_ch_state(ch, CH_DISCONNECTING); if (was_live) { kref_get(&ch->kref); spin_unlock_irq(&srpt_tgt->spinlock); ret = srpt_ch_qp_err(ch); if (ret < 0) - PRINT_ERROR("Setting queue pair in error state" - " failed: %d", ret); + PRINT_ERROR("%s: changing queue pair into error state" + " failed: %d", ch->sess_name, ret); + + ret = srpt_zerolength_write(ch); + if (ret < 0) { + PRINT_ERROR("%s: queuing zero-length write failed: %d", + ch->sess_name, ret); + WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); + } + kref_put(&ch->kref, srpt_free_ch); spin_lock_irq(&srpt_tgt->spinlock); @@ -2310,36 +2244,9 @@ static void srpt_close_ch(struct srpt_rdma_ch *ch) spin_unlock_irq(&srpt_tgt->spinlock); } -/** - * srpt_drain_channel() - Drain a channel by resetting the IB queue pair. - * @cm_id: Pointer to the CM ID of the channel to be drained. - * - * Note: Must be called from inside srpt_cm_handler to avoid a race between - * accessing sdev->spinlock and the call to kfree(sdev) in srpt_remove_one() - * (the caller of srpt_cm_handler holds the cm_id spinlock; srpt_remove_one() - * waits until all target sessions for the associated IB device have been - * unregistered and target session registration involves a call to - * ib_destroy_cm_id(), which locks the cm_id spinlock and hence waits until - * this function has finished). - */ -static void srpt_drain_channel(struct ib_cm_id *cm_id) -{ - struct srpt_rdma_ch *ch; - int ret; - - WARN_ON_ONCE(irqs_disabled()); - - ch = cm_id->context; - if (srpt_set_ch_state_to_draining(ch)) { - ret = srpt_ch_qp_err(ch); - if (ret < 0) - PRINT_ERROR("Setting queue pair in error state" - " failed: %d", ret); - } -} - static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) { + struct srpt_nexus *nexus; struct srpt_rdma_ch *ch; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) @@ -2347,14 +2254,15 @@ static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) #endif restart: - list_for_each_entry(ch, &srpt_tgt->rch_list, list) { - if (ch->state >= CH_DISCONNECTING) - continue; - PRINT_INFO("Closing channel %s because target %s has been" - " disabled", ch->sess_name, - srpt_tgt->scst_tgt->tgt_name); - WARN_ON_ONCE(!__srpt_close_ch(ch)); - goto restart; + list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { + list_for_each_entry(ch, &nexus->ch_list, list) { + if (ib_send_cm_dreq(ch->cm_id, NULL, 0) < 0) + continue; + PRINT_INFO("Closing channel %s because target %s has" + " been disabled", ch->sess_name, + srpt_tgt->scst_tgt->tgt_name); + goto restart; + } } } @@ -2374,6 +2282,48 @@ static struct srpt_tgt *srpt_convert_scst_tgt(struct scst_tgt *scst_tgt) return srpt_tgt; } +/* + * Look up (i_port_id, t_port_id) in srpt_tgt->nexus_list. Create an entry if + * it does not yet exist. + */ +static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, + u8 i_port_id[16], u8 t_port_id[16]) +{ + unsigned long flags; + struct srpt_nexus *nexus = NULL, *tmp_nexus = NULL, *n; + + for (;;) { + spin_lock_irqsave(&srpt_tgt->spinlock, flags); + list_for_each_entry(n, &srpt_tgt->nexus_list, entry) { + if (memcmp(n->i_port_id, i_port_id, 16) == 0 && + memcmp(n->t_port_id, t_port_id, 16) == 0) { + nexus = n; + break; + } + } + if (!nexus && tmp_nexus) { + list_add_tail(&tmp_nexus->entry, &srpt_tgt->nexus_list); + swap(nexus, tmp_nexus); + } + spin_unlock_irqrestore(&srpt_tgt->spinlock, flags); + + if (nexus) + break; + tmp_nexus = kzalloc(sizeof(*nexus), GFP_KERNEL); + if (!tmp_nexus) { + nexus = ERR_PTR(-ENOMEM); + break; + } + INIT_LIST_HEAD(&tmp_nexus->ch_list); + memcpy(tmp_nexus->i_port_id, i_port_id, 16); + memcpy(tmp_nexus->t_port_id, t_port_id, 16); + } + + kfree(tmp_nexus); + + return nexus; +} + #if !defined(CONFIG_SCST_PROC) /** * srpt_enable_target - Set the "enabled" status of a target. @@ -2388,8 +2338,8 @@ static int srpt_enable_target(struct scst_tgt *scst_tgt, bool enable) if (!srpt_tgt) goto out; - TRACE_DBG("%s target %s", enable ? "Enabling" : "Disabling", - scst_tgt->tgt_name); + PRINT_INFO("%s target %s", enable ? "Enabling" : "Disabling", + scst_tgt->tgt_name); spin_lock_irq(&srpt_tgt->spinlock); srpt_tgt->enabled = enable; @@ -2428,10 +2378,11 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, struct srpt_port *const sport = &sdev->port[param->port - 1]; struct srpt_tgt *const srpt_tgt = one_target_per_port ? &sport->srpt_tgt : &sdev->srpt_tgt; + struct srpt_nexus *nexus; struct srp_login_req *req; - struct srp_login_rsp *rsp; - struct srp_login_rej *rej; - struct ib_cm_rep_param *rep_param; + struct srp_login_rsp *rsp = NULL; + struct srp_login_rej *rej = NULL; + struct ib_cm_rep_param *rep_param = NULL; struct srpt_rdma_ch *ch = NULL; struct task_struct *thread; u32 it_iu_len; @@ -2485,6 +2436,13 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[12]), be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[14])); + nexus = srpt_get_nexus(srpt_tgt, req->initiator_port_id, + req->target_port_id); + if (IS_ERR(nexus)) { + ret = PTR_ERR(nexus); + goto out; + } + ret = -ENOMEM; rsp = kzalloc(sizeof(*rsp), GFP_KERNEL); rej = kzalloc(sizeof(*rej), GFP_KERNEL); @@ -2537,8 +2495,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, PRINT_ERROR("Translating pkey %#x failed (%d) - using index 0", be16_to_cpu(param->primary_path->pkey), ret); } - memcpy(ch->i_port_id, req->initiator_port_id, 16); - memcpy(ch->t_port_id, req->target_port_id, 16); + ch->nexus = nexus; ch->sport = sport; ch->srpt_tgt = srpt_tgt; ch->cm_id = cm_id; @@ -2600,7 +2557,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, "0x%016llx%016llx", be64_to_cpu(*(__be64 *) &sdev->port[param->port - 1].gid.raw[8]), - be64_to_cpu(*(__be64 *)(ch->i_port_id + 8))); + be64_to_cpu(*(__be64 *)(nexus->i_port_id + 8))); } else { /* * Default behavior: use the initiator port identifier as the @@ -2608,16 +2565,16 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, */ snprintf(ch->sess_name, sizeof(ch->sess_name), "0x%016llx%016llx", - be64_to_cpu(*(__be64 *)ch->i_port_id), - be64_to_cpu(*(__be64 *)(ch->i_port_id + 8))); + be64_to_cpu(*(__be64 *)nexus->i_port_id), + be64_to_cpu(*(__be64 *)(nexus->i_port_id + 8))); } TRACE_DBG("registering session %s", ch->sess_name); BUG_ON(!srpt_tgt->scst_tgt); ret = -ENOMEM; - ch->scst_sess = scst_register_session(srpt_tgt->scst_tgt, 0, ch->sess_name, - ch, NULL, NULL); + ch->scst_sess = scst_register_session(srpt_tgt->scst_tgt, 0, + ch->sess_name, ch, NULL, NULL); if (!ch->scst_sess) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); TRACE_DBG("Failed to create SCST session"); @@ -2640,28 +2597,19 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_NO_CHAN; restart: - list_for_each_entry(ch2, &srpt_tgt->rch_list, list) { - if (!memcmp(ch2->i_port_id, req->initiator_port_id, 16) - && param->port == ch2->sport->port - && param->listen_id == ch2->sport->sdev->cm_id) { - if (!__srpt_close_ch(ch2)) - continue; - - PRINT_INFO("Relogin - closed existing channel" - " %s; cm_id = %p", ch2->sess_name, - ch2->cm_id); - - rsp->rsp_flags = - SRP_LOGIN_RSP_MULTICHAN_TERMINATED; - - goto restart; - } + list_for_each_entry(ch2, &nexus->ch_list, list) { + if (ib_send_cm_dreq(ch2->cm_id, NULL, 0) < 0) + continue; + PRINT_INFO("Relogin - closed existing channel %s", + ch2->sess_name); + rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_TERMINATED; + goto restart; } } else { rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_MAINTAINED; } - list_add_tail(&ch->list, &srpt_tgt->rch_list); + list_add_tail(&ch->list, &nexus->ch_list); ch->thread = thread; if (!srpt_tgt->enabled) { @@ -2778,7 +2726,6 @@ out: static void srpt_cm_rej_recv(struct ib_cm_id *cm_id) { PRINT_INFO("Received InfiniBand REJ packet for cm_id %p.", cm_id); - srpt_drain_channel(cm_id); } /** @@ -2793,35 +2740,49 @@ static void srpt_cm_rtu_recv(struct ib_cm_id *cm_id) int ret; ret = srpt_ch_qp_rts(ch, ch->qp); - if (ret == 0 && srpt_test_and_set_ch_state(ch, CH_CONNECTING, - CH_LIVE)) { - wake_up_process(ch->thread); - } else { + if (ret < 0) { + PRINT_ERROR("%s: QP transition to RTS failed", ch->sess_name); srpt_close_ch(ch); + return; } + /* + * Note: calling srpt_close_ch() if the transition to the LIVE state + * fails is not necessary since that means that that function has + * already been invoked from another thread. + */ + if (!srpt_set_ch_state(ch, CH_LIVE)) + PRINT_ERROR("%s: Channel transition to LIVE state failed", + ch->sess_name); } static void srpt_cm_timewait_exit(struct ib_cm_id *cm_id) { + struct srpt_rdma_ch *ch = cm_id->context; + PRINT_INFO("Received InfiniBand TimeWait exit for cm_id %p.", cm_id); - srpt_drain_channel(cm_id); + srpt_close_ch(ch); } static void srpt_cm_rep_error(struct ib_cm_id *cm_id) { PRINT_INFO("Received InfiniBand REP error for cm_id %p.", cm_id); - srpt_drain_channel(cm_id); } /** * srpt_cm_dreq_recv() - Process reception of a DREQ message. */ -static void srpt_cm_dreq_recv(struct ib_cm_id *cm_id) +static int srpt_cm_dreq_recv(struct ib_cm_id *cm_id) { struct srpt_rdma_ch *ch = cm_id->context; + int ret; - ch->dreq_received = true; - srpt_set_ch_state_to_disc(ch); + ret = ib_send_cm_drep(cm_id, NULL, 0); + if (ret < 0) + PRINT_ERROR("%s: sending DREP failed", ch->sess_name); + + srpt_close_ch(ch); + + return ret; } /** @@ -2829,8 +2790,10 @@ static void srpt_cm_dreq_recv(struct ib_cm_id *cm_id) */ static void srpt_cm_drep_recv(struct ib_cm_id *cm_id) { + struct srpt_rdma_ch *ch = cm_id->context; + PRINT_INFO("Received InfiniBand DREP message for cm_id %p.", cm_id); - srpt_drain_channel(cm_id); + srpt_close_ch(ch); } /** @@ -2863,7 +2826,7 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) srpt_cm_rtu_recv(cm_id); break; case IB_CM_DREQ_RECEIVED: - srpt_cm_dreq_recv(cm_id); + ret = srpt_cm_dreq_recv(cm_id); break; case IB_CM_DREP_RECEIVED: srpt_cm_drep_recv(cm_id); @@ -3181,13 +3144,17 @@ static int srpt_perform_rdmas(struct srpt_rdma_ch *ch, wr.num_sge = 0; wr.wr_id = encode_wr_id(SRPT_RDMA_ABORT, ioctx->ioctx.index); wr.send_flags = IB_SEND_SIGNALED; + PRINT_INFO("Trying to abort failed RDMA transfer [%d]", + ioctx->ioctx.index); while (ch->state == CH_LIVE && ib_post_send(ch->qp, &wr, &bad_wr) != 0) { PRINT_INFO("Trying to abort failed RDMA transfer [%d]", ioctx->ioctx.index); msleep(1000); } - while (ch->state != CH_DRAINING && !ioctx->rdma_aborted) { + PRINT_INFO("Waiting until RDMA abort finished [%d]", + ioctx->ioctx.index); + while (ch->state < CH_DISCONNECTED && !ioctx->rdma_aborted) { PRINT_INFO("Waiting until RDMA abort finished [%d]", ioctx->ioctx.index); msleep(1000); @@ -3327,7 +3294,6 @@ static int srpt_xmit_response(struct scst_cmd *scmnd) ch = scst_sess_get_tgt_priv(scst_cmd_get_session(scmnd)); BUG_ON(!ch); - spin_lock(&ioctx->spinlock); state = ioctx->state; switch (state) { case SRPT_STATE_NEW: @@ -3335,10 +3301,9 @@ static int srpt_xmit_response(struct scst_cmd *scmnd) ioctx->state = SRPT_STATE_CMD_RSP_SENT; break; default: - WARN(true, "Unexpected command state %d", state); + WARN(true, "Unexpected command state %d\n", state); break; } - spin_unlock(&ioctx->spinlock); if (unlikely(scst_cmd_aborted_on_xmit(scmnd))) { srpt_adjust_req_lim(ch, 0, 1); @@ -3467,7 +3432,8 @@ static int srpt_get_initiator_port_transport_id(struct scst_tgt *tgt, res = 0; tr_id->protocol_identifier = SCSI_TRANSPORTID_PROTOCOLID_SRP; - memcpy(tr_id->i_port_id, ch->i_port_id, sizeof(ch->i_port_id)); + memcpy(tr_id->i_port_id, ch->nexus->i_port_id, + sizeof(tr_id->i_port_id)); *transport_id = (uint8_t *)tr_id; @@ -3527,16 +3493,19 @@ static int srpt_close_session(struct scst_session *sess) { struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(sess); - srpt_close_ch(ch); + ib_send_cm_dreq(ch->cm_id, NULL, 0); return 0; } -static int srpt_ch_list_empty(struct srpt_tgt *srpt_tgt) +static bool srpt_ch_list_empty(struct srpt_tgt *srpt_tgt) { - int res; + struct srpt_nexus *nexus; + bool res = true; spin_lock_irq(&srpt_tgt->spinlock); - res = list_empty(&srpt_tgt->rch_list); + list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) + if (!list_empty(&nexus->ch_list)) + res = false; spin_unlock_irq(&srpt_tgt->spinlock); return res; @@ -3547,6 +3516,7 @@ static int srpt_ch_list_empty(struct srpt_tgt *srpt_tgt) */ static int srpt_release_sport(struct srpt_tgt *srpt_tgt) { + struct srpt_nexus *nexus, *next_n; struct srpt_rdma_ch *ch; TRACE_ENTRY(); @@ -3565,14 +3535,24 @@ static int srpt_release_sport(struct srpt_tgt *srpt_tgt) PRINT_INFO("%s: waiting for session unregistration ...", srpt_tgt->scst_tgt->tgt_name); spin_lock_irq(&srpt_tgt->spinlock); - list_for_each_entry(ch, &srpt_tgt->rch_list, list) { - PRINT_INFO("%s: state %s; %d commands in progress", - ch->sess_name, get_ch_state_name(ch->state), + list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { + list_for_each_entry(ch, &nexus->ch_list, list) { + PRINT_INFO("%s: state %s; %d commands in" + " progress", ch->sess_name, + get_ch_state_name(ch->state), atomic_read(&ch->scst_sess->sess_cmd_count)); + } } spin_unlock_irq(&srpt_tgt->spinlock); } + spin_lock_irq(&srpt_tgt->spinlock); + list_for_each_entry_safe(nexus, next_n, &srpt_tgt->nexus_list, entry) { + list_del(&nexus->entry); + kfree(nexus); + } + spin_unlock_irq(&srpt_tgt->spinlock); + TRACE_EXIT(); return 0; } @@ -3783,6 +3763,7 @@ static struct scst_tgt_template srpt_template = { .close_session = srpt_close_session, .xmit_response = srpt_xmit_response, .rdy_to_xfer = srpt_rdy_to_xfer, + .on_abort_cmd = srpt_on_abort_cmd, .on_hw_pending_cmd_timeout = srpt_pending_cmd_timeout, .on_free_cmd = srpt_on_free_cmd, .task_mgmt_fn_done = srpt_tsk_mgmt_done, @@ -3816,7 +3797,7 @@ static struct scst_proc_data srpt_log_proc_data = { /* Note: the caller must have zero-initialized *@srpt_tgt. */ static void srpt_init_tgt(struct srpt_tgt *srpt_tgt) { - INIT_LIST_HEAD(&srpt_tgt->rch_list); + INIT_LIST_HEAD(&srpt_tgt->nexus_list); init_waitqueue_head(&srpt_tgt->ch_releaseQ); spin_lock_init(&srpt_tgt->spinlock); } @@ -3956,8 +3937,10 @@ static void srpt_add_one(struct ib_device *device) goto err_event; } - for (i = 0; i < sdev->srq_size; ++i) + for (i = 0; i < sdev->srq_size; ++i) { + INIT_LIST_HEAD(&sdev->ioctx_ring[i]->wait_list); srpt_post_recv(sdev, sdev->ioctx_ring[i]); + } WARN_ON(sdev->device->phys_port_cnt > ARRAY_SIZE(sdev->port)); diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 5e635c2ff..621ca3926 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -56,6 +56,7 @@ */ #define SRP_SERVICE_NAME_PREFIX "SRP.T10:" +struct srpt_nexus; struct srpt_tgt; enum { @@ -132,6 +133,16 @@ enum { RDMA_COMPL_TIMEOUT_S = 80, }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) && \ + !(defined(CONFIG_SUSE_KERNEL) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76)) && \ + !(defined(RHEL_MAJOR) && \ + (RHEL_MAJOR -0 > 6 || \ + RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 5)) +/* See also patch "IB/core: Add GID change event" (commit 761d90ed4). */ +enum { IB_EVENT_GID_CHANGE = 18 }; +#endif + enum srpt_opcode { SRPT_RECV, SRPT_SEND, @@ -139,6 +150,7 @@ enum srpt_opcode { SRPT_RDMA_ABORT, SRPT_RDMA_READ_LAST, SRPT_RDMA_WRITE_LAST, + SRPT_RDMA_ZEROLENGTH_WRITE, }; static inline u64 encode_wr_id(enum srpt_opcode opcode, u32 idx) @@ -240,7 +252,7 @@ struct srpt_tsk_mgmt { * @req_lim_delta: Value of the req_lim_delta value field in the latest * SRP response sent. * @tsk_mgmt: SRPT task management function context information. - * @rdma_ius_buf: DMA mapping context information. + * @rdma_ius_buf: Inline rdma_ius buffer for small requests. */ struct srpt_send_ioctx { struct srpt_ioctx ioctx; @@ -274,19 +286,20 @@ struct srpt_send_ioctx { * @CH_DISCONNECTING: DREQ has been received and waiting for DREP or DREQ has * been sent and waiting for DREP or channel is being closed * for another reason. - * @CH_DRAINING: QP is in ERR state. + * @CH_DISCONNECTED: Last WQE has been received. */ enum rdma_ch_state { CH_CONNECTING, CH_LIVE, CH_DISCONNECTING, - CH_DRAINING, + CH_DISCONNECTED, }; /** * struct srpt_rdma_ch - RDMA channel. * @thread: Kernel thread that processes the IB queues associated with * the channel. + * @nexus: I_T nexus this channel is associated with. * @cm_id: IB CM ID associated with the channel. * @qp: IB queue pair used for communicating over this channel. * @cq: IB completion queue for this channel. @@ -298,8 +311,6 @@ enum rdma_ch_state { * @sport: pointer to the information of the HCA port used by this * channel. * @srpt_tgt: Target port used by this channel. - * @i_port_id: 128-bit initiator port identifier copied from SRP_LOGIN_REQ. - * @t_port_id: 128-bit target port identifier copied from SRP_LOGIN_REQ. * @max_ti_iu_len: maximum target-to-initiator information unit length. * @req_lim: request limit: maximum number of requests that may be sent * by the initiator without having received a response. @@ -311,9 +322,8 @@ enum rdma_ch_state { * @ioctx_ring: Send I/O context ring. * @wc: Work completion array. * @state: channel state. See also enum rdma_ch_state. - * @dreq_received: Whether an IB CM DREQ event has been received. - * @last_wqe_received: Whether the Last WQE QP event has been received. - * @list: node for insertion in the srpt_device.rch_list list. + * @processing_wait_list: Whether or not cmd_wait_list is being processed. + * @list: Entry in srpt_nexus.ch_list; * @cmd_wait_list: list of SCST commands that arrived before the RTU event. This * list contains struct srpt_ioctx elements and is protected * against concurrent modification by the cm_id spinlock. @@ -323,6 +333,7 @@ enum rdma_ch_state { */ struct srpt_rdma_ch { struct task_struct *thread; + struct srpt_nexus *nexus; struct ib_cm_id *cm_id; struct ib_qp *qp; struct ib_cq *cq; @@ -330,11 +341,9 @@ struct srpt_rdma_ch { int rq_size; int max_sge; int max_rsp_size; - int sq_wr_avail; + atomic_t sq_wr_avail; struct srpt_port *sport; struct srpt_tgt *srpt_tgt; - u8 i_port_id[16]; - u8 t_port_id[16]; int max_ti_iu_len; int req_lim; int req_lim_delta; @@ -346,25 +355,38 @@ struct srpt_rdma_ch { struct list_head list; struct list_head cmd_wait_list; uint16_t pkey_index; - bool dreq_received; - bool last_wqe_received; + bool processing_wait_list; struct scst_session *scst_sess; u8 sess_name[40]; }; +/** + * struct srpt_nexus - I_T nexus + * @entry: srpt_tgt.nexus_list list node. + * @ch_list: struct srpt_rdma_ch list. Protected by srpt_tgt.spinlock + * @i_port_id: 128-bit initiator port identifier copied from SRP_LOGIN_REQ. + * @t_port_id: 128-bit target port identifier copied from SRP_LOGIN_REQ. + */ +struct srpt_nexus { + struct list_head entry; + struct list_head ch_list; + u8 i_port_id[16]; + u8 t_port_id[16]; +}; + /** * struct srpt_tgt - * @ch_releaseQ: Enables waiting for removal from rch_list. - * @spinlock: Protects rch_list. - * @rch_list: Per-device channel list -- see also srpt_rdma_ch.list. - * @scst_tgt: SCST target information associated with this HCA. - * @enabled: Whether or not this SCST target is enabled. + * @ch_releaseQ: Enables waiting for removal from nexus_list. + * @spinlock: Protects nexus_list. + * @nexus_list: Per-device I_T nexus list. + * @scst_tgt: SCST target information associated with this HCA. + * @enabled: Whether or not this SCST target is enabled. */ struct srpt_tgt { wait_queue_head_t ch_releaseQ; spinlock_t spinlock; - struct list_head rch_list; + struct list_head nexus_list; struct scst_tgt *scst_tgt; bool enabled; }; diff --git a/www/index.html b/www/index.html index 6bd2cffee..14a457cdb 100644 --- a/www/index.html +++ b/www/index.html @@ -149,7 +149,7 @@

                            Documentation

                            HTML

                            PDF

                            -

                            Gentoo HOWTO

                            +

                            Gentoo HOWTO

                            HOWTO For iSCSI-SCST

                            Gentoo HOWTO For iSCSI-SCST

                            Alpine Linux HOWTO

                            From 67e83b5458e45b4b08f6168c240aeb1a78ef33d8 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 22 Apr 2014 07:24:47 +0000 Subject: [PATCH 042/128] isert: Add support for compiling with OFED 3.5 Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5453 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/Makefile | 10 +++++++++- iscsi-scst/README.iser_ofed | 3 ++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index aa2053136..9e3d9e2be 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -54,7 +54,7 @@ all: include/iscsi_scst_itf_ver.h progs mods ISER_SYMVERS:=$(KMOD)/Module.symvers OFED_CFLAGS:= -MLNX_OFED:=$(shell if $( ofed_info | grep MLNX_OFED $) >/dev/null 2>/dev/null; then echo true; else echo false; fi) +MLNX_OFED:=$(shell if ofed_info | grep MLNX_OFED 2>/dev/null; then echo true; else echo false; fi) ifeq ($(MLNX_OFED),true) # Whether MLNX_OFED for ubuntu has been installed @@ -82,6 +82,9 @@ else # Whether or not the OFED kernel-ib-devel RPM has been installed. OFED_KERNEL_IB_DEVEL_RPM_INSTALLED:=$(shell if rpm -q kernel-ib-devel 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi) + # Whether or not the OFED compat-rdma-devel RPM has been installed. + OFED_COMPAT_RDMA_DEVEL_RPM_INSTALLED:=$(shell if rpm -q compat-rdma-devel 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi) + ifeq ($(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED),true) # Read OFED's config.mk, which contains the definition of the variable # BACKPORT_INCLUDES. @@ -89,6 +92,11 @@ else OFED_CFLAGS:=$(shell echo $(BACKPORT_INCLUDES) -I/usr/src/ofa_kernel/include) ISER_SYMVERS:="$(ISER_SYMVERS) /usr/src/ofa_kernel/Module.symvers" endif + + ifeq ($(OFED_COMPAT_RDMA_DEVEL_RPM_INSTALLED),true) + OFED_CFLAGS:=-I/usr/src/compat-rdma/include -include /usr/src/compat-rdma/include/linux/compat-2.6.h + ISER_SYMVERS:="$(ISER_SYMVERS) /usr/src/compat-rdma/Module.symvers" + endif endif mods: Modules.symvers Module.symvers diff --git a/iscsi-scst/README.iser_ofed b/iscsi-scst/README.iser_ofed index 93310d72d..dd2b1d563 100644 --- a/iscsi-scst/README.iser_ofed +++ b/iscsi-scst/README.iser_ofed @@ -80,7 +80,8 @@ version detection by iscsi-scst makefile. For the OFED package.Make sure to enable -at least the kernel-ib and kernel-ib-devel packages. An example: +at least the kernel-ib and kernel-ib-devel packages (compat-rdma and compat-rdma-devel for OFED 3.5 and above). +An example: wget http://www.openfabrics.org/downloads/OFED/ofed-1.5.1/OFED-1.5.1.tgz tar xzf OFED-1.5.1.tgz From de6aa12e9ccc4a951c5f2f86463725506e030b2e Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 22 Apr 2014 07:50:34 +0000 Subject: [PATCH 043/128] Merged revisions 5409-5452,5454 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5409 | bvassche | 2014-04-06 23:13:53 +0300 (Sun, 06 Apr 2014) | 4 lines scstadmin: Restore LUNs in "scstadmin -list_sessions" output Signed-off-by: Dave Butler ........ r5410 | bvassche | 2014-04-06 23:26:08 +0300 (Sun, 06 Apr 2014) | 1 line scstadmin: List keys alphabetically in the -list_sessions output ........ r5411 | vlnb | 2014-04-10 02:58:20 +0300 (Thu, 10 Apr 2014) | 10 lines vdisk_blockio: Specify REQ_SYNC for synchronous I/O requests Using READ_SYNC instead of READ increases the priority of read requests. Using WRITE_SYNC instead of REQ_WRITE increases the priority of write requests and avoids that the CFQ scheduler queues such writes waiting for further write requests. Signed-off-by: Bart Van Assche ........ r5412 | vlnb | 2014-04-10 02:59:02 +0300 (Thu, 10 Apr 2014) | 18 lines vdisk_blockio: Reenable COMPARE AND WRITE The COMPARE AND WRITE implementation has been tested as follows against an SCST vdisk_blockio device: lba=7 bdev=$bdev for ((i=0;i<4;i++)); do dd if=/dev/urandom of=b$i bs=8k count=1; done for p in "0 1" "1 2" "2 3" "3 0"; do set $p; cat b$1 b$2 >b$1$2; done dd if=/dev/urandom of=$bdev dd if=b0 of=$bdev seek=$lba md5sum $bdev for ((i=0;i<10000;i++)); do for f in b01 b12 b23 b30; do sg_compare_and_write -l $lba -i $f -n 16 -x 16384 $bdev; done; done md5sum $bdev Signed-off-by: Bart Van Assche ........ r5413 | bvassche | 2014-04-15 09:03:59 +0300 (Tue, 15 Apr 2014) | 1 line ib_srpt: Fix a sparse warning ........ r5414 | vlnb | 2014-04-16 00:26:06 +0300 (Wed, 16 Apr 2014) | 5 lines Fix READ(6)/WRITE(6) LBA in those commands is 3 bytes long, not 2. ........ r5415 | vlnb | 2014-04-16 00:30:26 +0300 (Wed, 16 Apr 2014) | 3 lines Add SYNCHRONIZE_CACHE(16) ........ r5416 | vlnb | 2014-04-16 01:04:54 +0300 (Wed, 16 Apr 2014) | 6 lines Make HEAD OF QUEUE requests sync Since the block layer has no way to specify bio as HQ, there's no choice, but to use every measure to approximate it as close as possible. ........ r5417 | vlnb | 2014-04-16 01:17:34 +0300 (Wed, 16 Apr 2014) | 3 lines Add NULLIO VERIFY ........ r5418 | vlnb | 2014-04-16 04:02:50 +0300 (Wed, 16 Apr 2014) | 3 lines REPORT SUPPORTED OPERATION CODES added ........ r5419 | vlnb | 2014-04-16 04:05:49 +0300 (Wed, 16 Apr 2014) | 3 lines Cleanup ........ r5420 | vlnb | 2014-04-16 05:24:44 +0300 (Wed, 16 Apr 2014) | 3 lines Follow up for r5418: some cleanups and fixes ........ r5421 | bvassche | 2014-04-16 09:17:19 +0300 (Wed, 16 Apr 2014) | 6 lines scst_targ: Fix a checkpatch complaint Avoid that checkpatch reports the following message: ERROR: space required before the open parenthesis '(' ........ r5422 | bvassche | 2014-04-16 09:35:19 +0300 (Wed, 16 Apr 2014) | 1 line scst_vdisk: Build fix for kernels < 2.6.36 (see also r5416) ........ r5423 | bvassche | 2014-04-16 10:30:34 +0300 (Wed, 16 Apr 2014) | 6 lines ib_srpt: Disable RDMA access by the initiator With the SRP protocol all RDMA operations are initiated by the target. Since no RDMA operations are initiated by the initiator, do not grant the initiator permission to submit RDMA reads or writes to the target. ........ r5424 | bvassche | 2014-04-16 11:01:58 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Constify two arguments of srpt_get_nexus() ........ r5425 | bvassche | 2014-04-16 11:08:50 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Clean up the code that prints the dgid during login ........ r5426 | bvassche | 2014-04-16 11:23:11 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Cache P_Key lookups ........ r5427 | bvassche | 2014-04-16 12:14:31 +0300 (Wed, 16 Apr 2014) | 1 line ib_srpt: Remove a superfluous assignment ........ r5428 | bvassche | 2014-04-16 12:31:56 +0300 (Wed, 16 Apr 2014) | 1 line scst_vdisk: Avoid that checkpatch complains about unnecessary line continuations ........ r5429 | bvassche | 2014-04-16 12:35:18 +0300 (Wed, 16 Apr 2014) | 1 line scst_lib: Avoid that checkpatch complains about unnecessary line continuations ........ r5430 | bvassche | 2014-04-17 09:09:41 +0300 (Thu, 17 Apr 2014) | 1 line scst_vdisk: Kernel 2.6.27 build fix ........ r5431 | bvassche | 2014-04-17 09:12:07 +0300 (Thu, 17 Apr 2014) | 5 lines scst_vdisk: Fix a kernel 2.6.27 compiler warning Avoid that the compiler reports that variables 'start_sector' and 'nr_sects' are set but not used when building against kernel 2.6.27. ........ r5432 | vlnb | 2014-04-18 03:32:51 +0300 (Fri, 18 Apr 2014) | 3 lines Update to kernels 3.14 ........ r5433 | bvassche | 2014-04-18 08:50:04 +0300 (Fri, 18 Apr 2014) | 1 line nightly build: Add kernel 3.14 build infrastructure ........ r5434 | bvassche | 2014-04-18 08:54:29 +0300 (Fri, 18 Apr 2014) | 1 line nightly build: Add kernel version 3.14.1 and update other kernel versions ........ r5435 | bvassche | 2014-04-18 08:56:35 +0300 (Fri, 18 Apr 2014) | 6 lines iscsi-scst: Fix a checkpatch warning Fix the following checkpatch 3.14 warning: Unnecessary parentheses - maybe == should be = ? ........ r5436 | bvassche | 2014-04-18 09:03:34 +0300 (Fri, 18 Apr 2014) | 6 lines scst.h: Fix a checkpatch warning Fix the following checkpatch 3.14 warning: Unnecessary space after function pointer name ........ r5437 | bvassche | 2014-04-18 09:08:07 +0300 (Fri, 18 Apr 2014) | 6 lines scst: Fix a checkpatch warning Fix the following checkpatch 3.14 warning: Unnecessary space after function pointer name ........ r5438 | bvassche | 2014-04-18 09:13:03 +0300 (Fri, 18 Apr 2014) | 6 lines scst: Fix a checkpatch 3.14 warning about whitespace Fix the following checkpatch 3.14 warning: missing space after return type ........ r5439 | bvassche | 2014-04-18 14:09:55 +0300 (Fri, 18 Apr 2014) | 1 line ib_srpt: Update README ........ r5440 | bvassche | 2014-04-18 15:04:05 +0300 (Fri, 18 Apr 2014) | 2 lines ib_srpt: Move IB/CM knowledge out of srpt_cm_req_recv() ........ r5441 | bvassche | 2014-04-18 15:08:00 +0300 (Fri, 18 Apr 2014) | 6 lines ib_srpt: Remove a superfluous check from the REQ handler ib_send_cm_rep() checks the connection state before sending a response. Hence checking ch->state before calling ib_send_cm_rep() is superfluous, so remove that check and also the locking that is no longer needed. ........ r5442 | bvassche | 2014-04-18 15:09:33 +0300 (Fri, 18 Apr 2014) | 5 lines ib_srpt: Prepare RDMA/CM support Move IB/CM members into a new struct. Report channel pointer instead of CM ID pointer in diagnostic messages. ........ r5443 | bvassche | 2014-04-18 15:10:47 +0300 (Fri, 18 Apr 2014) | 5 lines ib_srpt: Use a mutex instead of a spinlock to protect the channel list This is allowed because all CM callback functions are invoked from thread context. ........ r5444 | bvassche | 2014-04-18 15:11:18 +0300 (Fri, 18 Apr 2014) | 2 lines ib_srpt: Move the code for checking the QP timeout ........ r5445 | bvassche | 2014-04-18 15:22:30 +0300 (Fri, 18 Apr 2014) | 4 lines ib_srpt: Add RDMA/CM support Or in other words, add RoCE and iWARP support. ........ r5446 | bvassche | 2014-04-18 15:38:39 +0300 (Fri, 18 Apr 2014) | 1 line ib_srpt: Build fix for kernel versions < 3.0 ........ r5447 | bvassche | 2014-04-18 15:44:48 +0300 (Fri, 18 Apr 2014) | 1 line ib_srpt: RHEL 6.5 build fix ........ r5448 | bvassche | 2014-04-19 14:48:33 +0300 (Sat, 19 Apr 2014) | 1 line scst_vdisk, COMPARE AND WRITE: Convert a kernel warning into a SCSI sense code ........ r5449 | bvassche | 2014-04-19 14:52:34 +0300 (Sat, 19 Apr 2014) | 33 lines vdisk_blockio: Make COMPARE AND WRITE compatible with the scsi_debug driver This patch fixes the following kernel oops: BUG: unable to handle kernel paging request at ffffeae380000690 Call Trace: [] sg_miter_next+0x9/0xd0 [] sg_copy_buffer+0xa0/0x100 [] do_device_access.isra.8+0xa6/0x150 [scsi_debug] [] resp_read+0xe4/0x240 [scsi_debug] [] scsi_debug_queuecommand_lck+0x11e5/0x2060 [scsi_debug] [] scsi_debug_queuecommand+0x30/0x48 [scsi_debug] [] scsi_dispatch_cmd+0xaf/0x260 [] scsi_request_fn+0x32d/0x540 [] __blk_run_queue+0x2a/0x40 [] blk_queue_bio+0x274/0x350 [] generic_make_request+0xa8/0xf0 [] submit_bio+0x6c/0x140 [] blockio_rw_sync.isra.29+0x106/0x170 [scst_vdisk] [] vdisk_exec_caw+0xd9/0x3c0 [scst_vdisk] [] vdev_do_job+0x9e/0x320 [scst_vdisk] [] non_fileio_exec+0x57/0xd0 [scst_vdisk] [] scst_do_real_exec+0x92/0x3b0 [scst] [] scst_exec_check_blocking+0xe2/0x300 [scst] [] scst_exec_check_sn+0x17b/0x2d0 [scst] [] scst_process_active_cmd+0x431/0x770 [scst] [] scst_do_job_active+0xea/0x180 [scst] [] scst_cmd_thread+0x126/0x290 [scst] [] kthread+0xc1/0xe0 [] ret_from_fork+0x7c/0xb0 Reported-by: Sebastian Herbszt ........ r5450 | bvassche | 2014-04-20 09:24:23 +0300 (Sun, 20 Apr 2014) | 1 line iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch: Add ........ r5451 | bvassche | 2014-04-22 09:56:37 +0300 (Tue, 22 Apr 2014) | 1 line scst: Revert r5438, a whitespace-only change ........ r5452 | bvassche | 2014-04-22 10:05:21 +0300 (Tue, 22 Apr 2014) | 1 line scripts/run-regression-tests: Suppress the checkpatch warning "missing space after return type" ........ r5454 | bvassche | 2014-04-22 10:32:44 +0300 (Tue, 22 Apr 2014) | 1 line scst/README: Update the section about Linux initiator ALUA support ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5455 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/conn.c | 2 +- .../patches/put_page_callback-3.14.patch | 364 +++++++++++ .../patches/put_page_callback-3.2.57.patch | 287 +++++++++ nightly/conf/nightly.conf | 13 +- qla2x00t/qla2x00-target/Makefile_in-tree-3.14 | 5 + scripts/generate-kernel-patch | 4 +- scripts/run-regression-tests | 1 + scst/README | 39 +- scst/include/scst.h | 174 ++++-- scst/include/scst_const.h | 9 +- .../in-tree/Kconfig.drivers.Linux-3.14.patch | 13 + .../kernel/in-tree/Makefile.dev_handlers-3.14 | 14 + .../in-tree/Makefile.drivers.Linux-3.14.patch | 12 + scst/kernel/in-tree/Makefile.scst-3.14 | 13 + scst/kernel/scst_exec_req_fifo-3.14.patch | 528 ++++++++++++++++ scst/src/dev_handlers/scst_vdisk.c | 441 ++++++++++++- scst/src/scst_lib.c | 252 +++++++- scst/src/scst_pres.c | 2 +- scst/src/scst_targ.c | 213 ++++++- scst_local/in-tree/Makefile-3.14 | 2 + .../scst-0.9.10/lib/SCST/SCST.pm | 2 +- scstadmin/scstadmin.sysfs/scstadmin | 4 +- srpt/README | 61 +- srpt/patches/kernel-3.14-pre-cflags.patch | 12 + srpt/session-management.txt | 13 +- srpt/src/ib_srpt.c | 582 +++++++++++------- srpt/src/ib_srpt.h | 40 +- usr/fileio/common.c | 1 + 28 files changed, 2730 insertions(+), 373 deletions(-) create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.14.patch create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch create mode 100644 qla2x00t/qla2x00-target/Makefile_in-tree-3.14 create mode 100644 scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch create mode 100644 scst/kernel/in-tree/Makefile.dev_handlers-3.14 create mode 100644 scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch create mode 100644 scst/kernel/in-tree/Makefile.scst-3.14 create mode 100644 scst/kernel/scst_exec_req_fifo-3.14.patch create mode 100644 scst_local/in-tree/Makefile-3.14 create mode 100644 srpt/patches/kernel-3.14-pre-cflags.patch diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index ef043cffb..cceb7b698 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -475,7 +475,7 @@ void __iscsi_write_space_ready(struct iscsi_conn *conn) spin_lock_bh(&p->wr_lock); conn->wr_space_ready = 1; - if ((conn->wr_state == ISCSI_CONN_WR_STATE_SPACE_WAIT)) { + if (conn->wr_state == ISCSI_CONN_WR_STATE_SPACE_WAIT) { TRACE_DBG("wr space ready (conn %p)", conn); list_add_tail(&conn->wr_list_entry, &p->wr_list); conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST; diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.14.patch b/iscsi-scst/kernel/patches/put_page_callback-3.14.patch new file mode 100644 index 000000000..dcf824b17 --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.14.patch @@ -0,0 +1,364 @@ +=== modified file 'drivers/block/drbd/drbd_receiver.c' +--- old/drivers/block/drbd/drbd_receiver.c 2014-04-17 22:02:06 +0000 ++++ new/drivers/block/drbd/drbd_receiver.c 2014-04-17 22:48:38 +0000 +@@ -130,7 +130,7 @@ static int page_chain_free(struct page * + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; + +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/mm_types.h 2014-04-17 22:48:38 +0000 +@@ -195,6 +195,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/net.h 2014-04-17 22:48:38 +0000 +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -292,6 +293,45 @@ int kernel_sendpage(struct socket *sock, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/skbuff.h 2014-04-17 22:48:38 +0000 +@@ -2056,7 +2056,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -2079,7 +2079,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2014-04-17 22:02:06 +0000 ++++ new/net/Kconfig 2014-04-17 22:48:38 +0000 +@@ -75,6 +75,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/ceph/pagevec.c' +--- old/net/ceph/pagevec.c 2014-04-17 22:02:06 +0000 ++++ new/net/ceph/pagevec.c 2014-04-17 22:48:38 +0000 +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page ** + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + kfree(pages); + } + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2014-04-17 22:02:06 +0000 ++++ new/net/core/skbuff.c 2014-04-17 22:48:38 +0000 +@@ -425,7 +425,7 @@ struct sk_buff *__netdev_alloc_skb(struc + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -483,7 +483,7 @@ static void skb_clone_fraglist(struct sk + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -804,7 +804,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1647,7 +1647,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1700,7 +1700,7 @@ static bool spd_fill_page(struct splice_ + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2159,7 +2159,7 @@ skb_zerocopy(struct sk_buff *to, struct + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } +@@ -2813,7 +2813,7 @@ int skb_append_datato_frags(struct sock + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); + +=== modified file 'net/core/sock.c' +--- old/net/core/sock.c 2014-04-17 22:02:06 +0000 ++++ new/net/core/sock.c 2014-04-17 22:48:38 +0000 +@@ -1839,7 +1839,7 @@ bool skb_page_frag_refill(unsigned int s + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + order = SKB_FRAG_PAGE_ORDER; +@@ -2602,7 +2602,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2014-04-17 22:02:06 +0000 ++++ new/net/ipv4/Makefile 2014-04-17 22:48:38 +0000 +@@ -53,6 +53,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah. + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2014-04-17 22:02:06 +0000 ++++ new/net/ipv4/ip_output.c 2014-04-17 22:48:38 +0000 +@@ -1004,7 +1004,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1230,7 +1230,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2014-04-17 22:02:06 +0000 ++++ new/net/ipv4/tcp.c 2014-04-17 22:48:38 +0000 +@@ -939,7 +939,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1238,7 +1238,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2014-04-17 22:48:38 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + +=== modified file 'net/ipv6/ip6_output.c' +--- old/net/ipv6/ip6_output.c 2014-04-17 22:02:06 +0000 ++++ new/net/ipv6/ip6_output.c 2014-04-17 22:48:38 +0000 +@@ -1455,7 +1455,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, + diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch b/iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch new file mode 100644 index 000000000..6394566da --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.2.57.patch @@ -0,0 +1,287 @@ +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2012-01-10 22:58:17 +0000 ++++ new/include/linux/mm_types.h 2012-01-10 23:02:48 +0000 +@@ -149,6 +149,17 @@ struct page { + */ + void *shadow; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * If another subsystem starts using the double word pairing for atomic + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2012-01-10 22:58:17 +0000 ++++ new/include/linux/net.h 2012-01-10 23:02:48 +0000 +@@ -61,6 +61,7 @@ typedef enum { + #include /* For O_CLOEXEC and O_NONBLOCK */ + #include + #include ++#include + + struct poll_table_struct; + struct pipe_inode_info; +@@ -289,5 +290,44 @@ extern int kernel_sock_shutdown(struct s + MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \ + "-type-" __stringify(type)) + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #endif /* __KERNEL__ */ + #endif /* _LINUX_NET_H */ + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2012-01-10 22:58:17 +0000 ++++ new/include/linux/skbuff.h 2012-01-10 23:15:31 +0000 +@@ -1712,7 +1712,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -1735,7 +1735,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2012-01-10 22:58:17 +0000 ++++ new/net/Kconfig 2012-01-10 23:02:48 +0000 +@@ -72,6 +72,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2012-01-10 22:58:17 +0000 ++++ new/net/core/skbuff.c 2012-01-10 23:02:48 +0000 +@@ -654,7 +654,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)head->private; +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1493,7 +1493,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static inline struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1517,7 +1517,7 @@ new_page: + off = sk->sk_sndmsg_off; + mlen = PAGE_SIZE - off; + if (mlen < 64 && mlen < *len) { +- put_page(p); ++ net_put_page(p); + goto new_page; + } + +@@ -1527,7 +1527,7 @@ new_page: + memcpy(page_address(p) + off, page_address(page) + *offset, *len); + sk->sk_sndmsg_off += *len; + *offset = off; +- get_page(p); ++ net_get_page(p); + + return p; + } +@@ -1549,7 +1549,7 @@ static inline int spd_fill_page(struct s + if (!page) + return 1; + } else +- get_page(page); ++ net_get_page(page); + + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2012-01-10 22:58:17 +0000 ++++ new/net/ipv4/Makefile 2012-01-10 23:02:48 +0000 +@@ -48,6 +48,7 @@ obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o + obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2012-01-10 22:58:17 +0000 ++++ new/net/ipv4/ip_output.c 2012-01-10 23:02:48 +0000 +@@ -1232,7 +1232,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2012-01-10 22:58:17 +0000 ++++ new/net/ipv4/tcp.c 2012-01-10 23:02:48 +0000 +@@ -815,7 +815,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + +@@ -1022,7 +1022,7 @@ new_segment: + goto new_segment; + } else if (page) { + if (off == PAGE_SIZE) { +- put_page(page); ++ net_put_page(page); + TCP_PAGE(sk) = page = NULL; + off = 0; + } +@@ -1062,9 +1062,9 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, page, off, copy); + if (TCP_PAGE(sk)) { +- get_page(page); ++ net_get_page(page); + } else if (off + copy < PAGE_SIZE) { +- get_page(page); ++ net_get_page(page); + TCP_PAGE(sk) = page; + } + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2012-01-10 23:43:22 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index 15253e973..f01180f32 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,18 +3,19 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.13.9 \ -3.12.13-nc \ +3.14.1 \ +3.13.10 \ +3.12.17-nc \ 3.11.10-nc \ -3.10.36-nc \ +3.10.37-nc \ 3.9.11-nc \ -3.8.13-nc \ +3.8.14-nc \ 3.7.10-nc \ 3.6.11-nc \ 3.5.7-nc \ -3.4.86-nc \ +3.4.87-nc \ 3.3.8-nc \ -3.2.53-nc \ +3.2.57-nc \ 3.1.10-nc \ 3.0.101-nc \ 2.6.39.4-nc \ diff --git a/qla2x00t/qla2x00-target/Makefile_in-tree-3.14 b/qla2x00t/qla2x00-target/Makefile_in-tree-3.14 new file mode 100644 index 000000000..9657aee84 --- /dev/null +++ b/qla2x00t/qla2x00-target/Makefile_in-tree-3.14 @@ -0,0 +1,5 @@ +ccflags-y += -Idrivers/scsi/qla2xxx + +qla2x00tgt-y := qla2x00t.o + +obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o diff --git a/scripts/generate-kernel-patch b/scripts/generate-kernel-patch index 532572fb4..cc95d7a67 100755 --- a/scripts/generate-kernel-patch +++ b/scripts/generate-kernel-patch @@ -268,7 +268,9 @@ done scsi_exec_req_fifo_defined=0 scst_io_context=0 for p in scst/kernel/*-${kver}.patch \ - $(if [ "${1#3.7.}" != "$1" ] && [ "${1#3.7.}" -ge 10 ]; then + $(if [ "${1#3.2.}" != "$1" ] && [ "${1#3.2.}" -ge 57 ]; then + echo iscsi-scst/kernel/patches/*-3.2.57.patch; + elif [ "${1#3.7.}" != "$1" ] && [ "${1#3.7.}" -ge 10 ]; then echo iscsi-scst/kernel/patches/*-3.7.10.patch; elif [ "${1#3.10.}" != "$1" ] && [ "${1#3.10.}" -ge 30 ]; then echo iscsi-scst/kernel/patches/*-3.10.30.patch; diff --git a/scripts/run-regression-tests b/scripts/run-regression-tests index c859a4c00..bae2ad0a5 100755 --- a/scripts/run-regression-tests +++ b/scripts/run-regression-tests @@ -213,6 +213,7 @@ function run_checkpatch { echo "${errors} errors / ${warnings} warnings." grep -E '^WARNING|^ERROR' "${outputfile}" | sort | + grep -v 'WARNING: missing space after return type' | sed 's/^WARNING: Avoid CamelCase:.*/WARNING: Avoid CamelCase/' | uniq -c else diff --git a/scst/README b/scst/README index 6234643f1..2306f1371 100644 --- a/scst/README +++ b/scst/README @@ -1466,34 +1466,29 @@ Report target port groups: Initiator Support ................. -On Linux systems implicit ALUA support is provided by the scsi_dh_alua driver -of the device mapper. You will have to modify at least the following in -/etc/multipath.conf: -* path_checker scsi_dh_alua -* prio_callout "/sbin/mpath_prio_alua /dev/%n" +On Linux systems implicit ALUA support is provided by the scsi_dh_alua kernel +driver in combination with the user space multipathd daemon. You will have to +modify at least the following in /etc/multipath.conf to enable implicit ALUA: +* hardware_handler "1 alua" +* prio alua +* path_grouping_policy group_by_prio -If your distribution does not provide a /sbin/mpath_prio_alua script, you can -use the following implementation: -$ cat /sbin/mpath_prio_alua -#!/bin/bash -# Given a SCSI device node, query the target port group asymmetric access -# state and report it in numeric form. -tpg_id="$(sg_vpd --page=di "$1" | sed -n 's/.*Target port group: //p')" -aas="$(sg_rtpg "$1" \ -| grep -A1 "target port group id : $tpg_id" \ -| tail -n 1 \ -| sed 's/.*target port group asymmetric access state : //')" -echo $((aas)) +Note: newer versions of multipathd support a parameter called +"detect_prio". It can be more convenient to enable this parameter instead of +setting the parameter "prio" to "alua" for only those LUNs that support ALUA. More information about how to configure the device mapper and the scsi_dh_alua -driver can be found in the manual of your Linux distribution. +driver can be found in the manual of your Linux distribution ("man +multipath.conf"). Windows initiator systems support ALUA from Windows Server 2008 on. For more -information, see also: -* Microsoft, Multipathing Support in Windows Server 2008, MSDN -(http://blogs.msdn.com/b/san/archive/2008/07/27/multipathing-support-in-windows-server-2008.aspx). +information about ALUA support in Windows Server, see also: +* Microsoft, Windows Server 2008 R2 Multipath I/O Overview, MSDN + (http://technet.microsoft.com/en-us/library/cc725907.aspx). +* Microsoft, Multipathing Support in Windows Server 2008, July 2008, MSDN + (http://blogs.msdn.com/b/san/archive/2008/07/27/multipathing-support-in-windows-server-2008.aspx). * Microsoft, ALUA MPIO Logo Test, MSDN -(http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx). + (http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx). Caching diff --git a/scst/include/scst.h b/scst/include/scst.h index 378dc589c..518e434a5 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -661,6 +661,7 @@ struct scst_acg; struct scst_acg_dev; struct scst_acn; struct scst_aen; +struct scst_opcode_descriptor; /* * SCST uses 64-bit numbers to represent LUN's internally. The value @@ -763,7 +764,7 @@ struct scst_tgt_template { * * MUST HAVE */ - int (*xmit_response) (struct scst_cmd *cmd); + int (*xmit_response)(struct scst_cmd *cmd); /* * This function informs the driver that data @@ -787,7 +788,7 @@ struct scst_tgt_template { * * OPTIONAL */ - int (*rdy_to_xfer) (struct scst_cmd *cmd); + int (*rdy_to_xfer)(struct scst_cmd *cmd); /* * Called if cmd stays inside the target hardware, i.e. after @@ -797,7 +798,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*on_hw_pending_cmd_timeout) (struct scst_cmd *cmd); + void (*on_hw_pending_cmd_timeout)(struct scst_cmd *cmd); /* * Called to notify the driver that the command is about to be freed. @@ -813,7 +814,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*on_free_cmd) (struct scst_cmd *cmd); + void (*on_free_cmd)(struct scst_cmd *cmd); /* * This function allows target driver to handle data buffer @@ -844,7 +845,7 @@ struct scst_tgt_template { * * OPTIONAL. */ - int (*tgt_alloc_data_buf) (struct scst_cmd *cmd); + int (*tgt_alloc_data_buf)(struct scst_cmd *cmd); /* * This function informs the driver that data @@ -871,7 +872,7 @@ struct scst_tgt_template { * * OPTIONAL. */ - void (*preprocessing_done) (struct scst_cmd *cmd); + void (*preprocessing_done)(struct scst_cmd *cmd); /* * This function informs the driver that the said command is about @@ -884,7 +885,7 @@ struct scst_tgt_template { * * OPTIONAL */ - int (*pre_exec) (struct scst_cmd *cmd); + int (*pre_exec)(struct scst_cmd *cmd); /* * This function informs the driver that all affected by the @@ -897,7 +898,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*task_mgmt_affected_cmds_done) (struct scst_mgmt_cmd *mgmt_cmd); + void (*task_mgmt_affected_cmds_done)(struct scst_mgmt_cmd *mgmt_cmd); /* * This function informs the driver that the corresponding task @@ -910,7 +911,7 @@ struct scst_tgt_template { * * MUST HAVE if the target supports task management. */ - void (*task_mgmt_fn_done) (struct scst_mgmt_cmd *mgmt_cmd); + void (*task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd); /* * Called to notify target driver that the command is being aborted. @@ -919,7 +920,7 @@ struct scst_tgt_template { * * OPTIONAL */ - void (*on_abort_cmd) (struct scst_cmd *cmd); + void (*on_abort_cmd)(struct scst_cmd *cmd); /* * This function should detect the target adapters that @@ -930,7 +931,7 @@ struct scst_tgt_template { * * MUST HAVE */ - int (*detect) (struct scst_tgt_template *tgt_template); + int (*detect)(struct scst_tgt_template *tgt_template); /* * This function should free up the resources allocated to the device. @@ -940,7 +941,7 @@ struct scst_tgt_template { * * MUST HAVE */ - int (*release) (struct scst_tgt *tgt); + int (*release)(struct scst_tgt *tgt); /* * This function is used for Asynchronous Event Notifications. @@ -959,7 +960,7 @@ struct scst_tgt_template { * * MUST HAVE, if low-level protocol supports AENs. */ - int (*report_aen) (struct scst_aen *aen); + int (*report_aen)(struct scst_aen *aen); #ifdef CONFIG_SCST_PROC /* @@ -969,8 +970,8 @@ struct scst_tgt_template { * * OPTIONAL */ - int (*read_proc) (struct seq_file *seq, struct scst_tgt *tgt); - int (*write_proc) (char *buffer, char **start, off_t offset, + int (*read_proc)(struct seq_file *seq, struct scst_tgt *tgt); + int (*write_proc)(char *buffer, char **start, off_t offset, int length, int *eof, struct scst_tgt *tgt); #endif @@ -988,7 +989,7 @@ struct scst_tgt_template { * * SHOULD HAVE, because it's required for Persistent Reservations. */ - int (*get_initiator_port_transport_id) (struct scst_tgt *tgt, + int (*get_initiator_port_transport_id)(struct scst_tgt *tgt, struct scst_session *sess, uint8_t **transport_id); /* @@ -1003,14 +1004,14 @@ struct scst_tgt_template { * If you are sure your target driver doesn't need enabling target, * you should set enabled_attr_not_needed in 1. */ - int (*enable_target) (struct scst_tgt *tgt, bool enable); + int (*enable_target)(struct scst_tgt *tgt, bool enable); /* * This function shows if particular target is enabled or not. * * SHOULD HAVE, see above why. */ - bool (*is_target_enabled) (struct scst_tgt *tgt); + bool (*is_target_enabled)(struct scst_tgt *tgt); /* * This function adds a virtual target. @@ -1030,7 +1031,7 @@ struct scst_tgt_template { * * MUST HAVE if virtual targets are supported. */ - ssize_t (*add_target) (const char *target_name, char *params); + ssize_t (*add_target)(const char *target_name, char *params); /* * This function deletes a virtual target. See comment for add_target @@ -1038,7 +1039,7 @@ struct scst_tgt_template { * * MUST HAVE if virtual targets are supported. */ - ssize_t (*del_target) (const char *target_name); + ssize_t (*del_target)(const char *target_name); /* * This function called if not "add_target" or "del_target" command is @@ -1047,7 +1048,7 @@ struct scst_tgt_template { * * OPTIONAL. */ - ssize_t (*mgmt_cmd) (char *cmd); + ssize_t (*mgmt_cmd)(char *cmd); /* * Forcibly close a session. Note: this function may operate @@ -1068,7 +1069,7 @@ struct scst_tgt_template { * * OPTIONAL */ - uint16_t (*get_phys_transport_version) (struct scst_tgt *tgt); + uint16_t (*get_phys_transport_version)(struct scst_tgt *tgt); /* * Should return SCSI transport version. Used in the corresponding @@ -1076,7 +1077,7 @@ struct scst_tgt_template { * * OPTIONAL */ - uint16_t (*get_scsi_transport_version) (struct scst_tgt *tgt); + uint16_t (*get_scsi_transport_version)(struct scst_tgt *tgt); /* * Name of the template. Must be unique to identify @@ -1244,7 +1245,7 @@ struct scst_dev_type { * * MUST HAVE */ - int (*parse) (struct scst_cmd *cmd); + int (*parse)(struct scst_cmd *cmd); /* * This function allows dev handler to handle data buffer @@ -1264,7 +1265,7 @@ struct scst_dev_type { * * OPTIONAL */ - int (*dev_alloc_data_buf) (struct scst_cmd *cmd); + int (*dev_alloc_data_buf)(struct scst_cmd *cmd); /* * Called to execute CDB. Useful, for instance, to implement @@ -1286,7 +1287,7 @@ struct scst_dev_type { * OPTIONAL, if not set, the commands will be sent directly to SCSI * device. */ - int (*exec) (struct scst_cmd *cmd); + int (*exec)(struct scst_cmd *cmd); /* * Called to notify dev handler about the result of cmd execution @@ -1305,7 +1306,7 @@ struct scst_dev_type { * * OPTIONAL */ - int (*dev_done) (struct scst_cmd *cmd); + int (*dev_done)(struct scst_cmd *cmd); /* * Called to notify dev hander that the command is about to be freed. @@ -1314,7 +1315,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*on_free_cmd) (struct scst_cmd *cmd); + void (*on_free_cmd)(struct scst_cmd *cmd); /* * Called to notify dev handler that a task management command received @@ -1329,7 +1330,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*task_mgmt_fn_received) (struct scst_mgmt_cmd *mgmt_cmd, + void (*task_mgmt_fn_received)(struct scst_mgmt_cmd *mgmt_cmd, struct scst_tgt_dev *tgt_dev); /* @@ -1347,7 +1348,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*task_mgmt_fn_done) (struct scst_mgmt_cmd *mgmt_cmd, + void (*task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd, struct scst_tgt_dev *tgt_dev); /* @@ -1359,7 +1360,7 @@ struct scst_dev_type { * * OPTIONAL */ - void (*reassign_retained_states) (struct scst_tgt_dev *new_tgt_dev, + void (*reassign_retained_states)(struct scst_tgt_dev *new_tgt_dev, struct scst_tgt_dev *old_tgt_dev); /* @@ -1372,7 +1373,30 @@ struct scst_dev_type { * * MUST HAVE, if dev handler supports CDB splitting. */ - bool (*on_sg_tablesize_low) (struct scst_cmd *cmd); + bool (*on_sg_tablesize_low)(struct scst_cmd *cmd); + + /* + * Called to return array of supported opcodes in out_supp_opcodes + * argument with out_supp_opcodes_cnt elements count or execute + * REPORT SUPPORTED OPERATION CODES command in place. Must return + * 0 on success or any other code otherwise. In the latter case, + * cmd supposed to have correct sense set. + * + * OPTIONAL + */ + int (*get_supported_opcodes)(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); + + /* + * Called to put (release) array of supported opcodes returned + * by get_supported_opcodes() callback. + * + * OPTIONAL + */ + void (*put_supported_opcodes)(struct scst_cmd *cmd, + const struct scst_opcode_descriptor **supp_opcodes, + int supp_opcodes_cnt); /* * Called when new device is attaching to the dev handler @@ -1380,14 +1404,14 @@ struct scst_dev_type { * * OPTIONAL */ - int (*attach) (struct scst_device *dev); + int (*attach)(struct scst_device *dev); /* * Called when a device is detaching from the dev handler. * * OPTIONAL */ - void (*detach) (struct scst_device *dev); + void (*detach)(struct scst_device *dev); /* * Called when new tgt_dev (session) is attaching to the dev handler. @@ -1395,14 +1419,14 @@ struct scst_dev_type { * * OPTIONAL */ - int (*attach_tgt) (struct scst_tgt_dev *tgt_dev); + int (*attach_tgt)(struct scst_tgt_dev *tgt_dev); /* * Called when tgt_dev (session) is detaching from the dev handler. * * OPTIONAL */ - void (*detach_tgt) (struct scst_tgt_dev *tgt_dev); + void (*detach_tgt)(struct scst_tgt_dev *tgt_dev); #ifdef CONFIG_SCST_PROC /* @@ -1412,8 +1436,8 @@ struct scst_dev_type { * * OPTIONAL */ - int (*read_proc) (struct seq_file *seq, struct scst_dev_type *dev_type); - int (*write_proc) (char *buffer, char **start, off_t offset, + int (*read_proc)(struct seq_file *seq, struct scst_dev_type *dev_type); + int (*write_proc)(char *buffer, char **start, off_t offset, int length, int *eof, struct scst_dev_type *dev_type); #else /* @@ -1434,7 +1458,7 @@ struct scst_dev_type { * * MUST HAVE if virtual devices are supported. */ - ssize_t (*add_device) (const char *device_name, char *params); + ssize_t (*add_device)(const char *device_name, char *params); /* * This function deletes a virtual device. See comment for add_device @@ -1442,7 +1466,7 @@ struct scst_dev_type { * * MUST HAVE if virtual devices are supported. */ - ssize_t (*del_device) (const char *device_name); + ssize_t (*del_device)(const char *device_name); /* * This function called if not "add_device" or "del_device" command is @@ -1451,7 +1475,7 @@ struct scst_dev_type { * * OPTIONAL. */ - ssize_t (*mgmt_cmd) (char *cmd); + ssize_t (*mgmt_cmd)(char *cmd); #endif /* @@ -1778,9 +1802,9 @@ struct scst_session { * and scst_unregister_session() */ void *reg_sess_data; - void (*init_result_fn) (struct scst_session *sess, void *data, + void (*init_result_fn)(struct scst_session *sess, void *data, int result); - void (*unreg_done_fn) (struct scst_session *sess); + void (*unreg_done_fn)(struct scst_session *sess); #ifdef CONFIG_SCST_MEASURE_LATENCY spinlock_t lat_lock; @@ -1803,7 +1827,7 @@ struct scst_pr_abort_all_pending_mgmt_cmds_counter { atomic_t pr_abort_pending_cnt; /* Saved completion routine */ - void (*saved_cmd_done) (struct scst_cmd *cmd, int next_state, + void (*saved_cmd_done)(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context); /* @@ -2111,7 +2135,7 @@ struct scst_cmd { int64_t data_len; /* Completion routine */ - void (*scst_cmd_done) (struct scst_cmd *cmd, int next_state, + void (*scst_cmd_done)(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context); struct sgv_pool_obj *sgv; /* sgv object */ @@ -2896,6 +2920,58 @@ struct scst_aen { int delivery_status; }; +#define SCST_OD_DEFAULT_CONTROL_BYTE 0 + +struct scst_opcode_descriptor { + uint16_t od_serv_action; + uint8_t od_opcode; + uint8_t od_serv_action_valid:1; + uint8_t od_support:3; /* SUPPORT bits */ + uint16_t od_cdb_size; + uint8_t od_comm_specific_timeout; + uint32_t od_nominal_timeout; + uint32_t od_recommended_timeout; + uint8_t od_cdb_usage_bits[]; +} __packed; + +extern const struct scst_opcode_descriptor scst_op_descr_log_select; +extern const struct scst_opcode_descriptor scst_op_descr_log_sense; +extern const struct scst_opcode_descriptor scst_op_descr_mode_select6; +extern const struct scst_opcode_descriptor scst_op_descr_mode_sense6; +extern const struct scst_opcode_descriptor scst_op_descr_mode_select10; +extern const struct scst_opcode_descriptor scst_op_descr_mode_sense10; +extern const struct scst_opcode_descriptor scst_op_descr_rtpg; +extern const struct scst_opcode_descriptor scst_op_descr_stpg; +extern const struct scst_opcode_descriptor scst_op_descr_send_diagnostic; + +extern const struct scst_opcode_descriptor scst_op_descr_inquiry; +extern const struct scst_opcode_descriptor scst_op_descr_tur; +extern const struct scst_opcode_descriptor scst_op_descr_reserve6; +extern const struct scst_opcode_descriptor scst_op_descr_release6; +extern const struct scst_opcode_descriptor scst_op_descr_reserve10; +extern const struct scst_opcode_descriptor scst_op_descr_release10; +extern const struct scst_opcode_descriptor scst_op_descr_pr_in; +extern const struct scst_opcode_descriptor scst_op_descr_pr_out; +extern const struct scst_opcode_descriptor scst_op_descr_report_luns; +extern const struct scst_opcode_descriptor scst_op_descr_request_sense; +extern const struct scst_opcode_descriptor scst_op_descr_report_supp_tm_fns; +extern const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes; + +#define SCST_OPCODE_DESCRIPTORS \ + &scst_op_descr_inquiry, \ + &scst_op_descr_tur, \ + &scst_op_descr_reserve6, \ + &scst_op_descr_release6, \ + &scst_op_descr_reserve10, \ + &scst_op_descr_release10, \ + &scst_op_descr_pr_in, \ + &scst_op_descr_pr_out, \ + &scst_op_descr_report_luns, \ + &scst_op_descr_request_sense, \ + &scst_op_descr_report_supp_opcodes, \ + &scst_op_descr_report_supp_tm_fns, + + #ifndef smp_mb__after_set_bit /* There is no smp_mb__after_set_bit() in the kernel */ #define smp_mb__after_set_bit() smp_mb() @@ -2935,11 +3011,11 @@ void scst_unregister_target(struct scst_tgt *tgt); struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic, const char *initiator_name, void *tgt_priv, void *result_fn_data, - void (*result_fn) (struct scst_session *sess, void *data, int result)); + void (*result_fn)(struct scst_session *sess, void *data, int result)); struct scst_session *scst_register_session_non_gpl(struct scst_tgt *tgt, const char *initiator_name, void *tgt_priv); void scst_unregister_session(struct scst_session *sess, int wait, - void (*unreg_done_fn) (struct scst_session *sess)); + void (*unreg_done_fn)(struct scst_session *sess)); void scst_unregister_session_non_gpl(struct scst_session *sess); int __scst_register_dev_driver(struct scst_dev_type *dev_type, @@ -3078,8 +3154,8 @@ void scst_capacity_data_changed(struct scst_device *dev); struct scst_cmd *scst_find_cmd_by_tag(struct scst_session *sess, uint64_t tag); struct scst_cmd *scst_find_cmd(struct scst_session *sess, void *data, - int (*cmp_fn) (struct scst_cmd *cmd, - void *data)); + int (*cmp_fn)(struct scst_cmd *cmd, + void *data)); enum dma_data_direction scst_to_dma_dir(int scst_dir); enum dma_data_direction scst_to_tgt_dma_dir(int scst_dir); diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index 06bf7e923..e4ebb6944 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -413,6 +413,10 @@ static inline int scst_sense_response_code(const uint8_t *sense) #define WRITE_SAME_16 0x93 #endif +#ifndef SYNCHRONIZE_CACHE_16 +#define SYNCHRONIZE_CACHE_16 0x91 +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) /* * From . See also commit @@ -567,6 +571,7 @@ enum scst_tg_sup { ** Various timeouts *************************************************************/ #define SCST_DEFAULT_TIMEOUT (30 * HZ) +#define SCST_DEFAULT_NOMINAL_TIMEOUT_SEC 1 #define SCST_GENERIC_CHANGER_TIMEOUT (3 * HZ) #define SCST_GENERIC_CHANGER_LONG_TIMEOUT (14000 * HZ) @@ -582,8 +587,8 @@ enum scst_tg_sup { #define SCST_GENERIC_MODISK_REG_TIMEOUT (900 * HZ) #define SCST_GENERIC_MODISK_LONG_TIMEOUT (14000 * HZ) -#define SCST_GENERIC_DISK_SMALL_TIMEOUT (3 * HZ) -#define SCST_GENERIC_DISK_REG_TIMEOUT (30 * HZ) +#define SCST_GENERIC_DISK_SMALL_TIMEOUT (10 * HZ) +#define SCST_GENERIC_DISK_REG_TIMEOUT (60 * HZ) #define SCST_GENERIC_DISK_LONG_TIMEOUT (3600 * HZ) #define SCST_GENERIC_RAID_TIMEOUT (3 * HZ) diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch new file mode 100644 index 000000000..0d5a19f0f --- /dev/null +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.14.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/Kconfig b/drivers/Kconfig +index aa43b91..c96860e 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig" + + source "drivers/scsi/Kconfig" + ++source "drivers/scst/Kconfig" ++ + source "drivers/ata/Kconfig" + + source "drivers/md/Kconfig" diff --git a/scst/kernel/in-tree/Makefile.dev_handlers-3.14 b/scst/kernel/in-tree/Makefile.dev_handlers-3.14 new file mode 100644 index 000000000..f933b36f7 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.dev_handlers-3.14 @@ -0,0 +1,14 @@ +ccflags-y += -Wno-unused-parameter + +obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \ + scst_vdisk.o scst_raid.o scst_processor.o scst_user.o + +obj-$(CONFIG_SCST_DISK) += scst_disk.o +obj-$(CONFIG_SCST_TAPE) += scst_tape.o +obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o +obj-$(CONFIG_SCST_MODISK) += scst_modisk.o +obj-$(CONFIG_SCST_CHANGER) += scst_changer.o +obj-$(CONFIG_SCST_RAID) += scst_raid.o +obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o +obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o +obj-$(CONFIG_SCST_USER) += scst_user.o diff --git a/scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch new file mode 100644 index 000000000..f7213ed4c --- /dev/null +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-3.14.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/Makefile b/drivers/Makefile +index ab93de8..45077ec 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -128,6 +128,7 @@ obj-$(CONFIG_SSB) += ssb/ + obj-$(CONFIG_BCMA) += bcma/ + obj-$(CONFIG_VHOST_RING) += vhost/ + obj-$(CONFIG_VLYNQ) += vlynq/ ++obj-$(CONFIG_SCST) += scst/ + obj-$(CONFIG_STAGING) += staging/ + obj-y += platform/ + #common clk code diff --git a/scst/kernel/in-tree/Makefile.scst-3.14 b/scst/kernel/in-tree/Makefile.scst-3.14 new file mode 100644 index 000000000..53af5f388 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.scst-3.14 @@ -0,0 +1,13 @@ +ccflags-y += -Wno-unused-parameter + +scst-y += scst_main.o +scst-y += scst_pres.o +scst-y += scst_targ.o +scst-y += scst_lib.o +scst-y += scst_sysfs.o +scst-y += scst_mem.o +scst-y += scst_tg.o +scst-y += scst_debug.o + +obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \ + srpt/ scst_local/ diff --git a/scst/kernel/scst_exec_req_fifo-3.14.patch b/scst/kernel/scst_exec_req_fifo-3.14.patch new file mode 100644 index 000000000..70c47797d --- /dev/null +++ b/scst/kernel/scst_exec_req_fifo-3.14.patch @@ -0,0 +1,528 @@ +=== modified file 'block/blk-map.c' +--- old/block/blk-map.c 2014-04-17 22:02:06 +0000 ++++ new/block/blk-map.c 2014-04-17 22:08:48 +0000 +@@ -5,6 +5,8 @@ + #include + #include + #include ++#include ++#include + #include /* for struct sg_iovec */ + + #include "blk.h" +@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio) + } + EXPORT_SYMBOL(blk_rq_unmap_user); + ++struct blk_kern_sg_work { ++ atomic_t bios_inflight; ++ struct sg_table sg_table; ++ struct scatterlist *src_sgl; ++}; ++ ++static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw) ++{ ++ struct sg_table *sgt = &bw->sg_table; ++ struct scatterlist *sg; ++ int i; ++ ++ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { ++ struct page *pg = sg_page(sg); ++ if (pg == NULL) ++ break; ++ __free_page(pg); ++ } ++ ++ sg_free_table(sgt); ++ kfree(bw); ++ return; ++} ++ ++static void blk_bio_map_kern_endio(struct bio *bio, int err) ++{ ++ struct blk_kern_sg_work *bw = bio->bi_private; ++ ++ if (bw != NULL) { ++ /* Decrement the bios in processing and, if zero, free */ ++ BUG_ON(atomic_read(&bw->bios_inflight) <= 0); ++ if (atomic_dec_and_test(&bw->bios_inflight)) { ++ if ((bio_data_dir(bio) == READ) && (err == 0)) { ++ unsigned long flags; ++ ++ local_irq_save(flags); /* to protect KMs */ ++ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0); ++ local_irq_restore(flags); ++ } ++ blk_free_kern_sg_work(bw); ++ } ++ } ++ ++ bio_put(bio); ++ return; ++} ++ ++static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work **pbw, ++ gfp_t gfp, gfp_t page_gfp) ++{ ++ int res = 0, i; ++ struct scatterlist *sg; ++ struct scatterlist *new_sgl; ++ int new_sgl_nents; ++ size_t len = 0, to_copy; ++ struct blk_kern_sg_work *bw; ++ ++ bw = kzalloc(sizeof(*bw), gfp); ++ if (bw == NULL) ++ goto out; ++ ++ bw->src_sgl = sgl; ++ ++ for_each_sg(sgl, sg, nents, i) ++ len += sg->length; ++ to_copy = len; ++ ++ new_sgl_nents = PFN_UP(len); ++ ++ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp); ++ if (res != 0) ++ goto err_free; ++ ++ new_sgl = bw->sg_table.sgl; ++ ++ for_each_sg(new_sgl, sg, new_sgl_nents, i) { ++ struct page *pg; ++ ++ pg = alloc_page(page_gfp); ++ if (pg == NULL) ++ goto err_free; ++ ++ sg_assign_page(sg, pg); ++ sg->length = min_t(size_t, PAGE_SIZE, len); ++ ++ len -= PAGE_SIZE; ++ } ++ ++ if (rq_data_dir(rq) == WRITE) { ++ /* ++ * We need to limit amount of copied data to to_copy, because ++ * sgl might have the last element in sgl not marked as last in ++ * SG chaining. ++ */ ++ sg_copy(new_sgl, sgl, 0, to_copy); ++ } ++ ++ *pbw = bw; ++ /* ++ * REQ_COPY_USER name is misleading. It should be something like ++ * REQ_HAS_TAIL_SPACE_FOR_PADDING. ++ */ ++ rq->cmd_flags |= REQ_COPY_USER; ++ ++out: ++ return res; ++ ++err_free: ++ blk_free_kern_sg_work(bw); ++ res = -ENOMEM; ++ goto out; ++} ++ ++static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work *bw, gfp_t gfp) ++{ ++ int res; ++ struct request_queue *q = rq->q; ++ int rw = rq_data_dir(rq); ++ int max_nr_vecs, i; ++ size_t tot_len; ++ bool need_new_bio; ++ struct scatterlist *sg, *prev_sg = NULL; ++ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL; ++ int bios; ++ ++ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) { ++ WARN_ON(1); ++ res = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Let's keep each bio allocation inside a single page to decrease ++ * probability of failure. ++ */ ++ max_nr_vecs = min_t(size_t, ++ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)), ++ BIO_MAX_PAGES); ++ ++ need_new_bio = true; ++ tot_len = 0; ++ bios = 0; ++ for_each_sg(sgl, sg, nents, i) { ++ struct page *page = sg_page(sg); ++ void *page_addr = page_address(page); ++ size_t len = sg->length, l; ++ size_t offset = sg->offset; ++ ++ tot_len += len; ++ prev_sg = sg; ++ ++ /* ++ * Each segment must be aligned on DMA boundary and ++ * not on stack. The last one may have unaligned ++ * length as long as the total length is aligned to ++ * DMA padding alignment. ++ */ ++ if (i == nents - 1) ++ l = 0; ++ else ++ l = len; ++ if (((sg->offset | l) & queue_dma_alignment(q)) || ++ (page_addr && object_is_on_stack(page_addr + sg->offset))) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ while (len > 0) { ++ size_t bytes; ++ int rc; ++ ++ if (need_new_bio) { ++ bio = bio_kmalloc(gfp, max_nr_vecs); ++ if (bio == NULL) { ++ res = -ENOMEM; ++ goto out_free_bios; ++ } ++ ++ if (rw == WRITE) ++ bio->bi_rw |= REQ_WRITE; ++ ++ bios++; ++ bio->bi_private = bw; ++ bio->bi_end_io = blk_bio_map_kern_endio; ++ ++ if (hbio == NULL) ++ hbio = tbio = bio; ++ else ++ tbio = tbio->bi_next = bio; ++ } ++ ++ bytes = min_t(size_t, len, PAGE_SIZE - offset); ++ ++ rc = bio_add_pc_page(q, bio, page, bytes, offset); ++ if (rc < bytes) { ++ if (unlikely(need_new_bio || (rc < 0))) { ++ if (rc < 0) ++ res = rc; ++ else ++ res = -EIO; ++ goto out_free_bios; ++ } else { ++ need_new_bio = true; ++ len -= rc; ++ offset += rc; ++ continue; ++ } ++ } ++ ++ need_new_bio = false; ++ offset = 0; ++ len -= bytes; ++ page = nth_page(page, 1); ++ } ++ } ++ ++ if (hbio == NULL) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ /* Total length must be aligned on DMA padding alignment */ ++ if ((tot_len & q->dma_pad_mask) && ++ !(rq->cmd_flags & REQ_COPY_USER)) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ if (bw != NULL) ++ atomic_set(&bw->bios_inflight, bios); ++ ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio->bi_next = NULL; ++ ++ blk_queue_bounce(q, &bio); ++ ++ res = blk_rq_append_bio(q, rq, bio); ++ if (unlikely(res != 0)) { ++ bio->bi_next = hbio; ++ hbio = bio; ++ /* We can have one or more bios bounced */ ++ goto out_unmap_bios; ++ } ++ } ++ ++ res = 0; ++ ++ rq->buffer = NULL; ++out: ++ return res; ++ ++out_unmap_bios: ++ blk_rq_unmap_kern_sg(rq, res); ++ ++out_free_bios: ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio_put(bio); ++ } ++ goto out; ++} ++ ++/** ++ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC ++ * @rq: request to fill ++ * @sgl: area to map ++ * @nents: number of elements in @sgl ++ * @gfp: memory allocation flags ++ * ++ * Description: ++ * Data will be mapped directly if possible. Otherwise a bounce ++ * buffer will be used. ++ */ ++int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp) ++{ ++ int res; ++ ++ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp); ++ if (unlikely(res != 0)) { ++ struct blk_kern_sg_work *bw = NULL; ++ ++ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw, ++ gfp, rq->q->bounce_gfp | gfp); ++ if (unlikely(res != 0)) ++ goto out; ++ ++ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl, ++ bw->sg_table.nents, bw, gfp); ++ if (res != 0) { ++ blk_free_kern_sg_work(bw); ++ goto out; ++ } ++ } ++ ++ rq->buffer = NULL; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(blk_rq_map_kern_sg); ++ ++/** ++ * blk_rq_unmap_kern_sg - unmap a request with kernel sg ++ * @rq: request to unmap ++ * @err: non-zero error code ++ * ++ * Description: ++ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called ++ * only in case of an error! ++ */ ++void blk_rq_unmap_kern_sg(struct request *rq, int err) ++{ ++ struct bio *bio = rq->bio; ++ ++ while (bio) { ++ struct bio *b = bio; ++ bio = bio->bi_next; ++ b->bi_end_io(b, err); ++ } ++ rq->bio = NULL; ++ ++ return; ++} ++EXPORT_SYMBOL(blk_rq_unmap_kern_sg); ++ + /** + * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage + * @q: request queue where request should be inserted + +=== modified file 'include/linux/blkdev.h' +--- old/include/linux/blkdev.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/blkdev.h 2014-04-17 22:08:48 +0000 +@@ -705,6 +705,8 @@ extern unsigned long blk_max_low_pfn, bl + #define BLK_DEFAULT_SG_TIMEOUT (60 * HZ) + #define BLK_MIN_SG_TIMEOUT (7 * HZ) + ++#define SCSI_EXEC_REQ_FIFO_DEFINED ++ + #ifdef CONFIG_BOUNCE + extern int init_emergency_isa_pool(void); + extern void blk_queue_bounce(struct request_queue *q, struct bio **bio); +@@ -825,6 +827,9 @@ extern int blk_rq_map_kern(struct reques + extern int blk_rq_map_user_iov(struct request_queue *, struct request *, + struct rq_map_data *, struct sg_iovec *, int, + unsigned int, gfp_t); ++extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp); ++extern void blk_rq_unmap_kern_sg(struct request *rq, int err); + extern int blk_execute_rq(struct request_queue *, struct gendisk *, + struct request *, int); + extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, + +=== modified file 'include/linux/scatterlist.h' +--- old/include/linux/scatterlist.h 2014-04-17 22:02:06 +0000 ++++ new/include/linux/scatterlist.h 2014-04-17 22:08:48 +0000 +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + struct sg_table { + struct scatterlist *sgl; /* the list */ +@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt + size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, + void *buf, size_t buflen, off_t skip); + ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len); ++ + /* + * Maximum number of entries that will be allocated in one piece, if + * a list larger than this is required then chaining will be utilized. + +=== modified file 'lib/scatterlist.c' +--- old/lib/scatterlist.c 2014-04-17 22:02:06 +0000 ++++ new/lib/scatterlist.c 2014-04-17 22:08:48 +0000 +@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter + return sg_copy_buffer(sgl, nents, buf, buflen, skip, true); + } + EXPORT_SYMBOL(sg_pcopy_to_buffer); ++ ++ ++/* ++ * Can switch to the next dst_sg element, so, to copy to strictly only ++ * one dst_sg element, it must be either last in the chain, or ++ * copy_len == dst_sg->length. ++ */ ++static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len, ++ size_t *pdst_offs, struct scatterlist *src_sg, ++ size_t copy_len) ++{ ++ int res = 0; ++ struct scatterlist *dst_sg; ++ size_t src_len, dst_len, src_offs, dst_offs; ++ struct page *src_page, *dst_page; ++ ++ dst_sg = *pdst_sg; ++ dst_len = *pdst_len; ++ dst_offs = *pdst_offs; ++ dst_page = sg_page(dst_sg); ++ ++ src_page = sg_page(src_sg); ++ src_len = src_sg->length; ++ src_offs = src_sg->offset; ++ ++ do { ++ void *saddr, *daddr; ++ size_t n; ++ ++ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) + ++ (src_offs & ~PAGE_MASK); ++ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) + ++ (dst_offs & ~PAGE_MASK); ++ ++ if (((src_offs & ~PAGE_MASK) == 0) && ++ ((dst_offs & ~PAGE_MASK) == 0) && ++ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) && ++ (copy_len >= PAGE_SIZE)) { ++ copy_page(daddr, saddr); ++ n = PAGE_SIZE; ++ } else { ++ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK), ++ PAGE_SIZE - (src_offs & ~PAGE_MASK)); ++ n = min(n, src_len); ++ n = min(n, dst_len); ++ n = min_t(size_t, n, copy_len); ++ memcpy(daddr, saddr, n); ++ } ++ dst_offs += n; ++ src_offs += n; ++ ++ kunmap_atomic(saddr); ++ kunmap_atomic(daddr); ++ ++ res += n; ++ copy_len -= n; ++ if (copy_len == 0) ++ goto out; ++ ++ src_len -= n; ++ dst_len -= n; ++ if (dst_len == 0) { ++ dst_sg = sg_next(dst_sg); ++ if (dst_sg == NULL) ++ goto out; ++ dst_page = sg_page(dst_sg); ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ } ++ } while (src_len > 0); ++ ++out: ++ *pdst_sg = dst_sg; ++ *pdst_len = dst_len; ++ *pdst_offs = dst_offs; ++ return res; ++} ++ ++/** ++ * sg_copy - copy one SG vector to another ++ * @dst_sg: destination SG ++ * @src_sg: source SG ++ * @nents_to_copy: maximum number of entries to copy ++ * @copy_len: maximum amount of data to copy. If 0, then copy all. ++ * ++ * Description: ++ * Data from the source SG vector will be copied to the destination SG ++ * vector. End of the vectors will be determined by sg_next() returning ++ * NULL. Returns number of bytes copied. ++ */ ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len) ++{ ++ int res = 0; ++ size_t dst_len, dst_offs; ++ ++ if (copy_len == 0) ++ copy_len = 0x7FFFFFFF; /* copy all */ ++ ++ if (nents_to_copy == 0) ++ nents_to_copy = 0x7FFFFFFF; /* copy all */ ++ ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ ++ do { ++ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs, ++ src_sg, copy_len); ++ copy_len -= copied; ++ res += copied; ++ if ((copy_len == 0) || (dst_sg == NULL)) ++ goto out; ++ ++ nents_to_copy--; ++ if (nents_to_copy == 0) ++ goto out; ++ ++ src_sg = sg_next(src_sg); ++ } while (src_sg != NULL); ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(sg_copy); + diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index c00798e75..58ae8a9f9 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -244,6 +244,12 @@ static int vdisk_attach(struct scst_device *dev); static void vdisk_detach(struct scst_device *dev); static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev); static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev); +static int vdisk_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); +static int vcdrom_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt); static int fileio_alloc_data_buf(struct scst_cmd *cmd); static int vdisk_parse(struct scst_cmd *); static int vcdrom_parse(struct scst_cmd *); @@ -265,6 +271,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p); static enum compl_status_e blockio_exec_write_verify(struct vdisk_cmd_params *p); static enum compl_status_e fileio_exec_write_verify(struct vdisk_cmd_params *p); static enum compl_status_e nullio_exec_write_verify(struct vdisk_cmd_params *p); +static enum compl_status_e nullio_exec_verify(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_read_capacity(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p); static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p); @@ -564,6 +571,7 @@ static struct scst_dev_type vdisk_file_devtype = { .exec = vdisk_exec, .on_free_cmd = fileio_on_free_cmd, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vdisk_get_supported_opcodes, .devt_priv = (void *)fileio_ops, #ifdef CONFIG_SCST_PROC .read_proc = vdisk_read_proc, @@ -612,6 +620,7 @@ static struct scst_dev_type vdisk_blk_devtype = { .parse = non_fileio_parse, .exec = non_fileio_exec, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vdisk_get_supported_opcodes, .devt_priv = (void *)blockio_ops, #ifndef CONFIG_SCST_PROC .add_device = vdisk_add_blockio_device, @@ -654,6 +663,7 @@ static struct scst_dev_type vdisk_null_devtype = { .exec = non_fileio_exec, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, .devt_priv = (void *)nullio_ops, + .get_supported_opcodes = vdisk_get_supported_opcodes, #ifndef CONFIG_SCST_PROC .add_device = vdisk_add_nullio_device, .del_device = vdisk_del_device, @@ -693,6 +703,7 @@ static struct scst_dev_type vcdrom_devtype = { .exec = vcdrom_exec, .on_free_cmd = fileio_on_free_cmd, .task_mgmt_fn_done = vdisk_task_mgmt_fn_done, + .get_supported_opcodes = vcdrom_get_supported_opcodes, #ifdef CONFIG_SCST_PROC .read_proc = vcdrom_read_proc, .write_proc = vcdrom_write_proc, @@ -1397,8 +1408,314 @@ static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) return INVALID_OPCODE; } +#define VDEV_DEF_RDPROTECT 0 +#define VDEV_DEF_WRPROTECT 0 +#define VDEV_DEF_VRPROTECT 0 + +#define VDEF_DEF_GROUP_NUM 0 + +static const struct scst_opcode_descriptor scst_op_descr_cwr = { + .od_opcode = COMPARE_AND_WRITE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { COMPARE_AND_WRITE, VDEV_DEF_WRPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 0, 0, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_format_unit = { + .od_opcode = FORMAT_UNIT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_LONG_TIMEOUT/HZ, + .od_cdb_usage_bits = { FORMAT_UNIT, 0xF0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_get_lba_status = { + .od_opcode = SERVICE_ACTION_IN, + .od_serv_action = SAI_GET_LBA_STATUS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SERVICE_ACTION_IN, SAI_GET_LBA_STATUS, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_allow_medium_removal = { + .od_opcode = ALLOW_MEDIUM_REMOVAL, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 3, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read6 = { + .od_opcode = READ_6, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_6, 0x1F, + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read10 = { + .od_opcode = READ_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_10, VDEV_DEF_RDPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read12 = { + .od_opcode = READ_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_12, VDEV_DEF_RDPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read16 = { + .od_opcode = READ_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_16, VDEV_DEF_RDPROTECT | 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_capacity = { + .od_opcode = READ_CAPACITY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, + 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_capacity16 = { + .od_opcode = SERVICE_ACTION_IN, + .od_serv_action = SAI_READ_CAPACITY_16, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SERVICE_ACTION_IN, SAI_READ_CAPACITY_16, + 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_start_stop_unit = { + .od_opcode = START_STOP, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { START_STOP, 1, 0, 0xF, 0xF7, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_sync_cache10 = { + .od_opcode = SYNCHRONIZE_CACHE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { SYNCHRONIZE_CACHE, 2, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_sync_cache16 = { + .od_opcode = SYNCHRONIZE_CACHE_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { SYNCHRONIZE_CACHE_16, 2, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_unmap = { + .od_opcode = UNMAP, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { UNMAP, 0, 0, 0, 0, 0, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify10 = { + .od_opcode = VERIFY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY, VDEV_DEF_VRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify12 = { + .od_opcode = VERIFY_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY_12, VDEV_DEF_VRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_verify16 = { + .od_opcode = VERIFY_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { VERIFY_16, VDEV_DEF_VRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write6 = { + .od_opcode = WRITE_6, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_6, 0x1F, + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write10 = { + .od_opcode = WRITE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_10, VDEV_DEF_WRPROTECT | 0x1A, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write12 = { + .od_opcode = WRITE_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_12, VDEV_DEF_WRPROTECT | 0x1A, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write16 = { + .od_opcode = WRITE_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_16, VDEV_DEF_WRPROTECT | 0x1A, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify10 = { + .od_opcode = WRITE_VERIFY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY, VDEV_DEF_WRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify12 = { + .od_opcode = WRITE_VERIFY_12, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY_12, VDEV_DEF_WRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + VDEF_DEF_GROUP_NUM, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_verify16 = { + .od_opcode = WRITE_VERIFY_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_VERIFY_16, VDEV_DEF_WRPROTECT | 0x16, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_same10 = { + .od_opcode = WRITE_SAME, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_SAME, VDEV_DEF_WRPROTECT | 0x8, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_write_same16 = { + .od_opcode = WRITE_SAME_16, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 16, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { WRITE_SAME_16, VDEV_DEF_WRPROTECT | 0x8, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, VDEF_DEF_GROUP_NUM, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + +static const struct scst_opcode_descriptor scst_op_descr_read_toc = { + .od_opcode = READ_TOC, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_REG_TIMEOUT/HZ, + .od_cdb_usage_bits = { READ_TOC, 0, 0xF, 0, 0, 0, 0xFF, + 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; + #define SHARED_OPS \ [SYNCHRONIZE_CACHE] = vdisk_synchronize_cache, \ + [SYNCHRONIZE_CACHE_16] = vdisk_synchronize_cache, \ [MODE_SENSE] = vdisk_exec_mode_sense, \ [MODE_SENSE_10] = vdisk_exec_mode_sense, \ [MODE_SELECT] = vdisk_exec_mode_select, \ @@ -1420,10 +1737,39 @@ static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) [UNMAP] = vdisk_exec_unmap, \ [WRITE_SAME] = vdisk_exec_write_same, \ [WRITE_SAME_16] = vdisk_exec_write_same, \ + [COMPARE_AND_WRITE] = vdisk_exec_caw, \ [MAINTENANCE_IN] = vdisk_exec_maintenance_in, \ [SEND_DIAGNOSTIC] = vdisk_exec_send_diagnostic, \ [FORMAT_UNIT] = vdisk_exec_format_unit, +#define SHARED_OPCODE_DESCRIPTORS \ + &scst_op_descr_sync_cache10, \ + &scst_op_descr_sync_cache16, \ + &scst_op_descr_mode_sense6, \ + &scst_op_descr_mode_sense10, \ + &scst_op_descr_mode_select6, \ + &scst_op_descr_mode_select10, \ + &scst_op_descr_log_select, \ + &scst_op_descr_log_sense, \ + &scst_op_descr_start_stop_unit, \ + &scst_op_descr_read_capacity, \ + &scst_op_descr_send_diagnostic, \ + &scst_op_descr_rtpg, \ + &scst_op_descr_read6, \ + &scst_op_descr_read10, \ + &scst_op_descr_read12, \ + &scst_op_descr_read16, \ + &scst_op_descr_write6, \ + &scst_op_descr_write10, \ + &scst_op_descr_write12, \ + &scst_op_descr_write16, \ + &scst_op_descr_write_verify10, \ + &scst_op_descr_write_verify12, \ + &scst_op_descr_write_verify16, \ + &scst_op_descr_verify10, \ + &scst_op_descr_verify12, \ + &scst_op_descr_verify16, + static vdisk_op_fn blockio_ops[256] = { [READ_6] = blockio_exec_read, [READ_10] = blockio_exec_read, @@ -1451,7 +1797,6 @@ static vdisk_op_fn fileio_ops[256] = { [WRITE_10] = fileio_exec_write, [WRITE_12] = fileio_exec_write, [WRITE_16] = fileio_exec_write, - [COMPARE_AND_WRITE] = vdisk_exec_caw, [WRITE_VERIFY] = fileio_exec_write_verify, [WRITE_VERIFY_12] = fileio_exec_write_verify, [WRITE_VERIFY_16] = fileio_exec_write_verify, @@ -1473,9 +1818,52 @@ static vdisk_op_fn nullio_ops[256] = { [WRITE_VERIFY] = nullio_exec_write_verify, [WRITE_VERIFY_12] = nullio_exec_write_verify, [WRITE_VERIFY_16] = nullio_exec_write_verify, + [VERIFY] = nullio_exec_verify, + [VERIFY_12] = nullio_exec_verify, + [VERIFY_16] = nullio_exec_verify, SHARED_OPS }; +#define VDISK_OPCODE_DESCRIPTORS \ + /* &scst_op_descr_get_lba_status, */ \ + &scst_op_descr_read_capacity16, \ + &scst_op_descr_write_same10, \ + &scst_op_descr_write_same16, \ + &scst_op_descr_unmap, \ + &scst_op_descr_format_unit, \ + &scst_op_descr_cwr, + +static const struct scst_opcode_descriptor *vdisk_opcode_descriptors[] = { + SHARED_OPCODE_DESCRIPTORS + VDISK_OPCODE_DESCRIPTORS + SCST_OPCODE_DESCRIPTORS +}; + +static const struct scst_opcode_descriptor *vcdrom_opcode_descriptors[] = { + SHARED_OPCODE_DESCRIPTORS + &scst_op_descr_allow_medium_removal, + &scst_op_descr_read_toc, + SCST_OPCODE_DESCRIPTORS +}; + +static int vdisk_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt) +{ + *out_supp_opcodes = vdisk_opcode_descriptors; + *out_supp_opcodes_cnt = ARRAY_SIZE(vdisk_opcode_descriptors); + return 0; +} + +static int vcdrom_get_supported_opcodes(struct scst_cmd *cmd, + const struct scst_opcode_descriptor ***out_supp_opcodes, + int *out_supp_opcodes_cnt) +{ + *out_supp_opcodes = vcdrom_opcode_descriptors; + *out_supp_opcodes_cnt = ARRAY_SIZE(vcdrom_opcode_descriptors); + return 0; +} + /* * Compute p->loff and p->fua. * Returns true for success or false otherwise and set error in the commeand. @@ -2248,9 +2636,9 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, (unsigned long long)start_lba, (unsigned long long)blocks); if (virt_dev->blockio) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) sector_t start_sector = start_lba << (cmd->dev->block_shift - 9); sector_t nr_sects = blocks << (cmd->dev->block_shift - 9); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) struct inode *inode = fd->f_dentry->d_inode; gfp_t gfp = cmd->cmd_gfp_mask; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31) @@ -3530,6 +3918,7 @@ out: static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p) { + /* Changing it don't forget to add it to vdisk_opcode_descriptors! */ return INVALID_OPCODE; } @@ -4237,7 +4626,11 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) bios++; need_new_bio = 0; bio->bi_end_io = blockio_endio; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + bio->bi_iter.bi_sector = lba_start0 << (block_shift - 9); +#else bio->bi_sector = lba_start0 << (block_shift - 9); +#endif bio->bi_bdev = bdev; bio->bi_private = blockio_work; /* @@ -4262,6 +4655,14 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) bio->bi_rw |= REQ_FUA; if (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + bio->bi_rw |= REQ_SYNC; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + bio->bi_rw |= 1 << BIO_RW_SYNCIO; +#else + bio->bi_rw |= 1 << BIO_RW_SYNC; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 bio->bi_rw |= REQ_META; @@ -4469,8 +4870,10 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, { DECLARE_COMPLETION_ONSTACK(c); struct block_device *bdev = virt_dev->bdev; + const bool is_vmalloc = is_vmalloc_addr(buf); struct bio *bio; void *p; + struct page *q; int max_nr_vecs, rc; unsigned bytes, off; ssize_t ret = -ENOMEM; @@ -4497,8 +4900,9 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, #endif for (p = buf; p < buf + len; p += bytes) { off = offset_in_page(p); - bytes = PAGE_SIZE - off; - rc = bio_add_page(bio, virt_to_page(p), bytes, off); + bytes = min_t(size_t, PAGE_SIZE - off, buf + len - p); + q = is_vmalloc ? vmalloc_to_page(p) : virt_to_page(p); + rc = bio_add_page(bio, q, bytes, off); if (WARN_ON_ONCE(rc < bytes)) goto free; } @@ -4566,7 +4970,7 @@ static ssize_t vdev_read_sync(struct scst_vdisk_dev *virt_dev, void *buf, if (virt_dev->nullio) return len; else if (virt_dev->blockio) - return blockio_rw_sync(virt_dev, buf, len, loff, 0/*read*/); + return blockio_rw_sync(virt_dev, buf, len, loff, READ_SYNC); else return fileio_read_sync(virt_dev->fd, buf, len, loff); } @@ -4574,21 +4978,12 @@ static ssize_t vdev_read_sync(struct scst_vdisk_dev *virt_dev, void *buf, static ssize_t vdev_write_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff) { - int rw; - - if (virt_dev->nullio) { + if (virt_dev->nullio) return len; - } else if (virt_dev->blockio) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) - rw = REQ_WRITE; -#else - rw = 1 << BIO_RW; -#endif - - return blockio_rw_sync(virt_dev, buf, len, loff, rw); - } else { + else if (virt_dev->blockio) + return blockio_rw_sync(virt_dev, buf, len, loff, WRITE_SYNC); + else return fileio_write_sync(virt_dev->fd, buf, len, loff); - } } static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) @@ -4725,7 +5120,10 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) goto out; } - WARN_ON_ONCE(length != 2 * data_len); + if (length != 2 * data_len) { + scst_set_invalid_field_in_cdb(cmd, 13, 0); + goto out; + } loff = p->loff; read = vdev_read_sync(virt_dev, read_buf, data_len, &loff); @@ -4806,6 +5204,11 @@ static enum compl_status_e nullio_exec_write_verify(struct vdisk_cmd_params *p) return CMD_SUCCEEDED; } +static enum compl_status_e nullio_exec_verify(struct vdisk_cmd_params *p) +{ + return CMD_SUCCEEDED; +} + static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd, struct scst_tgt_dev *tgt_dev) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index da49e0c15..b90f82447 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -88,6 +88,240 @@ static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, static void scst_free_descriptors(struct scst_cmd *cmd); +const struct scst_opcode_descriptor scst_op_descr_inquiry = { + .od_opcode = INQUIRY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { INQUIRY, 1, 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_inquiry); + +const struct scst_opcode_descriptor scst_op_descr_tur = { + .od_opcode = TEST_UNIT_READY, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { TEST_UNIT_READY, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_tur); + +const struct scst_opcode_descriptor scst_op_descr_log_select = { + .od_opcode = LOG_SELECT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { LOG_SELECT, 3, 0xFF, 0xFF, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_log_select); + +const struct scst_opcode_descriptor scst_op_descr_log_sense = { + .od_opcode = LOG_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { LOG_SENSE, 1, 0xFF, 0xFF, 0, 0xFF, 0xFF, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_log_sense); + +const struct scst_opcode_descriptor scst_op_descr_mode_select6 = { + .od_opcode = MODE_SELECT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SELECT, 0x11, 0, 0, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_select6); + +const struct scst_opcode_descriptor scst_op_descr_mode_sense6 = { + .od_opcode = MODE_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SENSE, 8, 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_sense6); + +const struct scst_opcode_descriptor scst_op_descr_mode_select10 = { + .od_opcode = MODE_SELECT_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SELECT_10, 0x11, 0, 0, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_select10); + +const struct scst_opcode_descriptor scst_op_descr_mode_sense10 = { + .od_opcode = MODE_SENSE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MODE_SENSE_10, 0x18, 0xFF, 0xFF, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_mode_sense10); + +const struct scst_opcode_descriptor scst_op_descr_rtpg = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_TARGET_PGS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, 0xE0|MI_REPORT_TARGET_PGS, 0, 0, + 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_rtpg); + +const struct scst_opcode_descriptor scst_op_descr_stpg = { + .od_opcode = MAINTENANCE_OUT, + .od_serv_action = MO_SET_TARGET_PGS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MO_SET_TARGET_PGS, 0, 0, 0, 0, + 0xFF, 0xFF, 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_stpg); + +const struct scst_opcode_descriptor scst_op_descr_send_diagnostic = { + .od_opcode = SEND_DIAGNOSTIC, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { SEND_DIAGNOSTIC, 0xF7, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_send_diagnostic); + +const struct scst_opcode_descriptor scst_op_descr_reserve6 = { + .od_opcode = RESERVE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RESERVE, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_reserve6); + +const struct scst_opcode_descriptor scst_op_descr_release6 = { + .od_opcode = RELEASE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RELEASE, 0, 0, 0, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_release6); + +const struct scst_opcode_descriptor scst_op_descr_reserve10 = { + .od_opcode = RESERVE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RESERVE_10, 0, 0, 0, 0, 0, 0, 0, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_reserve10); + +const struct scst_opcode_descriptor scst_op_descr_release10 = { + .od_opcode = RELEASE_10, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { RELEASE_10, 0, 0, 0, 0, 0, 0, 0, 0, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_release10); + +const struct scst_opcode_descriptor scst_op_descr_pr_in = { + .od_opcode = PERSISTENT_RESERVE_IN, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { PERSISTENT_RESERVE_IN, 0x1F, 0, 0, 0, 0, 0, 0xFF, 0xFF, + SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_pr_in); + +const struct scst_opcode_descriptor scst_op_descr_pr_out = { + .od_opcode = PERSISTENT_RESERVE_OUT, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 10, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { PERSISTENT_RESERVE_OUT, 0x1F, 0xFF, 0, 0, 0xFF, + 0xFF, 0xFF, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_pr_out); + +const struct scst_opcode_descriptor scst_op_descr_report_luns = { + .od_opcode = REPORT_LUNS, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { REPORT_LUNS, 0, 0xFF, 0, 0, 0, 0xFF, 0xFF, + 0xFF, 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_luns); + +const struct scst_opcode_descriptor scst_op_descr_request_sense = { + .od_opcode = REQUEST_SENSE, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 6, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { REQUEST_SENSE, 1, 0, 0, 0xFF, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_request_sense); + +const struct scst_opcode_descriptor scst_op_descr_report_supp_tm_fns = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS, + 0x80, 0, 0, 0, 0xFF, 0xFF, 0xFF, + 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_supp_tm_fns); + +const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes = { + .od_opcode = MAINTENANCE_IN, + .od_serv_action = MI_REPORT_SUPPORTED_OPERATION_CODES, + .od_serv_action_valid = 1, + .od_support = 3, /* supported as in the standard */ + .od_cdb_size = 12, + .od_nominal_timeout = SCST_DEFAULT_NOMINAL_TIMEOUT_SEC, + .od_recommended_timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT/HZ, + .od_cdb_usage_bits = { MAINTENANCE_IN, MI_REPORT_SUPPORTED_OPERATION_CODES, + 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0, SCST_OD_DEFAULT_CONTROL_BYTE }, +}; +EXPORT_SYMBOL(scst_op_descr_report_supp_opcodes); + struct scst_sdbops; static int get_cdb_info_len_10(struct scst_cmd *cmd, @@ -122,7 +356,7 @@ static int get_cdb_info_verify16(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_len_1(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); -static int get_cdb_info_lba_2_len_1_256(struct scst_cmd *cmd, +static int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); static int get_cdb_info_bidi_lba_4_len_2(struct scst_cmd *cmd, const struct scst_sdbops *sdbops); @@ -313,9 +547,9 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_TEST_IO_IN_SIRQ_ALLOWED| #endif SCST_WRITE_EXCL_ALLOWED, - .info_lba_off = 2, .info_lba_len = 2, + .info_lba_off = 1, .info_lba_len = 3, .info_len_off = 4, .info_len_len = 1, - .get_cdb_info = get_cdb_info_lba_2_len_1_256}, + .get_cdb_info = get_cdb_info_lba_3_len_1_256}, {.ops = 0x08, .devkey = " MV O OV ", .info_op_name = "READ(6)", .info_data_direction = SCST_DATA_READ, @@ -343,9 +577,9 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_TEST_IO_IN_SIRQ_ALLOWED| #endif SCST_WRITE_MEDIUM, - .info_lba_off = 2, .info_lba_len = 2, + .info_lba_off = 1, .info_lba_len = 3, .info_len_off = 4, .info_len_len = 1, - .get_cdb_info = get_cdb_info_lba_2_len_1_256}, + .get_cdb_info = get_cdb_info_lba_3_len_1_256}, {.ops = 0x0A, .devkey = " M O OV ", .info_op_name = "WRITE(6)", .info_data_direction = SCST_DATA_WRITE, @@ -6618,10 +6852,11 @@ static int get_cdb_info_len_1(struct scst_cmd *cmd, return 0; } -static int get_cdb_info_lba_2_len_1_256(struct scst_cmd *cmd, +static int get_cdb_info_lba_3_len_1_256(struct scst_cmd *cmd, const struct scst_sdbops *sdbops) { - cmd->lba = get_unaligned_be16(cmd->cdb + sdbops->info_lba_off); + cmd->lba = (cmd->cdb[sdbops->info_lba_off] & 0x1F) << 16; + cmd->lba |= get_unaligned_be16(cmd->cdb + sdbops->info_lba_off + 1); /* * From the READ(6) specification: a TRANSFER LENGTH field set to zero * specifies that 256 logical blocks shall be read. @@ -6900,7 +7135,8 @@ static int get_cdb_info_min(struct scst_cmd *cmd, break; case MI_REPORT_SUPPORTED_OPERATION_CODES: cmd->op_name = "REPORT SUPPORTED OPERATION CODES"; - cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED; + cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED | + SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; break; case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: cmd->op_name = "REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS"; diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 5ab3b6eb1..48c246d3d 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -2201,7 +2201,7 @@ void scst_pr_preempt(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) static void scst_cmd_done_pr_preempt(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context) { - void (*saved_cmd_done) (struct scst_cmd *cmd, int next_state, + void (*saved_cmd_done)(struct scst_cmd *cmd, int next_state, enum scst_exec_context pref_context); TRACE_ENTRY(); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 705b24263..ff80f55d1 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -2111,6 +2111,208 @@ out_compl: return res; } +static int scst_report_supported_opcodes(struct scst_cmd *cmd) +{ + int res = SCST_EXEC_COMPLETED; + int length, buf_len, i, offs; + uint8_t *address; + uint8_t *buf; + bool inline_buf; + bool rctd = cmd->cdb[2] >> 7; + int options = cmd->cdb[2] & 7; + int req_opcode = cmd->cdb[3]; + int req_sa = get_unaligned_be16(&cmd->cdb[4]); + const struct scst_opcode_descriptor *op = NULL; + const struct scst_opcode_descriptor **supp_opcodes = NULL; + int supp_opcodes_cnt; + + TRACE_ENTRY(); + + if (cmd->devt->get_supported_opcodes == NULL) { + TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + goto out_compl; + } else { + int rc = cmd->devt->get_supported_opcodes(cmd, &supp_opcodes, + &supp_opcodes_cnt); + if (rc != 0) + goto out_compl; + } + + TRACE_DBG("cmd %p, options %d, req_opcode %x, req_sa %x, rctd %d", + cmd, options, req_opcode, req_sa, rctd); + + switch (options) { + case 0: /* all */ + buf_len = 4; + for (i = 0; i < supp_opcodes_cnt; i++) { + buf_len += 8; + if (rctd) + buf_len += 12; + } + break; + case 1: + buf_len = 0; + for (i = 0; i < supp_opcodes_cnt; i++) { + if (req_opcode == supp_opcodes[i]->od_opcode) { + op = supp_opcodes[i]; + if (op->od_serv_action_valid) { + TRACE(TRACE_MINOR, "Requested opcode %x " + "with unexpected service action " + "(dev %s, initiator %s)", + req_opcode, cmd->dev->virt_name, + cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + buf_len = 4 + op->od_cdb_size; + if (rctd) + buf_len += 12; + break; + } + } + if (op == NULL) { + TRACE(TRACE_MINOR, "Requested opcode %x not found " + "(dev %s, initiator %s)", req_opcode, + cmd->dev->virt_name, cmd->sess->initiator_name); + buf_len = 4; + } + break; + case 2: + buf_len = 0; + for (i = 0; i < supp_opcodes_cnt; i++) { + if (req_opcode == supp_opcodes[i]->od_opcode) { + op = supp_opcodes[i]; + if (!op->od_serv_action_valid) { + TRACE(TRACE_MINOR, "Requested opcode %x " + "without expected service action " + "(dev %s, initiator %s)", + req_opcode, cmd->dev->virt_name, + cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + if (req_sa != op->od_serv_action) { + op = NULL; /* reset it */ + continue; + } + buf_len = 4 + op->od_cdb_size; + if (rctd) + buf_len += 12; + break; + } + } + if (op == NULL) { + TRACE(TRACE_MINOR, "Requested opcode %x/%x not found " + "(dev %s, initiator %s)", req_opcode, req_sa, + cmd->dev->virt_name, cmd->sess->initiator_name); + buf_len = 4; + } + break; + default: + PRINT_ERROR("REPORT SUPPORTED OPERATION CODES: REPORTING OPTIONS " + "%x not supported (dev %s, initiator %s)", options, + cmd->dev->virt_name, cmd->sess->initiator_name); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out_compl; + } + + length = scst_get_buf_full_sense(cmd, &address); + TRACE_DBG("length %d, buf_len %d, op %p", length, buf_len, op); + if (unlikely(length <= 0)) + goto out_compl; + + if (length >= buf_len) { + buf = address; + inline_buf = true; + } else { + buf = vmalloc(buf_len); /* it can be big */ + if (buf == NULL) { + PRINT_ERROR("Unable to allocate REPORT SUPPORTED " + "OPERATION CODES buffer with size %d", buf_len); + scst_set_busy(cmd); + goto out_err_put; + } + inline_buf = false; + } + + memset(buf, 0, buf_len); + + switch (options) { + case 0: /* all */ + put_unaligned_be32(buf_len - 3, &buf[0]); + offs = 4; + for (i = 0; i < supp_opcodes_cnt; i++) { + op = supp_opcodes[i]; + buf[offs] = op->od_opcode; + if (op->od_serv_action_valid) { + put_unaligned_be16(op->od_serv_action, &buf[offs + 2]); + buf[offs + 5] |= 1; + } + put_unaligned_be16(op->od_cdb_size, &buf[offs + 6]); + offs += 8; + if (rctd) { + buf[(offs - 8) + 5] |= 2; + buf[offs + 1] = 0xA; + buf[offs + 3] = op->od_comm_specific_timeout; + put_unaligned_be32(op->od_nominal_timeout, &buf[offs + 4]); + put_unaligned_be32(op->od_recommended_timeout, &buf[offs + 8]); + offs += 12; + } + } + break; + case 1: + case 2: + if (op != NULL) { + buf[1] |= op->od_support; + put_unaligned_be16(op->od_cdb_size, &buf[2]); + memcpy(&buf[4], op->od_cdb_usage_bits, op->od_cdb_size); + if (rctd) { + buf[1] |= 0x80; + offs = 4 + op->od_cdb_size; + buf[offs + 1] = 0xA; + buf[offs + 3] = op->od_comm_specific_timeout; + put_unaligned_be32(op->od_nominal_timeout, &buf[offs + 4]); + put_unaligned_be32(op->od_recommended_timeout, &buf[offs + 8]); + } + } + break; + default: + sBUG_ON(1); + goto out_compl; + } + + if (length > buf_len) + length = buf_len; + if (!inline_buf) { + memcpy(address, buf, length); + vfree(buf); + } + + scst_put_buf_full(cmd, address); + if (length < cmd->resp_data_len) + scst_set_resp_data_len(cmd, length); + +out_compl: + if ((supp_opcodes != NULL) && (cmd->devt->put_supported_opcodes != NULL)) + cmd->devt->put_supported_opcodes(cmd, supp_opcodes, supp_opcodes_cnt); + + cmd->completed = 1; + + /* Report the result */ + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); + + TRACE_EXIT_RES(res); + return res; + +out_err_put: + scst_put_buf_full(cmd, address); + goto out_compl; +} + static int scst_maintenance_in(struct scst_cmd *cmd) { int res; @@ -2121,6 +2323,9 @@ static int scst_maintenance_in(struct scst_cmd *cmd) case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: res = scst_report_supported_tm_fns(cmd); break; + case MI_REPORT_SUPPORTED_OPERATION_CODES: + res = scst_report_supported_opcodes(cmd); + break; default: res = SCST_EXEC_NOT_COMPLETED; break; @@ -6880,7 +7085,7 @@ restart: */ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic, const char *initiator_name, void *tgt_priv, void *result_fn_data, - void (*result_fn) (struct scst_session *sess, void *data, int result)) + void (*result_fn)(struct scst_session *sess, void *data, int result)) { struct scst_session *sess; int res; @@ -6978,7 +7183,7 @@ EXPORT_SYMBOL(scst_register_session_non_gpl); * Otherwise, your target driver could wait for those commands forever. */ void scst_unregister_session(struct scst_session *sess, int wait, - void (*unreg_done_fn) (struct scst_session *sess)) + void (*unreg_done_fn)(struct scst_session *sess)) { unsigned long flags; DECLARE_COMPLETION_ONSTACK(c); @@ -7191,8 +7396,8 @@ static struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess, * Returns the command on success or NULL otherwise. */ struct scst_cmd *scst_find_cmd(struct scst_session *sess, void *data, - int (*cmp_fn) (struct scst_cmd *cmd, - void *data)) + int (*cmp_fn)(struct scst_cmd *cmd, + void *data)) { struct scst_cmd *cmd = NULL; unsigned long flags = 0; diff --git a/scst_local/in-tree/Makefile-3.14 b/scst_local/in-tree/Makefile-3.14 new file mode 100644 index 000000000..8cbbbff63 --- /dev/null +++ b/scst_local/in-tree/Makefile-3.14 @@ -0,0 +1,2 @@ +obj-$(CONFIG_SCST_LOCAL) += scst_local.o + diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm index dc2748c40..47e9ad172 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm @@ -741,7 +741,7 @@ sub luns { close $lHandle; - return (\%luns, undef); + return \%luns; } sub aluaAttributes { diff --git a/scstadmin/scstadmin.sysfs/scstadmin b/scstadmin/scstadmin.sysfs/scstadmin index 6e976e93b..8ee27556c 100755 --- a/scstadmin/scstadmin.sysfs/scstadmin +++ b/scstadmin/scstadmin.sysfs/scstadmin @@ -3414,11 +3414,11 @@ sub listAttributes { my $found = FALSE; - foreach my $attribute (keys %{$attributes}) { + foreach my $attribute (sort keys %{$attributes}) { my $first = TRUE; if (defined($$attributes{$attribute}->{'keys'})) { - foreach my $key (keys %{$$attributes{$attribute}->{'keys'}}) { + foreach my $key (sort keys %{$$attributes{$attribute}->{'keys'}}) { my $value = $$attributes{$attribute}->{'keys'}->{$key}->{'value'}; my $static = ($$attributes{$attribute}->{'static'}) ? 'No' : 'Yes'; $value = '' if ($value eq ''); diff --git a/srpt/README b/srpt/README index 01297583d..f605758b8 100644 --- a/srpt/README +++ b/srpt/README @@ -1,11 +1,10 @@ SCSI RDMA Protocol (SRP) Target driver for Linux ================================================= -The SRP target driver has been designed to work on top of the Linux -InfiniBand kernel drivers -- either the InfiniBand drivers included -with a Linux distribution or the OFED InfiniBand drivers. For more -information about using the SRP target driver in combination with -OFED, see also README.ofed. +The SRP target driver has been designed to work on top of the Linux RDMA +kernel drivers -- either the RDMA drivers included with a Linux distribution +or the OFED RDMA drivers. For more information about using the SRP target +driver in combination with OFED, see also README.ofed. The SRP target driver has been implemented as an SCST driver. This makes it possible to support a lot of I/O modes on real and virtual @@ -30,9 +29,13 @@ Installation Building and installing the SRP target driver is possible as follows: cd ${SCST_DIR} - make -s scst_clean scst scst_install - make -s srpt_clean srpt srpt_install - make -s scstadm scstadm_install + if type -p rpm >/dev/null; then + make -s rpm + sudo rpm -U rpmbuilddir/RPMS/*/*rpm scstadmin/rpmbuilddir/RPMS/*/*rpm + else + make -s scst_clean srpt_clean scst srpt scstadmin + sudo make -s scst_install srpt_install scstadm_install + fi The ib_srpt kernel module supports the following parameters: * one_target_per_port (boolean) and @@ -48,6 +51,11 @@ The ib_srpt kernel module supports the following parameters: use_node_guid_in_target_name are false. Mode (2) is choosen if one_target_per_port is false and use_node_guid_in_target_name is true. Mode (3) is choosen if one_target_per_port is true. +* rdma_cm_port (number) + A 16-bit number that specifies the port number to be registered via the + RDMA/CM. Must be specified to make communication over RoCE or iWARP + possible. If this parameter is zero (the default value) the SRP target + driver does not register with the RDMA/CM. * srp_max_req_size (number) Maximum size of an SRP control message in bytes. Examples of SRP control messages are: login request, logout request, data transfer request, ... @@ -109,6 +117,11 @@ Now verify that loading the configuration from file works correctly: /etc/init.d/scst reload +Note: when using InfiniBand loading the ib_ipoib kernel module and assigning +an IP address to each IPoIB interface is only needed when using the RDMA/CM. +When using the IB/CM however, it is allowed but not necessary to load the +ib_ipoib kernel module. + Configuring the SRP Initiator System ------------------------------------ @@ -117,7 +130,8 @@ First of all, load the SRP kernel module as follows: modprobe ib_srp -Next, discover the new SRP target by running the srp_daemon command: +Next, when using InfiniBand, discover the new SRP target by running the +srp_daemon command: for d in /dev/infiniband/umad*; do srp_daemon -oacd$d; done @@ -138,7 +152,20 @@ The meaning of the parameters in the above command is as follows: * pkey: IB partition key (P_Key) of the target to connect to. * service_id: must match ioc_guid. -Target GIDs can be queried e.g. via sysfs: +When using RoCE or iWARP, log in to the target system to determine the id_ext +and ioc_guid parameters and use these to log in. An example: + + [ target system ] + # sed 's/,\(pkey\|dgid\|ioc_guid\)=[^,]*//g' $(find /sys/kernel/scst_tgt/targets/ib_srpt -name login_info) | uniq + id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 + + [ initiator system ] + echo dest=192.168.5.1:5000,id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 + >/sys/class/infiniband_srp/srp-mlx4_0-1/add_target + echo dest=192.168.6.1:5000,id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 + >/sys/class/infiniband_srp/srp-mlx4_0-2/add_target + +Initiator port GIDs can be queried e.g. via sysfs: $ for f in /sys/devices/*/*/*/infiniband/*/ports/*/gids/0; do echo $f; \ cat $f | sed 's/://g'; done @@ -166,9 +193,9 @@ Target names The name assigned by the ib_srpt target driver to an SCST target is either ib_srpt_target_, the node GUID of a HCA in hexadecimal form with a colon -after every fourth digit or the port GUID with a colon afer every fourth -digit. The HCA node and port GUIDs can be obtained via the ibv_devinfo -command. An example: +after every fourth digit or the port GID with a colon afer every fourth +digit. The HCA node GUID and the port GIDs can be obtained via the +ibv_devinfo command. An example: # ibv_devinfo -v | grep -E '[^a-z]port:|guid|GID' node_guid: 0002:c903:0005:f34e @@ -359,7 +386,8 @@ Performance Notes - Initiator Side * The SRP initiator limits by default the queue depth to 64 commands. If your workload benefits from a larger queue depth, enlarge the queue depth by - setting the max_cmd_per_lun parameter in the SRP login string. + setting the max_cmd_per_lun and queue_size parameters in the SRP login + string. * The following parameters have a small but measurable impact on SRP performance: @@ -372,7 +400,7 @@ Performance Notes - Both Sides ------------------------------ * Disabling CONFIG_SCHED_DEBUG and CONFIG_SCHEDSTATS in the kernel config - helps. + improves performance. * Disable CONFIG_IRQSOFF_TRACER such that CONFIG_TRACE_IRQFLAGS is disabled. @@ -421,5 +449,4 @@ A: This means that you are using a system on which OFED has been installed but Feedback -------- -Send questions about this driver to scst-devel@lists.sourceforge.net, CC: -Vu Pham and Bart Van Assche . +Send questions about this driver to scst-devel@lists.sourceforge.net. diff --git a/srpt/patches/kernel-3.14-pre-cflags.patch b/srpt/patches/kernel-3.14-pre-cflags.patch new file mode 100644 index 000000000..3964ee179 --- /dev/null +++ b/srpt/patches/kernel-3.14-pre-cflags.patch @@ -0,0 +1,12 @@ +diff --git a/Makefile b/Makefile +index 540f7b2..078307f 100644 +--- a/Makefile ++++ b/Makefile +@@ -361,6 +361,7 @@ USERINCLUDE := \ + # Use LINUXINCLUDE when you must reference the include/ directory. + # Needed to be compatible with the O= option + LINUXINCLUDE := \ ++ $(PRE_CFLAGS) \ + -I$(srctree)/arch/$(hdr-arch)/include \ + -Iarch/$(hdr-arch)/include/generated \ + $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/session-management.txt b/srpt/session-management.txt index 752218787..89147cca8 100644 --- a/srpt/session-management.txt +++ b/srpt/session-management.txt @@ -2,7 +2,8 @@ ============================== The following actions related to SRP sessions can all occur concurrently: -* IB communication manager (CM) invokes srpt_cm_handler(). +* The communication manager invokes either srpt_ib_cm_handler() or + srpt_rdma_cm_handler(). * HCA driver invokes the queue pair (QP) completion handler srpt_completion(). * HCA driver invokes the QP async event handler srpt_qp_event(). * HCA transfers data between initiator and target via RDMA. @@ -11,7 +12,7 @@ The following actions related to SRP sessions can all occur concurrently: The actions that occur over the lifetime of a session are as follows: - A REQ message is received from the initiator. -- srpt_cm_req_recv() is invoked and allocates a queue pair and creates a +- srpt_cm_req_recv() is invoked, allocates a queue pair and creates a completion thread. - If the connection request is not accepted, a REJ message is sent and srpt_close_ch() is invoked. The srpt_close_ch() call causes the completion @@ -21,14 +22,12 @@ The actions that occur over the lifetime of a session are as follows: invoked. That function changes the queue pair state into RTS, the channel state into CH_LIVE and wakes up the completion thread. - RDMA communication starts and continues until either a DREQ message is - received or sent. The function ib_send_cm_dreq() can get invoked - either because a target port is disabled or from inside the - srpt_close_session() function. + received or sent. A DREQ is sent either because a target port is disabled or + from inside the srpt_close_session() function. - After a DREQ has been sent either a DREP will be received (srpt_cm_drep_recv()) or the TimeWait state will be reached and will be left (srpt_cm_timewait_exit()). -- srpt_cm_dre[pq]_recv() and srpt_cm_timewait_exit() all invoke - srpt_close_ch(). +- srpt_cm_drep_recv() and srpt_cm_timewait_exit() invoke srpt_close_ch(). - srpt_close_ch() changes the channel state into CH_DISCONNECTING, the queue pair state into IB_QPS_ERR and queues a zero-length write. - Upon receipt of the zero-length write completion the channel state is diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 05bcbc4b5..17ad77e70 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #if defined(CONFIG_SCST_PROC) #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -98,6 +99,10 @@ module_param(trace_flag, long, 0644); MODULE_PARM_DESC(trace_flag, "SCST trace flags."); #endif +static u16 rdma_cm_port; +module_param(rdma_cm_port, short, 0444); +MODULE_PARM_DESC(rdma_cm_port, "Port number RDMA/CM will bind to."); + static unsigned srp_max_rdma_size = DEFAULT_MAX_RDMA_SIZE; module_param(srp_max_rdma_size, int, 0644); MODULE_PARM_DESC(srp_max_rdma_size, @@ -154,6 +159,8 @@ module_param(one_target_per_port, bool, 0444); MODULE_PARM_DESC(one_target_per_port, "One SCST target per HCA port instead of one per HCA."); +static struct rdma_cm_id *rdma_cm_id; + static int srpt_get_u64_x(char *buffer, struct kernel_param *kp) { return sprintf(buffer, "0x%016llx", *(u64 *)kp->arg); @@ -339,14 +346,17 @@ static const char *get_ch_state_name(enum rdma_ch_state s) */ static void srpt_qp_event(struct ib_event *event, struct srpt_rdma_ch *ch) { - TRACE_DBG("QP event %d on cm_id=%p sess_name=%s state=%s", - event->event, ch->cm_id, ch->sess_name, + TRACE_DBG("QP event %d on ch=%p sess_name=%s state=%s", + event->event, ch, ch->sess_name, get_ch_state_name(ch->state)); switch (event->event) { case IB_EVENT_COMM_EST: #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) || defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19) - ib_cm_notify(ch->cm_id, event->event); + if (ch->using_rdma_cm) + rdma_notify(ch->rdma_cm.cm_id, event->event); + else + ib_cm_notify(ch->ib_cm.cm_id, event->event); #else /* Vanilla 2.6.19 kernel (or before) without OFED. */ PRINT_ERROR("how to perform ib_cm_notify() on a" @@ -646,17 +656,6 @@ static int srpt_refresh_port(struct srpt_port *sport) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) /* commit a3f5adaf4 */ - switch (rdma_port_get_link_layer(sport->sdev->device, sport->port)) { - case IB_LINK_LAYER_UNSPECIFIED: - case IB_LINK_LAYER_INFINIBAND: - break; - case IB_LINK_LAYER_ETHERNET: - default: - return 0; - } -#endif - memset(&port_modify, 0, sizeof(port_modify)); port_modify.set_port_cap_mask = IB_PORT_DEVICE_MGMT_SUP; port_modify.clr_port_cap_mask = 0; @@ -1116,15 +1115,20 @@ static int srpt_init_ch_qp(struct srpt_rdma_ch *ch, struct ib_qp *qp) struct ib_qp_attr *attr; int ret; + WARN_ON_ONCE(ch->using_rdma_cm); + attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; attr->qp_state = IB_QPS_INIT; - attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_READ | - IB_ACCESS_REMOTE_WRITE; attr->port_num = ch->sport->port; - attr->pkey_index = ch->pkey_index; + + ret = ib_find_cached_pkey(ch->sport->sdev->device, ch->sport->port, + ch->pkey, &attr->pkey_index); + if (ret < 0) + PRINT_ERROR("Translating pkey %#x failed (%d) - using index 0", + ch->pkey, ret); ret = ib_modify_qp(qp, attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS | IB_QP_PORT | @@ -1147,15 +1151,18 @@ static int srpt_ch_qp_rtr(struct srpt_rdma_ch *ch, struct ib_qp *qp) int attr_mask; int ret; + WARN_ON_ONCE(ch->using_rdma_cm); + attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; attr->qp_state = IB_QPS_RTR; - ret = ib_cm_init_qp_attr(ch->cm_id, attr, &attr_mask); + ret = ib_cm_init_qp_attr(ch->ib_cm.cm_id, attr, &attr_mask); if (ret) goto out; + attr->qp_access_flags = 0; attr->max_dest_rd_atomic = 4; ret = ib_modify_qp(qp, attr, attr_mask); @@ -1177,45 +1184,21 @@ static int srpt_ch_qp_rts(struct srpt_rdma_ch *ch, struct ib_qp *qp) struct ib_qp_attr *attr; int attr_mask; int ret; - uint64_t T_tr_ns, max_compl_time_ms; - uint32_t T_tr_ms; + + WARN_ON_ONCE(ch->using_rdma_cm); attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; attr->qp_state = IB_QPS_RTS; - ret = ib_cm_init_qp_attr(ch->cm_id, attr, &attr_mask); + ret = ib_cm_init_qp_attr(ch->ib_cm.cm_id, attr, &attr_mask); if (ret) goto out; + attr->qp_access_flags = 0; attr->max_rd_atomic = 4; - /* - * From IBTA C9-140: Transport Timer timeout interval - * T_tr = 4.096 us * 2**(local ACK timeout) where the local ACK timeout - * is a five-bit value, with zero meaning that the timer is disabled. - */ - WARN_ON(attr->timeout >= (1 << 5)); - if (attr->timeout) { - T_tr_ns = 1ULL << (12 + attr->timeout); - max_compl_time_ms = attr->retry_cnt * 4 * T_tr_ns; - do_div(max_compl_time_ms, 1000000); - T_tr_ms = T_tr_ns; - do_div(T_tr_ms, 1000000); - TRACE_DBG("Session %s: QP local ack timeout = %d or T_tr =" - " %u ms; retry_cnt = %d; max compl. time = %d ms", - ch->sess_name, attr->timeout, T_tr_ms, - attr->retry_cnt, (unsigned)max_compl_time_ms); - - if (max_compl_time_ms >= RDMA_COMPL_TIMEOUT_S * 1000) { - PRINT_ERROR("Maximum RDMA completion time (%d ms)" - " exceeds ib_srpt timeout (%d ms)", - (unsigned)max_compl_time_ms, - 1000 * RDMA_COMPL_TIMEOUT_S); - } - } - ret = ib_modify_qp(qp, attr, attr_mask); out: @@ -1377,7 +1360,7 @@ static void srpt_abort_cmd(struct srpt_send_ioctx *ioctx, TRACE_EXIT(); } -void srpt_on_abort_cmd(struct scst_cmd *cmd) +static void srpt_on_abort_cmd(struct scst_cmd *cmd) { struct srpt_send_ioctx *ioctx = scst_cmd_get_tgt_priv(cmd); struct srpt_rdma_ch *ch = ioctx->ch; @@ -1730,9 +1713,9 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch, srp_tsk = recv_ioctx->ioctx.buf; TRACE_DBG("recv_tsk_mgmt= %d for task_tag= %lld" - " using tag= %lld cm_id= %p sess= %p", + " using tag= %lld ch= %p sess= %p", srp_tsk->tsk_mgmt_func, srp_tsk->task_tag, srp_tsk->tag, - ch->cm_id, ch->scst_sess); + ch, ch->scst_sess); send_ioctx->tsk_mgmt.tag = srp_tsk->tag; @@ -2043,20 +2026,20 @@ static void srpt_unreg_sess(struct scst_session *scst_sess) sdev, ch->rq_size, ch->max_rsp_size, DMA_TO_DEVICE); - /* - * If the connection is still established, ib_destroy_cm_id() will - * send a DREQ. - */ - ib_destroy_cm_id(ch->cm_id); + /* Wait until CM callbacks have finished and prevent new callbacks. */ + if (ch->using_rdma_cm) + rdma_destroy_id(ch->rdma_cm.cm_id); + else + ib_destroy_cm_id(ch->ib_cm.cm_id); /* * Invoke wake_up() inside the lock to avoid that srpt_tgt disappears * after list_del() and before wake_up() has been invoked. */ - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_del(&ch->list); wake_up(&srpt_tgt->ch_releaseQ); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); kref_put(&ch->kref, srpt_free_ch); } @@ -2146,36 +2129,42 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) WARN_ON(ch->max_sge < 1); qp_init->cap.max_send_sge = ch->max_sge; - ch->qp = ib_create_qp(sdev->pd, qp_init); - if (IS_ERR(ch->qp)) { - ret = PTR_ERR(ch->qp); - PRINT_ERROR("failed to create_qp ret= %d", ret); - goto err_destroy_cq; + if (ch->using_rdma_cm) { + ret = rdma_create_qp(ch->rdma_cm.cm_id, sdev->pd, qp_init); + ch->qp = ch->rdma_cm.cm_id->qp; + if (ret) + PRINT_ERROR("failed to create queue pair (%d)", ret); + } else { + ch->qp = ib_create_qp(sdev->pd, qp_init); + if (!IS_ERR(ch->qp)) { + ret = srpt_init_ch_qp(ch, ch->qp); + if (ret) { + PRINT_ERROR("srpt_init_ch_qp(%#x) failed (%d)", + ch->qp->qp_num, ret); + ib_destroy_qp(ch->qp); + } + } else { + ret = PTR_ERR(ch->qp); + PRINT_ERROR("failed to create queue pair (%d)", ret); + } } + if (ret) + goto err_destroy_cq; TRACE_DBG("qp_num = %#x", ch->qp->qp_num); atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr); - TRACE_DBG("%s: max_cqe= %d max_sge= %d sq_size = %d" - " cm_id= %p", __func__, ch->cq->cqe, - qp_init->cap.max_send_sge, qp_init->cap.max_send_wr, - ch->cm_id); - - ret = srpt_init_ch_qp(ch, ch->qp); - if (ret) { - PRINT_ERROR("srpt_init_ch_qp(%#x) failed (%d)", ch->qp->qp_num, - ret); - goto err_destroy_qp; - } + TRACE_DBG("%s: max_cqe= %d max_sge= %d sq_size = %d ch= %p", __func__, + ch->cq->cqe, qp_init->cap.max_send_sge, + qp_init->cap.max_send_wr, ch); out: kfree(qp_init); return ret; -err_destroy_qp: - ib_destroy_qp(ch->qp); err_destroy_cq: + ch->qp = NULL; ib_destroy_cq(ch->cq); goto out; } @@ -2186,8 +2175,23 @@ static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) ib_destroy_cq(ch->cq); } +static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) +{ + int ret; + + if (ch->using_rdma_cm) { + ret = rdma_disconnect(ch->rdma_cm.cm_id); + } else { + ret = ib_send_cm_dreq(ch->ib_cm.cm_id, NULL, 0); + if (ret < 0) + ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0); + } + + return ret; +} + /** - * __srpt_close_ch() - Close an RDMA channel. + * srpt_close_ch() - Close an RDMA channel. * * Make sure all resources associated with the channel will be deallocated at * an appropriate time. @@ -2195,22 +2199,14 @@ static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) * Returns true if and only if the channel state has been modified from * CH_CONNECTING or CH_LIVE into CH_DISCONNECTING. */ -static bool __srpt_close_ch(struct srpt_rdma_ch *ch) - __releases(&ch->srpt_tgt->spinlock) - __acquires(&ch->srpt_tgt->spinlock) +static bool srpt_close_ch(struct srpt_rdma_ch *ch) { - struct srpt_tgt *srpt_tgt = ch->srpt_tgt; int ret; bool was_live; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - lockdep_assert_held(&srpt_tgt->spinlock); -#endif - was_live = srpt_set_ch_state(ch, CH_DISCONNECTING); if (was_live) { kref_get(&ch->kref); - spin_unlock_irq(&srpt_tgt->spinlock); ret = srpt_ch_qp_err(ch); if (ret < 0) @@ -2225,43 +2221,27 @@ static bool __srpt_close_ch(struct srpt_rdma_ch *ch) } kref_put(&ch->kref, srpt_free_ch); - - spin_lock_irq(&srpt_tgt->spinlock); } return was_live; } -/** - * srpt_close_ch() - Close an RDMA channel. - */ -static void srpt_close_ch(struct srpt_rdma_ch *ch) -{ - struct srpt_tgt *srpt_tgt = ch->srpt_tgt; - - spin_lock_irq(&srpt_tgt->spinlock); - __srpt_close_ch(ch); - spin_unlock_irq(&srpt_tgt->spinlock); -} - static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) { struct srpt_nexus *nexus; struct srpt_rdma_ch *ch; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - lockdep_assert_held(&srpt_tgt->spinlock); + lockdep_assert_held(&srpt_tgt->mutex); #endif -restart: list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { list_for_each_entry(ch, &nexus->ch_list, list) { - if (ib_send_cm_dreq(ch->cm_id, NULL, 0) < 0) + if (srpt_disconnect_ch(ch) < 0) continue; PRINT_INFO("Closing channel %s because target %s has" " been disabled", ch->sess_name, srpt_tgt->scst_tgt->tgt_name); - goto restart; } } } @@ -2287,13 +2267,13 @@ static struct srpt_tgt *srpt_convert_scst_tgt(struct scst_tgt *scst_tgt) * it does not yet exist. */ static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, - u8 i_port_id[16], u8 t_port_id[16]) + const u8 i_port_id[16], + const u8 t_port_id[16]) { - unsigned long flags; struct srpt_nexus *nexus = NULL, *tmp_nexus = NULL, *n; for (;;) { - spin_lock_irqsave(&srpt_tgt->spinlock, flags); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry(n, &srpt_tgt->nexus_list, entry) { if (memcmp(n->i_port_id, i_port_id, 16) == 0 && memcmp(n->t_port_id, t_port_id, 16) == 0) { @@ -2305,7 +2285,7 @@ static struct srpt_nexus *srpt_get_nexus(struct srpt_tgt *srpt_tgt, list_add_tail(&tmp_nexus->entry, &srpt_tgt->nexus_list); swap(nexus, tmp_nexus); } - spin_unlock_irqrestore(&srpt_tgt->spinlock, flags); + mutex_unlock(&srpt_tgt->mutex); if (nexus) break; @@ -2341,11 +2321,11 @@ static int srpt_enable_target(struct scst_tgt *scst_tgt, bool enable) PRINT_INFO("%s target %s", enable ? "Enabling" : "Disabling", scst_tgt->tgt_name); - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); srpt_tgt->enabled = enable; if (!enable) __srpt_close_all_ch(srpt_tgt); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); res = 0; @@ -2370,19 +2350,23 @@ static bool srpt_is_target_enabled(struct scst_tgt *scst_tgt) * Ownership of the cm_id is transferred to the SCST session if this function * returns zero. Otherwise the caller remains the owner of cm_id. */ -static int srpt_cm_req_recv(struct ib_cm_id *cm_id, - struct ib_cm_req_event_param *param, - void *private_data) +static int srpt_cm_req_recv(struct srpt_device *const sdev, + struct ib_cm_id *ib_cm_id, + struct rdma_cm_id *rdma_cm_id, + u8 port_num, __be16 pkey, + const struct srp_login_req *req) { - struct srpt_device *const sdev = cm_id->context; - struct srpt_port *const sport = &sdev->port[param->port - 1]; + struct srpt_port *const sport = &sdev->port[port_num - 1]; + const __be16 *const raw_port_gid = (__be16 *)sport->gid.raw; struct srpt_tgt *const srpt_tgt = one_target_per_port ? &sport->srpt_tgt : &sdev->srpt_tgt; struct srpt_nexus *nexus; - struct srp_login_req *req; struct srp_login_rsp *rsp = NULL; struct srp_login_rej *rej = NULL; - struct ib_cm_rep_param *rep_param = NULL; + union { + struct rdma_conn_param rdma_cm; + struct ib_cm_rep_param ib_cm; + } *rep_param = NULL; struct srpt_rdma_ch *ch = NULL; struct task_struct *thread; u32 it_iu_len; @@ -2392,16 +2376,14 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, EXTRACHECKS_WARN_ON_ONCE(irqs_disabled()); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) - WARN_ON(!sdev || !private_data); - if (!sdev || !private_data) + WARN_ON(!sdev || !req); + if (!sdev || !req) return -EINVAL; #else - if (WARN_ON(!sdev || !private_data)) + if (WARN_ON(!sdev || !req)) return -EINVAL; #endif - req = (struct srp_login_req *)private_data; - it_iu_len = be32_to_cpu(req->req_it_iu_len); PRINT_INFO("Received SRP_LOGIN_REQ with i_port_id" @@ -2426,15 +2408,15 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, be16_to_cpu(*(__be16 *)&req->target_port_id[12]), be16_to_cpu(*(__be16 *)&req->target_port_id[14]), it_iu_len, - param->port, - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[0]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[2]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[4]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[6]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[8]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[10]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[12]), - be16_to_cpu(*(__be16 *)&sdev->port[param->port - 1].gid.raw[14])); + port_num, + be16_to_cpu(raw_port_gid[0]), + be16_to_cpu(raw_port_gid[1]), + be16_to_cpu(raw_port_gid[2]), + be16_to_cpu(raw_port_gid[3]), + be16_to_cpu(raw_port_gid[4]), + be16_to_cpu(raw_port_gid[5]), + be16_to_cpu(raw_port_gid[6]), + be16_to_cpu(raw_port_gid[7])); nexus = srpt_get_nexus(srpt_tgt, req->initiator_port_id, req->target_port_id); @@ -2487,19 +2469,18 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, } kref_init(&ch->kref); - ret = ib_find_pkey(sdev->device, sport->port, - be16_to_cpu(param->primary_path->pkey), - &ch->pkey_index); - if (ret < 0) { - ch->pkey_index = 0; - PRINT_ERROR("Translating pkey %#x failed (%d) - using index 0", - be16_to_cpu(param->primary_path->pkey), ret); - } + ch->pkey = be16_to_cpu(pkey); ch->nexus = nexus; ch->sport = sport; ch->srpt_tgt = srpt_tgt; - ch->cm_id = cm_id; - cm_id->context = ch; + if (ib_cm_id) { + ch->ib_cm.cm_id = ib_cm_id; + ib_cm_id->context = ch; + } else { + ch->using_rdma_cm = true; + ch->rdma_cm.cm_id = rdma_cm_id; + rdma_cm_id->context = ch; + } /* * Avoid QUEUE_FULL conditions by limiting the number of buffers used * for the SRP protocol to the SCST SCSI command queue size. @@ -2533,18 +2514,16 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, } if (one_target_per_port) { - __be16 *const raw_gid = (__be16 *)param->primary_path->dgid.raw; - snprintf(ch->sess_name, sizeof(ch->sess_name), "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", - be16_to_cpu(raw_gid[0]), - be16_to_cpu(raw_gid[1]), - be16_to_cpu(raw_gid[2]), - be16_to_cpu(raw_gid[3]), - be16_to_cpu(raw_gid[4]), - be16_to_cpu(raw_gid[5]), - be16_to_cpu(raw_gid[6]), - be16_to_cpu(raw_gid[7])); + be16_to_cpu(raw_port_gid[0]), + be16_to_cpu(raw_port_gid[1]), + be16_to_cpu(raw_port_gid[2]), + be16_to_cpu(raw_port_gid[3]), + be16_to_cpu(raw_port_gid[4]), + be16_to_cpu(raw_port_gid[5]), + be16_to_cpu(raw_port_gid[6]), + be16_to_cpu(raw_port_gid[7])); } else if (use_port_guid_in_session_name) { /* * If the kernel module parameter use_port_guid_in_session_name @@ -2556,7 +2535,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, snprintf(ch->sess_name, sizeof(ch->sess_name), "0x%016llx%016llx", be64_to_cpu(*(__be64 *) - &sdev->port[param->port - 1].gid.raw[8]), + &sdev->port[port_num - 1].gid.raw[8]), be64_to_cpu(*(__be64 *)(nexus->i_port_id + 8))); } else { /* @@ -2590,20 +2569,18 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, goto unreg_ch; } - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); if ((req->req_flags & SRP_MTCH_ACTION) == SRP_MULTICHAN_SINGLE) { struct srpt_rdma_ch *ch2; rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_NO_CHAN; -restart: list_for_each_entry(ch2, &nexus->ch_list, list) { - if (ib_send_cm_dreq(ch2->cm_id, NULL, 0) < 0) + if (srpt_disconnect_ch(ch2) < 0) continue; PRINT_INFO("Relogin - closed existing channel %s", ch2->sess_name); rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_TERMINATED; - goto restart; } } else { rsp->rsp_flags = SRP_LOGIN_RSP_MULTICHAN_MAINTAINED; @@ -2618,13 +2595,13 @@ restart: PRINT_INFO("rejected SRP_LOGIN_REQ because the target %s (%s)" " is not enabled", srpt_tgt->scst_tgt->tgt_name, sdev->device->name); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); goto reject; } - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); - ret = srpt_ch_qp_rtr(ch, ch->qp); + ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rtr(ch, ch->qp); if (ret) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); PRINT_ERROR("rejected SRP_LOGIN_REQ because enabling" @@ -2632,8 +2609,8 @@ restart: goto reject; } - TRACE_DBG("Establish connection sess=%p name=%s cm_id=%p", - ch->scst_sess, ch->sess_name, ch->cm_id); + TRACE_DBG("Establish connection sess=%p name=%s ch=%p", + ch->scst_sess, ch->sess_name, ch); /* create srp_login_response */ rsp->opcode = SRP_LOGIN_RSP; @@ -2648,27 +2625,34 @@ restart: ch->req_lim_delta = 0; /* create cm reply */ - rep_param->qp_num = ch->qp->qp_num; - rep_param->private_data = (void *)rsp; - rep_param->private_data_len = sizeof(*rsp); - rep_param->rnr_retry_count = 7; - rep_param->flow_control = 1; - rep_param->failover_accepted = 0; - rep_param->srq = 1; - rep_param->responder_resources = 4; - rep_param->initiator_depth = 4; + if (ch->using_rdma_cm) { + rep_param->rdma_cm.private_data = (void *)rsp; + rep_param->rdma_cm.private_data_len = sizeof(*rsp); + rep_param->rdma_cm.rnr_retry_count = 7; + rep_param->rdma_cm.flow_control = 1; + rep_param->rdma_cm.responder_resources = 4; + rep_param->rdma_cm.initiator_depth = 4; + } else { + rep_param->ib_cm.qp_num = ch->qp->qp_num; + rep_param->ib_cm.private_data = (void *)rsp; + rep_param->ib_cm.private_data_len = sizeof(*rsp); + rep_param->ib_cm.rnr_retry_count = 7; + rep_param->ib_cm.flow_control = 1; + rep_param->ib_cm.failover_accepted = 0; + rep_param->ib_cm.srq = 1; + rep_param->ib_cm.responder_resources = 4; + rep_param->ib_cm.initiator_depth = 4; + } - spin_lock_irq(&srpt_tgt->spinlock); - if (ch->state == CH_CONNECTING) - ret = ib_send_cm_rep(cm_id, rep_param); + if (ch->using_rdma_cm) + ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); else - ret = -ECONNABORTED; - spin_unlock_irq(&srpt_tgt->spinlock); + ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); switch (ret) { case 0: break; - case -ECONNABORTED: + case -EINVAL: goto reject; default: rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); @@ -2691,7 +2675,10 @@ free_ring: ch->max_rsp_size, DMA_TO_DEVICE); free_ch: - cm_id->context = NULL; + if (rdma_cm_id) + rdma_cm_id->context = NULL; + else + ib_cm_id->context = NULL; kfree(ch); ch = NULL; @@ -2703,8 +2690,11 @@ reject: rej->tag = req->tag; rej->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); - ib_send_cm_rej(cm_id, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0, rej, - sizeof(*rej)); + if (rdma_cm_id) + rdma_reject(rdma_cm_id, rej, sizeof(*rej)); + else + ib_send_cm_rej(ib_cm_id, IB_CM_REJ_CONSUMER_DEFINED, NULL, 0, + rej, sizeof(*rej)); if (ch && ch->thread) { srpt_close_ch(ch); @@ -2723,28 +2713,106 @@ out: return ret; } +static int srpt_ib_cm_req_recv(struct ib_cm_id *cm_id, + struct ib_cm_req_event_param *param, + void *private_data) +{ + return srpt_cm_req_recv(cm_id->context, cm_id, NULL, param->port, + param->primary_path->pkey, + private_data); +} + +static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct srpt_device *sdev; + struct srp_login_req req; + const struct srp_login_req_rdma *req_rdma; + + sdev = ib_get_client_data(cm_id->device, &srpt_client); + if (!sdev) + return -ECONNREFUSED; + + if (event->param.conn.private_data_len < sizeof(*req_rdma)) + return -EINVAL; + + /* Transform srp_login_req_rdma into srp_login_req. */ + req_rdma = event->param.conn.private_data; + memset(&req, 0, sizeof(req)); + req.opcode = req_rdma->opcode; + req.tag = req_rdma->tag; + req.req_it_iu_len = req_rdma->req_it_iu_len; + req.req_buf_fmt = req_rdma->req_buf_fmt; + req.req_flags = req_rdma->req_flags; + memcpy(req.initiator_port_id, req_rdma->initiator_port_id, 16); + memcpy(req.target_port_id, req_rdma->target_port_id, 16); + + return srpt_cm_req_recv(sdev, NULL, cm_id, cm_id->port_num, + cm_id->route.path_rec->pkey, &req); +} + static void srpt_cm_rej_recv(struct ib_cm_id *cm_id) { PRINT_INFO("Received InfiniBand REJ packet for cm_id %p.", cm_id); } +static void srpt_check_timeout(struct srpt_rdma_ch *ch) +{ + struct ib_qp_attr attr; + struct ib_qp_init_attr iattr; + uint64_t T_tr_ns, max_compl_time_ms; + uint32_t T_tr_ms; + + if (ib_query_qp(ch->qp, &attr, IB_QP_TIMEOUT, &iattr) < 0) { + PRINT_ERROR("Querying QP attributes failed"); + return; + } + + /* + * From IBTA C9-140: Transport Timer timeout interval + * T_tr = 4.096 us * 2**(local ACK timeout) where the local ACK timeout + * is a five-bit value, with zero meaning that the timer is disabled. + */ + WARN_ON(attr.timeout >= (1 << 5)); + if (attr.timeout) { + T_tr_ns = 1ULL << (12 + attr.timeout); + max_compl_time_ms = attr.retry_cnt * 4 * T_tr_ns; + do_div(max_compl_time_ms, 1000000); + T_tr_ms = T_tr_ns; + do_div(T_tr_ms, 1000000); + TRACE_DBG("Session %s: QP local ack timeout = %d or T_tr =" + " %u ms; retry_cnt = %d; max compl. time = %d ms", + ch->sess_name, attr.timeout, T_tr_ms, + attr.retry_cnt, (unsigned)max_compl_time_ms); + + if (max_compl_time_ms >= RDMA_COMPL_TIMEOUT_S * 1000) { + PRINT_ERROR("Maximum RDMA completion time (%d ms)" + " exceeds ib_srpt timeout (%d ms)", + (unsigned)max_compl_time_ms, + 1000 * RDMA_COMPL_TIMEOUT_S); + } + } +} + /** - * srpt_cm_rtu_recv() - Process IB CM RTU_RECEIVED and USER_ESTABLISHED events. + * srpt_cm_rtu_recv() - Process RTU event. * - * An IB_CM_RTU_RECEIVED message indicates that the connection is established - * and that the recipient may begin transmitting (RTU = ready to use). + * An RTU (read to use) message indicates that the connection has been + * established and that the recipient may begin transmitting. */ -static void srpt_cm_rtu_recv(struct ib_cm_id *cm_id) +static void srpt_cm_rtu_recv(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; int ret; - ret = srpt_ch_qp_rts(ch, ch->qp); + ret = ch->using_rdma_cm ? 0 : srpt_ch_qp_rts(ch, ch->qp); if (ret < 0) { PRINT_ERROR("%s: QP transition to RTS failed", ch->sess_name); srpt_close_ch(ch); return; } + + srpt_check_timeout(ch); + /* * Note: calling srpt_close_ch() if the transition to the LIVE state * fails is not necessary since that means that that function has @@ -2755,11 +2823,9 @@ static void srpt_cm_rtu_recv(struct ib_cm_id *cm_id) ch->sess_name); } -static void srpt_cm_timewait_exit(struct ib_cm_id *cm_id) +static void srpt_cm_timewait_exit(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; - - PRINT_INFO("Received InfiniBand TimeWait exit for cm_id %p.", cm_id); + PRINT_INFO("Received InfiniBand TimeWait exit for ch %p.", ch); srpt_close_ch(ch); } @@ -2771,28 +2837,18 @@ static void srpt_cm_rep_error(struct ib_cm_id *cm_id) /** * srpt_cm_dreq_recv() - Process reception of a DREQ message. */ -static int srpt_cm_dreq_recv(struct ib_cm_id *cm_id) +static int srpt_cm_dreq_recv(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; - int ret; - - ret = ib_send_cm_drep(cm_id, NULL, 0); - if (ret < 0) - PRINT_ERROR("%s: sending DREP failed", ch->sess_name); - - srpt_close_ch(ch); - - return ret; + srpt_disconnect_ch(ch); + return 0; } /** * srpt_cm_drep_recv() - Process reception of a DREP message. */ -static void srpt_cm_drep_recv(struct ib_cm_id *cm_id) +static void srpt_cm_drep_recv(struct srpt_rdma_ch *ch) { - struct srpt_rdma_ch *ch = cm_id->context; - - PRINT_INFO("Received InfiniBand DREP message for cm_id %p.", cm_id); + PRINT_INFO("Received InfiniBand DREP message for ch %p.", ch); srpt_close_ch(ch); } @@ -2815,24 +2871,24 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) ret = 0; switch (event->event) { case IB_CM_REQ_RECEIVED: - ret = srpt_cm_req_recv(cm_id, &event->param.req_rcvd, - event->private_data); + ret = srpt_ib_cm_req_recv(cm_id, &event->param.req_rcvd, + event->private_data); break; case IB_CM_REJ_RECEIVED: srpt_cm_rej_recv(cm_id); break; case IB_CM_RTU_RECEIVED: case IB_CM_USER_ESTABLISHED: - srpt_cm_rtu_recv(cm_id); + srpt_cm_rtu_recv((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_DREQ_RECEIVED: - ret = srpt_cm_dreq_recv(cm_id); + ret = srpt_cm_dreq_recv((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_DREP_RECEIVED: - srpt_cm_drep_recv(cm_id); + srpt_cm_drep_recv((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_TIMEWAIT_EXIT: - srpt_cm_timewait_exit(cm_id); + srpt_cm_timewait_exit((struct srpt_rdma_ch *)cm_id->context); break; case IB_CM_REP_ERROR: srpt_cm_rep_error(cm_id); @@ -2852,6 +2908,37 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) return ret; } +static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + int ret = 0; + + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: + ret = srpt_rdma_cm_req_recv(cm_id, event); + break; + case RDMA_CM_EVENT_ESTABLISHED: + srpt_cm_rtu_recv(cm_id->context); + break; + case RDMA_CM_EVENT_DISCONNECTED: + srpt_cm_dreq_recv(cm_id->context); + break; + case RDMA_CM_EVENT_TIMEWAIT_EXIT: + srpt_cm_timewait_exit(cm_id->context); + break; + case RDMA_CM_EVENT_DEVICE_REMOVAL: + break; + case RDMA_CM_EVENT_ADDR_CHANGE: + break; + default: + PRINT_ERROR("received unrecognized RDMA CM event %d", + event->event); + break; + } + + return ret; +} + /** * srpt_map_sg_to_ib_sge() - Map an SG list to an IB SGE list. */ @@ -3493,7 +3580,8 @@ static int srpt_close_session(struct scst_session *sess) { struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(sess); - ib_send_cm_dreq(ch->cm_id, NULL, 0); + srpt_disconnect_ch(ch); + return 0; } @@ -3502,11 +3590,11 @@ static bool srpt_ch_list_empty(struct srpt_tgt *srpt_tgt) struct srpt_nexus *nexus; bool res = true; - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) if (!list_empty(&nexus->ch_list)) res = false; - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); return res; } @@ -3525,16 +3613,16 @@ static int srpt_release_sport(struct srpt_tgt *srpt_tgt) BUG_ON(!srpt_tgt); /* Disallow new logins and close all active sessions. */ - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); srpt_tgt->enabled = false; __srpt_close_all_ch(srpt_tgt); - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); while (wait_event_timeout(srpt_tgt->ch_releaseQ, srpt_ch_list_empty(srpt_tgt), 5 * HZ) <= 0) { PRINT_INFO("%s: waiting for session unregistration ...", srpt_tgt->scst_tgt->tgt_name); - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { list_for_each_entry(ch, &nexus->ch_list, list) { PRINT_INFO("%s: state %s; %d commands in" @@ -3543,15 +3631,15 @@ static int srpt_release_sport(struct srpt_tgt *srpt_tgt) atomic_read(&ch->scst_sess->sess_cmd_count)); } } - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); } - spin_lock_irq(&srpt_tgt->spinlock); + mutex_lock(&srpt_tgt->mutex); list_for_each_entry_safe(nexus, next_n, &srpt_tgt->nexus_list, entry) { list_del(&nexus->entry); kfree(nexus); } - spin_unlock_irq(&srpt_tgt->spinlock); + mutex_unlock(&srpt_tgt->mutex); TRACE_EXIT(); return 0; @@ -3799,7 +3887,7 @@ static void srpt_init_tgt(struct srpt_tgt *srpt_tgt) { INIT_LIST_HEAD(&srpt_tgt->nexus_list); init_waitqueue_head(&srpt_tgt->ch_releaseQ); - spin_lock_init(&srpt_tgt->spinlock); + mutex_init(&srpt_tgt->mutex); } /** @@ -3807,6 +3895,7 @@ static void srpt_init_tgt(struct srpt_tgt *srpt_tgt) */ static void srpt_add_one(struct ib_device *device) { + struct ib_cm_id *cm_id; struct srpt_device *sdev; struct srpt_port *sport; struct srpt_tgt *srpt_tgt; @@ -3894,12 +3983,12 @@ static void srpt_add_one(struct ib_device *device) srpt_service_guid = be64_to_cpu(device->node_guid) & ~be64_to_cpu(IB_SERVICE_ID_AGN_MASK); - sdev->cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev); - if (IS_ERR(sdev->cm_id)) { - PRINT_ERROR("ib_create_cm_id() failed: %ld", - PTR_ERR(sdev->cm_id)); + cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev); + if (IS_ERR(cm_id)) { + PRINT_ERROR("ib_create_cm_id() failed: %ld", PTR_ERR(cm_id)); goto err_srq; } + sdev->cm_id = cm_id; /* print out target login information */ TRACE_DBG("Target login info: id_ext=%016llx," @@ -4033,10 +4122,13 @@ static void srpt_remove_one(struct ib_device *device) ib_destroy_cm_id(sdev->cm_id); + ib_set_client_data(device, &srpt_client, NULL); + /* - * SCST target unregistration must happen after destroying sdev->cm_id - * such that no new SRP_LOGIN_REQ information units can arrive while - * unregistering the SCST target. + * SCST target unregistration must happen after sdev->cm_id has been + * destroyed and after the client data has been reset such that no new + * SRP_LOGIN_REQ information units can arrive while unregistering the + * SCST target. */ if (one_target_per_port) { for (i = 0; i < sdev->device->phys_port_cnt; i++) { @@ -4188,20 +4280,55 @@ static int __init srpt_init_module(void) goto out_unregister_target; } + if (rdma_cm_port) { + struct sockaddr_in addr; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + rdma_cm_id = rdma_create_id(srpt_rdma_cm_handler, NULL, + RDMA_PS_TCP, IB_QPT_RC); +#else + rdma_cm_id = rdma_create_id(srpt_rdma_cm_handler, NULL, + RDMA_PS_TCP); +#endif + if (IS_ERR(rdma_cm_id)) { + rdma_cm_id = NULL; + PRINT_ERROR("RDMA/CM ID creation failed"); + goto out_unregister_client; + } + + /* We will listen on any RDMA device. */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = cpu_to_be16(rdma_cm_port); + if (rdma_bind_addr(rdma_cm_id, (void *)&addr)) { + PRINT_ERROR("Binding RDMA/CM ID to port %u failed\n", + rdma_cm_port); + goto out_unregister_client; + } + + if (rdma_listen(rdma_cm_id, 128)) { + PRINT_ERROR("rdma_listen() failed"); + goto out_unregister_client; + } + } + #ifdef CONFIG_SCST_PROC ret = srpt_register_procfs_entry(&srpt_template); if (ret) { PRINT_ERROR("couldn't register procfs entry"); - goto out_unregister_client; + goto out_rdma_cm; } #endif /*CONFIG_SCST_PROC*/ return 0; #ifdef CONFIG_SCST_PROC +out_rdma_cm: + rdma_destroy_id(rdma_cm_id); +#endif /*CONFIG_SCST_PROC*/ out_unregister_client: ib_unregister_client(&srpt_client); -#endif /*CONFIG_SCST_PROC*/ out_unregister_target: scst_unregister_target_template(&srpt_template); out: @@ -4212,6 +4339,7 @@ static void __exit srpt_cleanup_module(void) { TRACE_ENTRY(); + rdma_destroy_id(rdma_cm_id); ib_unregister_client(&srpt_client); #ifdef CONFIG_SCST_PROC srpt_unregister_procfs_entry(&srpt_template); diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 621ca3926..e9b2b4913 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #if defined(INSIDE_KERNEL_TREE) #include @@ -327,15 +328,22 @@ enum rdma_ch_state { * @cmd_wait_list: list of SCST commands that arrived before the RTU event. This * list contains struct srpt_ioctx elements and is protected * against concurrent modification by the cm_id spinlock. - * @pkey_index: P_Key index of the IB partition for this SRP channel. + * @pkey: P_Key of the IB partition for this SRP channel. * @scst_sess: SCST session information associated with this SRP channel. * @sess_name: SCST session name. */ struct srpt_rdma_ch { struct task_struct *thread; struct srpt_nexus *nexus; - struct ib_cm_id *cm_id; struct ib_qp *qp; + union { + struct { + struct ib_cm_id *cm_id; + } ib_cm; + struct { + struct rdma_cm_id *cm_id; + } rdma_cm; + }; struct ib_cq *cq; struct kref kref; int rq_size; @@ -354,7 +362,8 @@ struct srpt_rdma_ch { enum rdma_ch_state state; struct list_head list; struct list_head cmd_wait_list; - uint16_t pkey_index; + uint16_t pkey; + bool using_rdma_cm; bool processing_wait_list; struct scst_session *scst_sess; @@ -364,7 +373,7 @@ struct srpt_rdma_ch { /** * struct srpt_nexus - I_T nexus * @entry: srpt_tgt.nexus_list list node. - * @ch_list: struct srpt_rdma_ch list. Protected by srpt_tgt.spinlock + * @ch_list: struct srpt_rdma_ch list. Protected by srpt_tgt.mutex. * @i_port_id: 128-bit initiator port identifier copied from SRP_LOGIN_REQ. * @t_port_id: 128-bit target port identifier copied from SRP_LOGIN_REQ. */ @@ -378,14 +387,14 @@ struct srpt_nexus { /** * struct srpt_tgt * @ch_releaseQ: Enables waiting for removal from nexus_list. - * @spinlock: Protects nexus_list. + * @mutex: Protects @nexus_list and srpt_nexus.ch_list. * @nexus_list: Per-device I_T nexus list. * @scst_tgt: SCST target information associated with this HCA. * @enabled: Whether or not this SCST target is enabled. */ struct srpt_tgt { wait_queue_head_t ch_releaseQ; - spinlock_t spinlock; + struct mutex mutex; struct list_head nexus_list; struct scst_tgt *scst_tgt; bool enabled; @@ -444,6 +453,25 @@ struct srpt_device { struct srpt_tgt srpt_tgt; }; +/** + * struct srp_login_req_rdma - RDMA/CM login parameters. + * + * RDMA/CM over InfiniBand can only carry 92 - 36 = 56 bytes of private + * data. srp_login_req_rdma contains the same information as + * struct srp_login_req but with the reserved data removed. + * + * To do: Move this structure to . + */ +struct srp_login_req_rdma { + u64 tag; + __be16 req_buf_fmt; + u8 req_flags; + u8 opcode; + __be32 req_it_iu_len; + u8 initiator_port_id[16]; + u8 target_port_id[16]; +}; + #endif /* IB_SRPT_H */ /* diff --git a/usr/fileio/common.c b/usr/fileio/common.c index 96a674144..c756e7736 100644 --- a/usr/fileio/common.c +++ b/usr/fileio/common.c @@ -335,6 +335,7 @@ static int do_exec(struct vdisk_cmd *vcmd) } break; case SYNCHRONIZE_CACHE: + case SYNCHRONIZE_CACHE_16: { int immed = cdb[1] & 0x2; if (data_len == 0) From 23250756c2bbab4121c11fe809990373650c4ccf Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 28 Apr 2014 12:22:13 +0000 Subject: [PATCH 044/128] isert: Handle TargetRecvDataSegmentLength in RFC compliant way iSER RFC defines TargetRecvDataSegmentLength to be mandatory. Also, according to the RFC, the target must be prepared to receive at least 512 bytes, so fix that. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5486 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/TODO | 2 -- iscsi-scst/kernel/isert-scst/iser_pdu.c | 2 +- iscsi-scst/usr/iscsid.c | 9 +++++++++ iscsi-scst/usr/param.c | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/TODO b/iscsi-scst/kernel/isert-scst/TODO index 2bd1eca90..479c6b76b 100644 --- a/iscsi-scst/kernel/isert-scst/TODO +++ b/iscsi-scst/kernel/isert-scst/TODO @@ -1,5 +1,3 @@ -* In login, handle declarative statements correctly: - use text_key_add() to add target declarative keys. * Add suppport for immediate data in iSER * Add suppport for data-out in iSER * Look into allocating wr and sg entries dynamically from kmem_cache instead of embedding them into iser_cmnd diff --git a/iscsi-scst/kernel/isert-scst/iser_pdu.c b/iscsi-scst/kernel/isert-scst/iser_pdu.c index 7d151db26..eeecc2f2e 100644 --- a/iscsi-scst/kernel/isert-scst/iser_pdu.c +++ b/iscsi-scst/kernel/isert-scst/iser_pdu.c @@ -371,7 +371,7 @@ static inline void isert_link_recv_pdu_wrs(struct isert_cmnd *from_pdu, int isert_alloc_conn_resources(struct isert_connection *isert_conn) { struct isert_cmnd *pdu, *prev_pdu = NULL, *first_pdu = NULL; - int t_datasz = ISER_HDRS_SZ; + int t_datasz = 512; /* RFC states that minimum receive data size is 512 */ int i_datasz = ISER_HDRS_SZ + SCST_SENSE_BUFFERSIZE; int i, err = 0; int to_alloc; diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index 67accbd64..2e104a756 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -642,6 +642,15 @@ static int login_finish(struct connection *conn) { int res = 0; + if (conn->is_iser && + conn->session_params[key_target_recv_data_length].key_state == KEY_STATE_START) { + char buf[32] = "\0"; + params_val_to_str(session_keys, key_target_recv_data_length, + session_keys[key_target_recv_data_length].local_def, + buf, sizeof(buf)); + text_key_add(conn, "TargetRecvDataSegmentLength", buf); + } + switch (conn->session_type) { case SESSION_NORMAL: if (!conn->sess) diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index c4689ed25..27a3f8ad1 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -383,7 +383,7 @@ struct iscsi_key session_keys[] = { {"OFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops}, {"IFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops}, {"RDMAExtensions", 0, 0, 0, 0, 1, &and_ops}, - {"TargetRecvDataSegmentLength", 8192, -1, 512, -1, 0, &minimum_ops}, + {"TargetRecvDataSegmentLength", 8192, 512, 512, -1, 0, &minimum_ops}, {"InitiatorRecvDataSegmentLength", 8192, -1, 512, -1, 0, &minimum_ops}, {"MaxAHSLength", 256, 0, 0, -1, 0, &minimum_ops}, {"TaggedBufferForSolicitedDataOnly", 0, 0, 0, 0, 0, &and_ops}, From 6a2d41b4802ad58010847f548d5adc7a169c54b7 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 8 May 2014 08:32:01 +0000 Subject: [PATCH 045/128] Merged revisions 5456-5485,5487-5508 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5456 | bvassche | 2014-04-23 08:15:57 +0300 (Wed, 23 Apr 2014) | 1 line scst/README: Update implicit ALUA section ........ r5457 | bvassche | 2014-04-23 11:57:17 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: RHEL 5.9 build fix ........ r5458 | bvassche | 2014-04-23 11:59:18 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: Clean up the CM event handling code ........ r5459 | bvassche | 2014-04-23 12:02:29 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: Clean up the CM event handling code (part 2) ........ r5460 | bvassche | 2014-04-23 12:04:15 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt, RDMA/CM: Avoid hanging sessions due to a cable pull ........ r5461 | bvassche | 2014-04-23 12:20:52 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: RHEL 5.9 build fix (part 2) ........ r5462 | bvassche | 2014-04-23 16:38:54 +0300 (Wed, 23 Apr 2014) | 1 line ib_srpt: Clean up the CM event handling messages (part 3) ........ r5463 | vlnb | 2014-04-24 03:07:22 +0300 (Thu, 24 Apr 2014) | 3 lines Cleanups ........ r5464 | vlnb | 2014-04-24 05:30:52 +0300 (Thu, 24 Apr 2014) | 35 lines scst_lib: Avoid integer overflows This patch fixes the following kernel oops: [3696]: scst: scst_parse_cmd:826:Warning: expected transfer length 522240 for opcode 0x08 (handler vcdrom, target iscsi) doesn't match decoded value -2048 [3696]: scst_parse_cmd:828:Suspicious CDB: (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F 0: 08 1f ff ff ff 00 ...... BUG: unable to handle kernel paging request at ffff88283597f0c8 IP: [] sg_init_table+0x5f/0x90 Call Trace: [] sgv_pool_alloc+0x3b8/0xbf0 [scst] [] scst_alloc_space+0xb6/0x290 [scst] [] scst_prepare_space+0x3b8/0x6e0 [scst] [] scst_process_active_cmd+0x455/0x7e0 [scst] [] scst_cmd_init_done+0x2f2/0x5c0 [scst] [] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst] [] scsi_cmnd_start+0x25a/0x550 [iscsi_scst] [] cmnd_rx_start+0x148/0x1a0 [iscsi_scst] [] process_read_io+0x3b8/0x800 [iscsi_scst] [] scst_do_job_rd+0xc7/0x220 [iscsi_scst] [] istrd+0x16d/0x2e0 [iscsi_scst] [] kthread+0xed/0x110 [] ret_from_fork+0x7c/0xb0 and causes the following message to be reported instead: [11269]: scst: scst_generic_parse:7402:***WARNING***: bufflen 16777215, data_len 16777215 or out_bufflen 0 too large for device disk12 (block size 2048) scst_generic_parse:CDB: (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F 0: 08 1f ff ff ff 00 ...... Signed-off-by: Bart Van Assche ........ r5465 | vlnb | 2014-04-24 05:33:07 +0300 (Thu, 24 Apr 2014) | 5 lines scst_targ: Clarify a comment Signed-off-by: Bart Van Assche ........ r5466 | vlnb | 2014-04-24 05:39:05 +0300 (Thu, 24 Apr 2014) | 12 lines scst: Avoid that smatch complains about dead code The panic() function that implements BUG() has been declared with attribute noreturn in some RHEL 5 kernel headers and also in the RHEL 6 kernel headers. Smatch warns about dead code if any code follows a function that has been declared with attribute noreturn. Hence add a few preprocessor statements to suppress the smatch warning when building against an upstream kernel or when building against RHEL 6 or later. Signed-off-by: Bart Van Assche ........ r5467 | vlnb | 2014-04-24 06:15:12 +0300 (Thu, 24 Apr 2014) | 3 lines Fix queuing of UA for aborted by PREEMPT AND ABORT, if TAS is 0, + some clarifications ........ r5468 | vlnb | 2014-04-24 07:03:51 +0300 (Thu, 24 Apr 2014) | 6 lines Fix for TARGET RESET race It can happen, when a device added after blocking, so unblocking then will make dev->block_count of the new device negative. ........ r5469 | bvassche | 2014-04-24 11:41:15 +0300 (Thu, 24 Apr 2014) | 1 line scst/README: Update multipathd information in implicit ALUA section ........ r5470 | bvassche | 2014-04-24 13:37:58 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: OFED 3.12 build fix ........ r5471 | bvassche | 2014-04-24 13:38:37 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: Add support in the Makefile for MLNX OFED and for OFED 3.x ........ r5472 | bvassche | 2014-04-24 14:11:26 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: Make srpt_disconnect_ch() close sessions properly that have not yet reached the connected state ........ r5473 | bvassche | 2014-04-24 15:27:00 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt, Makefile: Introduce the OFED_KERNEL_DIR variable ........ r5474 | bvassche | 2014-04-24 21:43:41 +0300 (Thu, 24 Apr 2014) | 1 line ib_srpt: Unload properly with RDMA/CM disabled ........ r5475 | bvassche | 2014-04-24 21:45:23 +0300 (Thu, 24 Apr 2014) | 1 line scstadmin, regression tests: Follow-up for r5409 ........ r5476 | bvassche | 2014-04-24 21:49:52 +0300 (Thu, 24 Apr 2014) | 16 lines scst: Avoid that reassigning a session triggers a kernel crash This patch fixes the following kernel bug: BUG: unable to handle kernel NULL pointer dereference at 0000000000000064 IP: [] scst_alloc_add_tgt_dev+0x9c/0x540 [scst] Call Trace: [] scst_check_reassign_sessions+0x367/0x3b0 [scst] [] scst_acg_add_acn+0x117/0x1a0 [scst] [] scst_acg_ini_mgmt_store_work_fn+0x152/0x370 [scst] [] sysfs_work_thread_fn+0xa6/0x2f0 [scst] [] kthread+0xd2/0xf0 [] ret_from_fork+0x7c/0xb0 Reported-by: Zhen Xu ........ r5477 | vlnb | 2014-04-25 02:07:58 +0300 (Fri, 25 Apr 2014) | 3 lines Minor logging cleanup ........ r5478 | vlnb | 2014-04-25 02:44:25 +0300 (Fri, 25 Apr 2014) | 3 lines Cleanup ........ r5479 | vlnb | 2014-04-25 05:03:04 +0300 (Fri, 25 Apr 2014) | 3 lines Saved mode pages added ........ r5480 | vlnb | 2014-04-25 05:47:31 +0300 (Fri, 25 Apr 2014) | 3 lines Follow up for the previous commit ........ r5481 | vlnb | 2014-04-26 04:56:36 +0300 (Sat, 26 Apr 2014) | 3 lines Processing of QErr and TMF_ONLY added ........ r5482 | vlnb | 2014-04-26 05:12:17 +0300 (Sat, 26 Apr 2014) | 3 lines Cleanups ........ r5483 | bvassche | 2014-04-26 09:32:32 +0300 (Sat, 26 Apr 2014) | 7 lines scst: Fix recently introduced checkpatch complaints about whitespace Fix two instances of the following checkpatch errors: ERROR: code indent should use tabs where possible ERROR: spaces required around that ':' (ctx:VxW) ........ r5484 | bvassche | 2014-04-27 09:26:29 +0300 (Sun, 27 Apr 2014) | 6 lines scst_const.h: Fix a checkpatch complaint about whitespace Avoid that checkpatch reports the following warning message: WARNING: please, no space before tabs ........ r5485 | bvassche | 2014-04-28 12:29:25 +0300 (Mon, 28 Apr 2014) | 5 lines scst: Export scst_path_put() This patch makes the code that was added via r5479 build against kernel version 2.6.38 and before. ........ r5487 | vlnb | 2014-04-28 23:31:14 +0300 (Mon, 28 Apr 2014) | 3 lines Cleanups ........ r5488 | vlnb | 2014-04-29 00:38:22 +0300 (Tue, 29 Apr 2014) | 3 lines Returned sense cleanups ........ r5489 | vlnb | 2014-04-29 01:30:03 +0300 (Tue, 29 Apr 2014) | 3 lines Let REPORT SUPPORTED OPERATION CODES be handled by dev handler as well ........ r5490 | vlnb | 2014-04-29 03:13:24 +0300 (Tue, 29 Apr 2014) | 6 lines It's wrong to clean reservation on failed RESERVE commands With multiple outstanding commands it can open a race window leading to loose of a valid reservation ........ r5491 | vlnb | 2014-04-29 04:32:13 +0300 (Tue, 29 Apr 2014) | 3 lines SCSI logging improvements ........ r5492 | vlnb | 2014-04-29 04:45:43 +0300 (Tue, 29 Apr 2014) | 3 lines Missed hunk in the previous commit ........ r5493 | vlnb | 2014-04-29 05:09:29 +0300 (Tue, 29 Apr 2014) | 3 lines Minor logging improvement ........ r5494 | bvassche | 2014-04-29 15:35:41 +0300 (Tue, 29 Apr 2014) | 1 line ib_srpt, README: Fix RDMA/CM login instructions ........ r5495 | bvassche | 2014-04-29 15:39:50 +0300 (Tue, 29 Apr 2014) | 4 lines ib_srpt: Make LUN masking work again This patch fixes a regression that was introduced in r5425. ........ r5496 | vlnb | 2014-04-30 04:04:27 +0300 (Wed, 30 Apr 2014) | 3 lines Extended INQUIRY page added ........ r5497 | vlnb | 2014-04-30 04:07:41 +0300 (Wed, 30 Apr 2014) | 3 lines Cleanup ........ r5498 | bvassche | 2014-04-30 08:30:45 +0300 (Wed, 30 Apr 2014) | 1 line ib_srpt: Fix two recently introduced checkpatch complaints about whitespace ........ r5499 | bvassche | 2014-04-30 09:01:17 +0300 (Wed, 30 Apr 2014) | 1 line nightly build: Update kernel versions ........ r5500 | vlnb | 2014-05-02 05:50:12 +0300 (Fri, 02 May 2014) | 3 lines Fix CDROM empty case ........ r5501 | vlnb | 2014-05-02 05:50:34 +0300 (Fri, 02 May 2014) | 3 lines Cleanups ........ r5502 | vlnb | 2014-05-02 05:56:19 +0300 (Fri, 02 May 2014) | 9 lines scst_sysfs: Save the value of the 'preferred' attribute only if it has been set The default value of the 'preferred' attribute is 0 (disabled). Hence it is only necessary that scstadmin saves the value of that attribute if it is not zero. Signed-off-by: Bart Van Assche ........ r5503 | vlnb | 2014-05-07 02:22:32 +0300 (Wed, 07 May 2014) | 3 lines Cleanups and logging improvements ........ r5504 | vlnb | 2014-05-07 05:13:11 +0300 (Wed, 07 May 2014) | 5 lines Fix COMMAND DATA LENGTH in All_commands parameter data of REPORT SUPPORTED OPERATION CODES Reported by Sebastian Herbszt ........ r5505 | bvassche | 2014-05-07 11:38:47 +0300 (Wed, 07 May 2014) | 5 lines scst: Fix the procfs build Move the definition of scst_get_opcode_name() up such that it occurs outside #ifndef CONFIG_SCST_PROC / #endif. See also r5491. ........ r5506 | bvassche | 2014-05-07 11:57:04 +0300 (Wed, 07 May 2014) | 4 lines scst_vdisk: Build fix for kernel versions < 2.6.37 See also r5420 / r5479. ........ r5507 | bvassche | 2014-05-07 16:42:56 +0300 (Wed, 07 May 2014) | 1 line nightly build: Update kernel versions ........ r5508 | vlnb | 2014-05-08 05:28:49 +0300 (Thu, 08 May 2014) | 7 lines Avoid that the code for dumping the PR state triggers a race condition Callers of scst_pr_dump_prs() must hold dev_pr_mutex. Signed-off-by: Bart Van Assche ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5509 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/iscsi.c | 5 +- nightly/conf/nightly.conf | 6 +- qla2x00t/qla2x00-target/qla2x00t.c | 26 +- scst/README | 184 ++-- scst/README_in-tree | 30 +- scst/include/scst.h | 92 +- scst/include/scst_const.h | 20 +- scst/include/scst_user.h | 2 + scst/src/Makefile | 2 +- scst/src/dev_handlers/Makefile | 1 + scst/src/dev_handlers/scst_disk.c | 5 +- scst/src/dev_handlers/scst_user.c | 58 +- scst/src/dev_handlers/scst_vdisk.c | 764 +++++++++++++-- scst/src/scst_lib.c | 880 ++++++++++++++++-- scst/src/scst_main.c | 4 +- scst/src/scst_pres.c | 59 +- scst/src/scst_priv.h | 7 +- scst/src/scst_proc.c | 7 +- scst/src/scst_sysfs.c | 16 +- scst/src/scst_targ.c | 433 +++++---- .../scst-0.9.10/t/03-targets.t | 16 +- srpt/Makefile | 53 +- srpt/README | 2 +- srpt/src/ib_srpt.c | 206 ++-- srpt/src/ib_srpt.h | 22 +- usr/fileio/fileio.c | 8 +- 26 files changed, 2295 insertions(+), 613 deletions(-) diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index b6e5599e5..bb724fb0b 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -1457,9 +1457,10 @@ static void cmnd_prepare_get_rejected_immed_data(struct iscsi_cmnd *cmnd) TRACE_DBG_FLAG(iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(cmnd), "Skipping (cmnd %p, ITT %x, op %x, cmd op %x, " - "datasize %u, scst_cmd %p, scst state %d)", cmnd, + "datasize %u, scst_cmd %p, scst state %d, status %d)", cmnd, cmnd->pdu.bhs.itt, cmnd_opcode(cmnd), cmnd_hdr(cmnd)->scb[0], - cmnd->pdu.datasize, cmnd->scst_cmd, cmnd->scst_state); + cmnd->pdu.datasize, cmnd->scst_cmd, cmnd->scst_state, + cmnd->scst_cmd ? cmnd->scst_cmd->status : -1); iscsi_extracheck_is_rd_thread(conn); diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index f01180f32..ac7cd2ecc 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,17 +3,17 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.14.1 \ +3.14.3 \ 3.13.10 \ 3.12.17-nc \ 3.11.10-nc \ -3.10.37-nc \ +3.10.39-nc \ 3.9.11-nc \ 3.8.14-nc \ 3.7.10-nc \ 3.6.11-nc \ 3.5.7-nc \ -3.4.87-nc \ +3.4.89-nc \ 3.3.8-nc \ 3.2.57-nc \ 3.1.10-nc \ diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 720daea09..346d6df25 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -2623,16 +2623,16 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, if (unlikely(scst_get_resid(scst_cmd, &prm->residual, NULL))) { if (prm->residual > 0) { TRACE_DBG("Residual underflow: %d (tag %lld, " - "op %x, bufflen %d, rq_result %x)", + "op %s, bufflen %d, rq_result %x)", prm->residual, scst_cmd->tag, - scst_cmd->cdb[0], cmd->bufflen, + scst_get_opcode_name(scst_cmd), cmd->bufflen, prm->rq_result); prm->rq_result |= SS_RESIDUAL_UNDER; } else if (prm->residual < 0) { TRACE_DBG("Residual overflow: %d (tag %lld, " - "op %x, bufflen %d, rq_result %x)", + "op %s, bufflen %d, rq_result %x)", prm->residual, scst_cmd->tag, - scst_cmd->cdb[0], cmd->bufflen, + scst_get_opcode_name(scst_cmd), cmd->bufflen, prm->rq_result); prm->rq_result |= SS_RESIDUAL_OVER; prm->residual = -prm->residual; @@ -3591,18 +3591,18 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, TRACE(TRACE_MINOR_AND_MGMT_DBG, "qla2x00t(%ld): CTIO with " "status %#x received, state %x, scst_cmd %p, " - "op %x (LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " + "op %s (LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " "TIMEOUT=b, INVALID_RX_ID=8)", ha->instance, - status, cmd->state, scst_cmd, scst_cmd->cdb[0]); + status, cmd->state, scst_cmd, scst_get_opcode_name(scst_cmd)); break; case CTIO_PORT_LOGGED_OUT: case CTIO_PORT_UNAVAILABLE: PRINT_INFO("qla2x00t(%ld): CTIO with PORT LOGGED " "OUT (29) or PORT UNAVAILABLE (28) status %x " - "received (state %x, scst_cmd %p, op %x)", + "received (state %x, scst_cmd %p, op %s)", ha->instance, status, cmd->state, scst_cmd, - scst_cmd->cdb[0]); + scst_get_opcode_name(scst_cmd)); break; case CTIO_SRR_RECEIVED: @@ -3613,9 +3613,9 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, default: PRINT_ERROR("qla2x00t(%ld): CTIO with error status " - "0x%x received (state %x, scst_cmd %p, op %x)", + "0x%x received (state %x, scst_cmd %p, op %s)", ha->instance, status, cmd->state, scst_cmd, - scst_cmd->cdb[0]); + scst_get_opcode_name(scst_cmd)); break; } @@ -4652,10 +4652,10 @@ restart: cmd->sg = scst_cmd_get_sg(&cmd->scst_cmd); cmd->sg_cnt = scst_cmd_get_sg_cnt(&cmd->scst_cmd); - TRACE_MGMT_DBG("SRR cmd %p (scst_cmd %p, tag %d, op %x), " + TRACE_MGMT_DBG("SRR cmd %p (scst_cmd %p, tag %d, op %s), " "sg_cnt=%d, offset=%d", cmd, &cmd->scst_cmd, - cmd->tag, cmd->scst_cmd.cdb[0], cmd->sg_cnt, - cmd->offset); + cmd->tag, scst_get_opcode_name(&cmd->scst_cmd), + cmd->sg_cnt, cmd->offset); if (IS_FWI2_CAPABLE(ha)) q24_handle_srr(ha, sctio, imm); diff --git a/scst/README b/scst/README index 2306f1371..ba4c4f5d5 100644 --- a/scst/README +++ b/scst/README @@ -949,6 +949,11 @@ cache. The following parameters possible for vdisk_fileio: initiators can unmap blocks of storage, if they don't need them anymore. Backend storage also must support this facility. + - tst - allows to specify TST control mode page field. It specifies + the type of task set in the device. Possible values are: 0 - the + device maintains one task set for all I_T nexuses and 1 - the device + maintains separate task sets for each I_T nexus. Default - 1. + - removable - with this flag set the device is reported to remote initiators as removable. @@ -965,16 +970,17 @@ storage HBAs and for applications that either do not need caching between application and disk or need the large block throughput. See below for more info. -The following parameters possible for vdisk_blockio: filename, -blocksize, nv_cache, read_only, removable, rotational, thin_provisioned. -See vdisk_fileio above for description of those parameters. +The following common with vdisk_fileio parameters are possible for +vdisk_blockio: filename, blocksize, nv_cache, write_through, read_only, +removable, rotational, thin_provisioned, tst. See vdisk_fileio above for +description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. Intended to be used for performance measurements at the same way as "*_perf" handlers. The following parameters possible for vdisk_nullio: -blocksize, read_only, removable. See vdisk_fileio above for description -of those parameters. +blocksize, read_only, removable, tst. See vdisk_fileio above for +description of those parameters. vdisk_nullio also has extra attribute: @@ -986,7 +992,7 @@ vdisk_nullio also has extra attribute: "dummy" placeholder on LUN 0, if LUN 0 is not desired. Handler vcdrom allows emulation of a virtual CDROM device using an ISO -file as backend. It doesn't have any parameters. +file as backend. It has only single parameter: tst. For example: @@ -1025,6 +1031,9 @@ Each vdisk_fileio's device has the following attributes in SCST device belongs to (in SCSI terminology all SCST devices called Logical Units). See SPC for more info. + - tst - contains TST field of SCSI Control mode page. See SPC-4 for + more details about this field. + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -1087,6 +1096,7 @@ For example: |-- thin_provisioned |-- threads_num |-- threads_pool_type +|-- tst |-- type |-- usn `-- write_through @@ -1094,17 +1104,17 @@ For example: Each vdisk_blockio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, filename, nv_cache, read_only, removable, resync_size, rotational, size_mb, t10_dev_id, -thin_provisioned, threads_num, threads_pool_type, type, usn. See above -description of those parameters. +thin_provisioned, threads_num, threads_pool_type, tst, type, usn. See +above description of those parameters. Each vdisk_nullio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only, removable, size_mb, t10_dev_id, threads_num, threads_pool_type, type, -usn, dummy. See above description of those parameters. +tst, usn, dummy. See above description of those parameters. Each vcdrom's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: filename, size_mb, -t10_dev_id, threads_num, threads_pool_type, type, usn. See above +t10_dev_id, threads_num, threads_pool_type, type, usn, tst. See above description of those parameters. Exception is filename attribute. For vcdrom it is writable. Writing to it allows to virtually insert or change virtual CD media in the virtual CDROM device. For example: @@ -1352,70 +1362,48 @@ The steps involved in configuring ALUA are: As an example, in a H.A. setup with two systems each having one InfiniBand HCA controlled by the ib_srpt driver and where each system exports two LUNs -could be configured as follows: +the following configuration can be used in scst.conf on both systems: -own_tgt_id=1 -other_tgt_id=2 -cd /sys/kernel/scst_tgt/device_groups -echo del dgroup1 >mgmt -echo del dgroup2 >mgmt -echo create dgroup1 >mgmt -echo add disk01 >dgroup1/devices/mgmt -echo create tgroup1 >dgroup1/target_groups/mgmt -echo ${own_tgt_id} >dgroup1/target_groups/tgroup1/group_id -echo add ib_srpt_0 >dgroup1/target_groups/tgroup1/mgmt -echo ${own_tgt_id} >dgroup1/target_groups/tgroup1/ib_srpt_0/rel_tgt_id -if [ ${own_tgt_id} = 1 ]; then - echo 1 >dgroup1/target_groups/tgroup1/preferred -fi -echo create tgroup2 >dgroup1/target_groups/mgmt -echo ${other_tgt_id} >dgroup1/target_groups/tgroup2/group_id -echo add ib_srpt_0-other >dgroup1/target_groups/tgroup2/mgmt -echo ${other_tgt_id} >dgroup1/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id -if [ ${other_tgt_id} = 1 ]; then - echo 1 >dgroup1/target_groups/tgroup2/preferred -fi -echo create dgroup2 >mgmt -echo add disk02 >dgroup2/devices/mgmt -echo create tgroup1 >dgroup2/target_groups/mgmt -echo ${own_tgt_id} >dgroup2/target_groups/tgroup1/group_id -echo add ib_srpt_0 >dgroup2/target_groups/tgroup1/mgmt -echo ${own_tgt_id} >dgroup2/target_groups/tgroup1/ib_srpt_0/rel_tgt_id -if [ ${own_tgt_id} = 2 ]; then - echo 1 >dgroup2/target_groups/tgroup1/preferred -fi -echo create tgroup2 >dgroup2/target_groups/mgmt -echo ${other_tgt_id} >dgroup2/target_groups/tgroup2/group_id -echo add ib_srpt_0-other >dgroup2/target_groups/tgroup2/mgmt -echo ${other_tgt_id} >dgroup2/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id -if [ ${other_tgt_id} = 2 ]; then - echo 1 >dgroup2/target_groups/tgroup2/preferred -fi +DEVICE_GROUP dgroup1 { + DEVICE disk01 -The second system in the same H.A. setup can be configured with the same -commands but with the values of ${own_rel_tgt_id} and ${other_rel_tgt_id} -swapped. + TARGET_GROUP tgroup1 { + group_id 256 + preferred 1 + state active + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7e1 { + rel_tgt_id 1 + } + } + TARGET_GROUP tgroup2 { + group_id 257 + state standby + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7f2 { + rel_tgt_id 2 + } + } +} -The result of the above commands is: +DEVICE_GROUP dgroup2 { + DEVICE disk02 + + TARGET_GROUP tgroup1 { + group_id 256 + state standby + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7e1 { + rel_tgt_id 1 + } + } + TARGET_GROUP tgroup2 { + group_id 257 + preferred 1 + state active + TARGET fe80:0000:0000:0000:0002:c903:00fa:b7f2 { + rel_tgt_id 2 + } + } +} -$ find -type f | grep -v '/mgmt$' | cut -c3- | sort | \ - while read f; do echo $f = $(head -n 1 $f); done -dgroup1/target_groups/tgroup1/group_id = 1 -dgroup1/target_groups/tgroup1/ib_srpt_0/rel_tgt_id = 1 -dgroup1/target_groups/tgroup1/preferred = 1 -dgroup1/target_groups/tgroup1/state = active -dgroup1/target_groups/tgroup2/group_id = 2 -dgroup1/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id = 2 -dgroup1/target_groups/tgroup2/preferred = 0 -dgroup1/target_groups/tgroup2/state = active -dgroup2/target_groups/tgroup1/group_id = 1 -dgroup2/target_groups/tgroup1/ib_srpt_0/rel_tgt_id = 1 -dgroup2/target_groups/tgroup1/preferred = 1 -dgroup2/target_groups/tgroup1/state = active -dgroup2/target_groups/tgroup2/group_id = 2 -dgroup2/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id = 2 -dgroup2/target_groups/tgroup2/preferred = 0 -dgroup2/target_groups/tgroup2/state = active Checking the Target Configuration ................................. @@ -1446,7 +1434,7 @@ configured on the target: # sg_rtpg /dev/sdb Report target port groups: - target port group id : 0x1 , Pref=1 + target port group id : 0x100 , Pref=1 target port group asymmetric access state : 0x00 T_SUP : 0, O_SUP : 0, LBD_SUP : 0, U_SUP : 1, S_SUP : 1, AN_SUP : 1, AO_SUP : 1 status code : 0x02 @@ -1454,7 +1442,7 @@ Report target port groups: target port count : 01 Relative target port ids: 0x01 - target port group id : 0x2 , Pref=0 + target port group id : 0x101 , Pref=0 target port group asymmetric access state : 0x00 T_SUP : 0, O_SUP : 0, LBD_SUP : 0, U_SUP : 1, S_SUP : 1, AN_SUP : 1, AO_SUP : 1 status code : 0x02 @@ -1463,6 +1451,24 @@ Report target port groups: Relative target port ids: 0x02 +The relative target port ID and the target port group ID for a certain path +can be queried e.g. as follows: + +# sg_vpd -p di /dev/sdb +Device Identification VPD page: + Addressed logical unit: + designator type: T10 vendor identification, code set: ASCII + vendor id: SCST_FIO + vendor specific: 27cddc71-disk01 + designator type: EUI-64 based, code set: Binary + 0x3237636464633731 + Target port: + designator type: Relative target port, code set: Binary + Relative target port: 0x1 + designator type: Target port group, code set: Binary + Target port group: 0x100 + + Initiator Support ................. @@ -1472,14 +1478,40 @@ modify at least the following in /etc/multipath.conf to enable implicit ALUA: * hardware_handler "1 alua" * prio alua * path_grouping_policy group_by_prio +* path_checker tur -Note: newer versions of multipathd support a parameter called -"detect_prio". It can be more convenient to enable this parameter instead of -setting the parameter "prio" to "alua" for only those LUNs that support ALUA. +Notes: +- Newer versions of multipathd support a parameter called + "detect_prio". It can be more convenient to enable this parameter instead of + setting the parameter "prio" to "alua" for only those LUNs that support ALUA. +- Older versions of multipathd (e.g. RHEL 5 and SLES 10 SP1) need + 'prio_callout "/sbin/mpath_prio_alua /dev/%n"' instead of 'prio alua'. + +# multipath -ll +23237636464633731 dm-3 SCST_FIO,disk01 +size=1.0G features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw +|-+- policy='service-time 0' prio=1 status=active +| `- 10:0:0:0 sdd 8:48 active ready running +`-+- policy='service-time 0' prio=130 status=enabled + `- 11:0:0:0 sde 8:64 active ready running +23133326137346538 dm-4 SCST_FIO,disk02 +size=1.0G features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw +|-+- policy='service-time 0' prio=130 status=active +| `- 10:0:0:2 sdn 8:208 active ready running +`-+- policy='service-time 0' prio=1 status=enabled + `- 11:0:0:2 sdp 8:240 active ready running + +The following information can be derived from the above output: +* That the hardware handler (hw_handler) has been set to "1 alua". +* That multipathd created two priority groups - one with priority 1 and one + with priority 130. +* That the SRP path with SCSI host number 10 will be used for communication + with LUN "disk01" and that the SRP path with SCSI host number 11 will be used + for communication with LUN "disk02". More information about how to configure the device mapper and the scsi_dh_alua driver can be found in the manual of your Linux distribution ("man -multipath.conf"). +multipath.conf", "man multipath" and "man multipathd"). Windows initiator systems support ALUA from Windows Server 2008 on. For more information about ALUA support in Windows Server, see also: diff --git a/scst/README_in-tree b/scst/README_in-tree index f66ef58e6..739647867 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -807,6 +807,11 @@ cache. The following parameters possible for vdisk_fileio: initiators can unmap blocks of storage, if they don't need them anymore. Backend storage also must support this facility. + - tst - allows to specify TST control mode page field. It specifies + the type of task set in the device. Possible values are: 0 - the + device maintains one task set for all I_T nexuses and 1 - the device + maintains separate task sets for each I_T nexus. Default - 1. + - removable - with this flag set the device is reported to remote initiators as removable. @@ -823,16 +828,17 @@ storage HBAs and for applications that either do not need caching between application and disk or need the large block throughput. See below for more info. -The following parameters possible for vdisk_blockio: filename, -blocksize, nv_cache, read_only, removable, rotational, thin_provisioned. -See vdisk_fileio above for description of those parameters. +The following common with vdisk_fileio parameters are possible for +vdisk_blockio: filename, blocksize, nv_cache, write_through, read_only, +removable, rotational, thin_provisioned, tst. See vdisk_fileio above for +description of those parameters. Handler vdisk_nullio provides NULLIO mode to create virtual devices. In this mode no real I/O is done, but success returned to initiators. Intended to be used for performance measurements at the same way as "*_perf" handlers. The following parameters possible for vdisk_nullio: -blocksize, read_only, removable. See vdisk_fileio above for description -of those parameters. +blocksize, read_only, removable, tst. See vdisk_fileio above for +description of those parameters. vdisk_nullio also has extra attribute: @@ -844,7 +850,7 @@ vdisk_nullio also has extra attribute: "dummy" placeholder on LUN 0, if LUN 0 is not desired. Handler vcdrom allows emulation of a virtual CDROM device using an ISO -file as backend. It doesn't have any parameters. +file as backend. It has only single parameter: tst. For example: @@ -883,6 +889,9 @@ Each vdisk_fileio's device has the following attributes in SCST device belongs to (in SCSI terminology all SCST devices called Logical Units). See SPC for more info. + - tst - contains TST field of SCSI Control mode page. See SPC-4 for + more details about this field. + - thin_provisioned - contains thin provisioning status of this virtual device. @@ -939,6 +948,7 @@ For example: |-- thin_provisioned |-- threads_num |-- threads_pool_type +|-- tst |-- type |-- usn `-- write_through @@ -946,17 +956,17 @@ For example: Each vdisk_blockio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, filename, nv_cache, read_only, removable, resync_size, rotational, size_mb, t10_dev_id, -thin_provisioned, threads_num, threads_pool_type, type, usn. See above -description of those parameters. +thin_provisioned, threads_num, threads_pool_type, tst, type, usn. See +above description of those parameters. Each vdisk_nullio's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only, removable, size_mb, t10_dev_id, threads_num, threads_pool_type, type, -usn, dummy. See above description of those parameters. +tst, usn, dummy. See above description of those parameters. Each vcdrom's device has the following attributes in /sys/kernel/scst_tgt/devices/device_name: filename, size_mb, -t10_dev_id, threads_num, threads_pool_type, type, usn. See above +t10_dev_id, threads_num, threads_pool_type, type, usn, tst. See above description of those parameters. Exception is filename attribute. For vcdrom it is writable. Writing to it allows to virtually insert or change virtual CD media in the virtual CDROM device. For example: diff --git a/scst/include/scst.h b/scst/include/scst.h index 518e434a5..53142c430 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -225,6 +225,8 @@ static inline bool list_entry_in_list(const struct list_head *entry) ** !! as well! *************************************************************/ enum { + /** Active states **/ + /* Dev handler's parse() is going to be called */ SCST_CMD_STATE_PARSE = 0, @@ -258,6 +260,12 @@ enum { /* Checks before target driver's xmit_response() is called */ SCST_CMD_STATE_PRE_XMIT_RESP, + /* Checks 1 before target driver's xmit_response() is called */ + SCST_CMD_STATE_PRE_XMIT_RESP1, + + /* Checks 2 before target driver's xmit_response() is called */ + SCST_CMD_STATE_PRE_XMIT_RESP2, + /* Target driver's xmit_response() is going to be called */ SCST_CMD_STATE_XMIT_RESP, @@ -269,6 +277,8 @@ enum { SCST_CMD_STATE_LAST_ACTIVE = (SCST_CMD_STATE_FINISHED_INTERNAL+100), + /** Passive states **/ + /* A cmd is created, but scst_cmd_init_done() not called */ SCST_CMD_STATE_INIT_WAIT, @@ -568,7 +578,11 @@ enum scst_exec_context { /* Set if the cmd is aborted by other initiator */ #define SCST_CMD_ABORTED_OTHER 1 -/* Set if no response should be sent to the target about this cmd */ +/* + * Set if no response should be sent to the target about this cmd. + * Must be set together with SCST_CMD_ABORTED for better processing + * in scst_pre_xmit_response2(). + */ #define SCST_CMD_NO_RESP 2 /* Set if the cmd is dead and can be destroyed at any time */ @@ -2078,8 +2092,8 @@ struct scst_cmd { unsigned long start_time; - /* List entry for tgt_dev's SN related lists */ - struct list_head sn_cmd_list_entry; + /* List entry for tgt_dev's deferred (SN, etc.) lists */ + struct list_head deferred_cmd_list_entry; /* Cmd's serial number, used to execute cmd's in order of arrival */ unsigned int sn; @@ -2243,6 +2257,10 @@ struct scst_cmd { void *cmd_data_descriptors; int cmd_data_descriptors_cnt; +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + char not_parsed_op_name[8]; +#endif + #ifdef CONFIG_SCST_MEASURE_LATENCY uint64_t start, curr_start, parse_time, alloc_buf_time; uint64_t restart_waiting_time, rdy_to_xfer_time; @@ -2311,6 +2329,7 @@ struct scst_mgmt_cmd { unsigned int needs_unblocking:1; unsigned int lun_set:1; /* set, if lun field is valid */ unsigned int cmd_sn_set:1; /* set, if cmd_sn field is valid */ + unsigned int scst_get_called:1; /* set, if scst_get() was called */ /* Set if dev handler's task_mgmt_fn_received was called */ unsigned int task_mgmt_fn_received_called:1; unsigned int mcmd_dropped:1; /* set if mcmd was dropped */ @@ -2403,20 +2422,51 @@ struct scst_device { /************************************************************* ** Dev's control mode page related values. Updates serialized - ** by scst_block_dev(). Modified independently to the above - ** fields, hence the alignment. + ** by device blocking. Since device blocking protects only + ** commands on the execution stage, in all other read cases + ** use ACCESS_ONCE(), if necessary. Modified independently + ** to the above fields, hence the alignment. *************************************************************/ unsigned int queue_alg:4 __aligned(sizeof(long)); unsigned int tst:3; + unsigned int qerr:2; + unsigned int tmf_only:1; unsigned int tas:1; unsigned int swp:1; unsigned int d_sense:1; + /** + ** Saved and default versions of them, which supported. TST is not + ** among them, because it's hard to switch curr_order_data on the + ** fly. To ensure that no commands lost, we need to flush the previous + ** curr_order_data at first and with one being active command (MODE + ** SELECT), we don't have facility for that at the moment. Suspending + ** activities will hang waiting for the active MODE SELECT. ToDo. + **/ + + unsigned int queue_alg_saved:4; + unsigned int queue_alg_default:4; + + unsigned int tmf_only_saved:1; + unsigned int tmf_only_default:1; + + unsigned int qerr_saved:2; + unsigned int qerr_default:2; + + unsigned int tas_saved:1; + unsigned int tas_default:1; + + unsigned int swp_saved:1; + unsigned int swp_default:1; + + unsigned int d_sense_saved:1; + unsigned int d_sense_default:1; + /* * Set if device implements own ordered commands management. If not set - * and queue_alg is SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER, - * expected_sn will be incremented only after commands finished. + * and queue_alg is SCST_QUEUE_ALG_0_RESTRICTED_REORDER, expected_sn + * will be incremented only after commands finished. */ unsigned int has_own_order_mgmt:1; @@ -2884,7 +2934,6 @@ struct scst_tg_tgt { uint16_t rel_tgt_id; }; - /* * Used to store per-session UNIT ATTENTIONs */ @@ -4260,7 +4309,7 @@ static inline int scst_check_local_events(struct scst_cmd *cmd) return __scst_check_local_events(cmd, true); } -int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd); +int scst_get_cmd_abnormal_done_state(struct scst_cmd *cmd); void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd); struct scst_trace_log { @@ -4559,6 +4608,15 @@ static inline void put_unaligned_be24(const uint32_t v, uint8_t *const p) p[2] = v >> 0; } +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +const char *scst_get_opcode_name(struct scst_cmd *cmd); +#else +static inline const char *scst_get_opcode_name(struct scst_cmd *cmd) +{ + return cmd->op_name; +} +#endif + #ifndef CONFIG_SCST_PROC /* @@ -4593,6 +4651,7 @@ int scst_wait_info_completion(struct scst_sysfs_user_info *info, unsigned int scst_get_setup_id(void); + /* * Needed to avoid potential circular locking dependency between scst_mutex * and internal sysfs locking (s_active). It could be since most sysfs entries @@ -4694,4 +4753,19 @@ void scst_write_same(struct scst_cmd *cmd); __be64 scst_pack_lun(const uint64_t lun, enum scst_lun_addr_method addr_method); uint64_t scst_unpack_lun(const uint8_t *lun, int len); +int scst_save_global_mode_pages(const struct scst_device *dev, + uint8_t *buf, int size); +int scst_restore_global_mode_pages(struct scst_device *dev, char *params, + char **last_param); + +int scst_read_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, uint8_t *buf, int size); +int scst_write_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, const uint8_t *buf, int size); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) +void scst_path_put(struct nameidata *nd); +#endif +int scst_remove_file(const char *name); + #endif /* __SCST_H */ diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index e4ebb6944..de3dd38be 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -495,20 +495,28 @@ enum { /************************************************************* ** Values for the control mode page TST field *************************************************************/ -#define SCST_CONTR_MODE_ONE_TASK_SET 0 -#define SCST_CONTR_MODE_SEP_TASK_SETS 1 +#define SCST_TST_0_SINGLE_TASK_SET 0 +#define SCST_TST_1_SEP_TASK_SETS 1 /******************************************************************* ** Values for the control mode page QUEUE ALGORITHM MODIFIER field *******************************************************************/ -#define SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER 0 -#define SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER 1 +#define SCST_QUEUE_ALG_0_RESTRICTED_REORDER 0 +#define SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER 1 /************************************************************* ** Values for the control mode page D_SENSE field *************************************************************/ -#define SCST_CONTR_MODE_FIXED_SENSE 0 -#define SCST_CONTR_MODE_DESCR_SENSE 1 +#define SCST_D_SENSE_0_FIXED_SENSE 0 +#define SCST_D_SENSE_1_DESCR_SENSE 1 + +/************************************************************* + ** Values for the control mode page QErr field + *************************************************************/ +#define SCST_QERR_0_ALL_RESUME 0 +#define SCST_QERR_1_ABORT_ALL 1 +#define SCST_QERR_2_RESERVED 2 +#define SCST_QERR_3_ABORT_THIS_NEXUS_ONLY 3 /************************************************************* ** TransportID protocol identifiers diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index 0e1306de5..1cda6c214 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -88,7 +88,9 @@ struct scst_user_opt { /* SCSI control mode page parameters, see SPC */ uint8_t tst; + uint8_t tmf_only; uint8_t queue_alg; + uint8_t qerr; uint8_t tas; uint8_t swp; uint8_t d_sense; diff --git a/scst/src/Makefile b/scst/src/Makefile index 07b179724..32575c7ef 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -116,7 +116,7 @@ endif @echo "*!! target drivers, custom dev handlers and necessary user !!*" @echo "*!! space applications. Otherwise, because of the versions !!*" @echo "*!! mismatch, you could have many problems and crashes. !!*" - @echo "*!! See IMPORTANT note in the \"Installation\" section of !!*" + @echo "*!! See IMPORTANT note in the \"Installation\" section of !!*" @echo "*!! SCST's README file for more info. !!*" @echo "*!! !!*" @echo "*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*" diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index 3ae5471d6..464ff06db 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -71,6 +71,7 @@ all: $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) install: all + mkdir -p $(DESTDIR)/var/lib/scst/vdev_mode_pages $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) \ modules_install diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index 4e432f6f1..684aca4ff 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -406,8 +406,9 @@ static int disk_exec(struct scst_cmd *cmd) if (unlikely((cmd->bufflen >> block_shift) > max_sectors)) { if ((cmd->out_bufflen >> block_shift) > max_sectors) { PRINT_ERROR("Too limited max_sectors %d for " - "bidirectional cmd %x (out_bufflen %d)", - max_sectors, cmd->cdb[0], cmd->out_bufflen); + "bidirectional cmd %p (op %s, out_bufflen %d)", + max_sectors, cmd, scst_get_opcode_name(cmd), + cmd->out_bufflen); /* Let lower level handle it */ res = SCST_EXEC_NOT_COMPLETED; goto out; diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 962a496b7..e2cf60d26 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -62,7 +62,9 @@ struct scst_user_dev { unsigned int blocking:1; unsigned int cleanup_done:1; unsigned int tst:3; + unsigned int tmf_only:1; unsigned int queue_alg:4; + unsigned int qerr:2; unsigned int tas:1; unsigned int swp:1; unsigned int d_sense:1; @@ -1368,8 +1370,8 @@ out_process: return res; out_inval: - PRINT_ERROR("Invalid parse_reply parameters (LUN %lld, op %x, cmd %p)", - (long long unsigned int)cmd->lun, cmd->cdb[0], cmd); + PRINT_ERROR("Invalid parse_reply parameters (LUN %lld, op %s, cmd %p)", + (long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd); PRINT_BUFFER("Invalid parse_reply", reply, sizeof(*reply)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); res = -EINVAL; @@ -1554,8 +1556,8 @@ out: return res; out_inval: - PRINT_ERROR("Invalid exec_reply parameters (LUN %lld, op %x, cmd %p)", - (long long unsigned int)cmd->lun, cmd->cdb[0], cmd); + PRINT_ERROR("Invalid exec_reply parameters (LUN %lld, op %s, cmd %p)", + (long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd); PRINT_BUFFER("Invalid exec_reply", reply, sizeof(*reply)); out_hwerr: @@ -2618,10 +2620,22 @@ static int dev_user_attach(struct scst_device *sdev) sdev->dh_priv = dev; sdev->tst = dev->tst; + sdev->tmf_only = dev->tmf_only; + sdev->tmf_only_saved = dev->tmf_only; + sdev->tmf_only_default = dev->tmf_only; sdev->queue_alg = dev->queue_alg; + sdev->qerr = dev->qerr; + sdev->qerr_saved = dev->qerr; + sdev->qerr_default = dev->qerr; sdev->swp = dev->swp; + sdev->swp_saved = dev->swp; + sdev->swp_default = dev->swp; sdev->tas = dev->tas; + sdev->tas_saved = dev->tas; + sdev->tas_default = dev->tas; sdev->d_sense = dev->d_sense; + sdev->d_sense_saved = dev->d_sense; + sdev->d_sense_default = dev->d_sense; sdev->has_own_order_mgmt = dev->has_own_order_mgmt; dev->sdev = sdev; @@ -3350,20 +3364,34 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, goto out; } - if (((opt->tst != SCST_CONTR_MODE_ONE_TASK_SET) && - (opt->tst != SCST_CONTR_MODE_SEP_TASK_SETS)) || - ((opt->queue_alg != SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) && - (opt->queue_alg != SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER)) || + if (((opt->tst != SCST_TST_0_SINGLE_TASK_SET) && + (opt->tst != SCST_TST_1_SEP_TASK_SETS)) || + (opt->tmf_only > 1) || + ((opt->queue_alg != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (opt->queue_alg != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) || + ((opt->qerr == SCST_QERR_2_RESERVED) || + (opt->qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) || (opt->swp > 1) || (opt->tas > 1) || (opt->has_own_order_mgmt > 1) || (opt->d_sense > 1)) { - PRINT_ERROR("Invalid SCSI option (tst %x, queue_alg %x, swp %x," - " tas %x, d_sense %d, has_own_order_mgmt %x)", opt->tst, - opt->queue_alg, opt->swp, opt->tas, opt->d_sense, - opt->has_own_order_mgmt); + PRINT_ERROR("Invalid SCSI option (tst %x, tmf_only %x, " + "queue_alg %x, qerr %x, swp %x, tas %x, d_sense %d, " + "has_own_order_mgmt %x)", + opt->tst, opt->tmf_only, opt->queue_alg, opt->qerr, + opt->swp, opt->tas, opt->d_sense, opt->has_own_order_mgmt); res = -EINVAL; goto out; } +#if 1 + if ((dev->tst != opt->tst) && (dev->sdev != NULL) && + !list_empty(&dev->sdev->dev_tgt_dev_list)) { + PRINT_ERROR("On the fly setting of TST not supported. " + "See comment in struct scst_device."); + res = -EINVAL; + goto out; + } +#endif + dev->parse_type = opt->parse_type; dev->on_free_cmd_type = opt->on_free_cmd_type; dev->memory_reuse_type = opt->memory_reuse_type; @@ -3371,14 +3399,18 @@ static int __dev_user_set_opt(struct scst_user_dev *dev, dev->partial_len = opt->partial_len; dev->tst = opt->tst; + dev->tmf_only = opt->tmf_only; dev->queue_alg = opt->queue_alg; + dev->qerr = opt->qerr; dev->swp = opt->swp; dev->tas = opt->tas; dev->d_sense = opt->d_sense; dev->has_own_order_mgmt = opt->has_own_order_mgmt; if (dev->sdev != NULL) { dev->sdev->tst = opt->tst; + dev->sdev->tmf_only = opt->tmf_only; dev->sdev->queue_alg = opt->queue_alg; + dev->sdev->qerr = opt->qerr; dev->sdev->swp = opt->swp; dev->sdev->tas = opt->tas; dev->sdev->d_sense = opt->d_sense; @@ -3449,7 +3481,9 @@ static int dev_user_get_opt(struct file *file, void __user *arg) opt.partial_transfers_type = dev->partial_transfers_type; opt.partial_len = dev->partial_len; opt.tst = dev->tst; + opt.tmf_only = dev->tmf_only; opt.queue_alg = dev->queue_alg; + opt.qerr = dev->qerr; opt.tas = dev->tas; opt.swp = dev->swp; opt.d_sense = dev->d_sense; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 58ae8a9f9..8ca710993 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifndef INSIDE_KERNEL_TREE #include #endif @@ -114,18 +115,20 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define VDISK_NULLIO_SIZE (5LL*1024*1024*1024*1024/2) -#define DEF_TST SCST_CONTR_MODE_SEP_TASK_SETS +#define DEF_TST SCST_TST_1_SEP_TASK_SETS +#define DEF_TMF_ONLY 0 /* * Since we can't control backstorage device's reordering, we have to always * report unrestricted reordering. */ -#define DEF_QUEUE_ALG_WT SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER -#define DEF_QUEUE_ALG SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER +#define DEF_QUEUE_ALG_WT SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER +#define DEF_QUEUE_ALG SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER + +#define DEF_QERR SCST_QERR_0_ALL_RESUME #define DEF_SWP 0 #define DEF_TAS 0 - -#define DEF_DSENSE SCST_CONTR_MODE_FIXED_SENSE +#define DEF_DSENSE SCST_D_SENSE_0_FIXED_SENSE #ifdef CONFIG_SCST_PROC #define VDISK_PROC_HELP "help" @@ -162,6 +165,8 @@ struct scst_vdisk_dev { unsigned int thin_provisioned_manually_set:1; unsigned int dev_thin_provisioned:1; unsigned int rotational:1; + unsigned int wt_flag_saved:1; + unsigned int tst:3; unsigned int format_active:1; struct file *fd; @@ -217,6 +222,8 @@ struct vdisk_cmd_params { bool use_zero_copy; }; +static bool vdev_saved_mode_pages_enabled = true; + enum compl_status_e { #if defined(SCST_DEBUG) COMPL_STATUS_START_AT = 777, @@ -332,6 +339,8 @@ static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_tp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_tst_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj, @@ -402,6 +411,8 @@ static struct kobj_attribute vdisk_wt_attr = __ATTR(write_through, S_IRUGO, vdisk_sysfs_wt_show, NULL); static struct kobj_attribute vdisk_tp_attr = __ATTR(thin_provisioned, S_IRUGO, vdisk_sysfs_tp_show, NULL); +static struct kobj_attribute vdisk_tst_attr = + __ATTR(tst, S_IRUGO, vdisk_sysfs_tst_show, NULL); static struct kobj_attribute vdisk_rotational_attr = __ATTR(rotational, S_IRUGO, vdisk_sysfs_rotational_show, NULL); static struct kobj_attribute vdisk_nv_cache_attr = @@ -455,6 +466,7 @@ static const struct attribute *vdisk_fileio_attrs[] = { &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, &vdisk_tp_attr.attr, + &vdisk_tst_attr.attr, &vdisk_rotational_attr.attr, &vdisk_nv_cache_attr.attr, &vdisk_o_direct_attr.attr, @@ -480,6 +492,7 @@ static const struct attribute *vdisk_blockio_attrs[] = { &vdisk_rd_only_attr.attr, &vdisk_wt_attr.attr, &vdisk_nv_cache_attr.attr, + &vdisk_tst_attr.attr, &vdisk_removable_attr.attr, &vdisk_rotational_attr.attr, &vdisk_filename_attr.attr, @@ -501,6 +514,7 @@ static const struct attribute *vdisk_nullio_attrs[] = { &vdev_size_mb_rw_attr.attr, &vdisk_blocksize_attr.attr, &vdisk_rd_only_attr.attr, + &vdisk_tst_attr.attr, &vdev_dummy_attr.attr, &vdisk_removable_attr.attr, &vdev_t10_vend_id_attr.attr, @@ -519,6 +533,7 @@ static const struct attribute *vcdrom_attrs[] = { &vdev_size_ro_attr.attr, &vdev_size_mb_ro_attr.attr, &vcdrom_filename_attr.attr, + &vdisk_tst_attr.attr, &vdev_t10_vend_id_attr.attr, &vdev_vend_specific_id_attr.attr, &vdev_prod_id_attr.attr, @@ -589,6 +604,7 @@ static struct scst_dev_type vdisk_file_devtype = { "removable, " "rotational, " "thin_provisioned, " + "tst, " "write_through, " "zero_copy", #endif @@ -634,6 +650,7 @@ static struct scst_dev_type vdisk_blk_devtype = { "removable, " "rotational, " "thin_provisioned, " + "tst, " "write_through", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -675,7 +692,8 @@ static struct scst_dev_type vdisk_null_devtype = { "removable, " "rotational, " "size, " - "size_mb", + "size_mb, " + "tst", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -711,7 +729,7 @@ static struct scst_dev_type vcdrom_devtype = { .add_device = vcdrom_add_device, .del_device = vcdrom_del_device, .dev_attrs = vcdrom_attrs, - .add_device_parameters = NULL, + .add_device_parameters = "tst", #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, @@ -944,6 +962,274 @@ static struct scst_vdisk_dev *vdev_find(const char *name) return res; } +#define VDEV_WT_LABEL "WRITE_THROUGH" +#define VDEV_MODE_PAGES_BUF_SIZE (64*1024) +#define VDEV_MODE_PAGES_DIR "/var/lib/scst/vdev_mode_pages" + +static int __vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev, + uint8_t *buf, int size) +{ + int res = 0; + + TRACE_ENTRY(); + + if (virt_dev->wt_flag != DEF_WRITE_THROUGH) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + VDEV_WT_LABEL, virt_dev->wt_flag); + if (res >= size-1) + goto out_overflow; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_overflow: + PRINT_ERROR("Mode pages buffer overflow (size %d)", size); + res = -EOVERFLOW; + goto out; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && !defined(RHEL_MAJOR) +/* + * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit + * e1ca7788dec6773b1a2bce51b7141948f2b8bccf). + */ +static void *vzalloc(unsigned long size) +{ + return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, + PAGE_KERNEL); +} +#endif + +static int vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev) +{ + int res, rc, offs; + uint8_t *buf; + int size; + char *name, *name1; + + TRACE_ENTRY(); + + size = VDEV_MODE_PAGES_BUF_SIZE; + + buf = vzalloc(size); + if (buf == NULL) { + PRINT_ERROR("Unable to alloc mode pages buffer (size %d)", size); + res = -ENOMEM; + goto out; + } + + name = kasprintf(GFP_KERNEL, "%s/%s", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name == NULL) { + PRINT_ERROR("Unable to create name %s/%s", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_vfree; + } + + name1 = kasprintf(GFP_KERNEL, "%s/%s1", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name1 == NULL) { + PRINT_ERROR("Unable to create name %s/%s1", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_free_name; + } + + offs = scst_save_global_mode_pages(virt_dev->dev, buf, size); + if (offs < 0) { + res = offs; + goto out_free_name1; + } + + rc = __vdev_save_mode_pages(virt_dev, &buf[offs], size - offs); + if (rc < 0) { + res = rc; + goto out_free_name1; + } + + offs += rc; + if (offs == 0) { + res = 0; + scst_remove_file(name); + scst_remove_file(name1); + goto out_free_name1; + } + + res = scst_write_file_transactional(name, name1, + virt_dev->name, strlen(virt_dev->name), buf, offs); + +out_free_name1: + kfree(name1); + +out_free_name: + kfree(name); + +out_vfree: + vfree(buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int vdev_restore_wt(struct scst_vdisk_dev *virt_dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, VDEV_WT_LABEL, virt_dev->name); + res = -EINVAL; + goto out; + } + + virt_dev->wt_flag = val; + virt_dev->wt_flag_saved = val; + + PRINT_INFO("WT_FLAG restored to %d for vdev %s", virt_dev->wt_flag, + virt_dev->name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Params are NULL-terminated */ +static int __vdev_load_mode_pages(struct scst_vdisk_dev *virt_dev, char *params) +{ + int res; + char *param, *p, *pp; + unsigned long val; + + TRACE_ENTRY(); + + while (1) { + param = scst_get_next_token_str(¶ms); + if (param == NULL) + break; + + p = scst_get_next_lexem(¶m); + if (*p == '\0') + break; + + pp = scst_get_next_lexem(¶m); + if (*pp == '\0') + goto out_need_param; + + if (scst_get_next_lexem(¶m)[0] != '\0') + goto out_too_many; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtoul(pp, 0, &val); +#else + res = strict_strtoul(pp, 0, &val); +#endif + if (res != 0) + goto out_strtoul_failed; + + if (strcasecmp(VDEV_WT_LABEL, p) == 0) + res = vdev_restore_wt(virt_dev, val); + else { + TRACE_DBG("Unknown parameter %s", p); + res = -EINVAL; + break; + } + if (res != 0) + goto out; + } + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; + +out_strtoul_failed: + PRINT_ERROR("strtoul() for %s failed: %d (device %s)", pp, res, + virt_dev->name); + goto out; + +out_need_param: + PRINT_ERROR("Parameter %s value missed for device %s", p, virt_dev->name); + res = -EINVAL; + goto out; + +out_too_many: + PRINT_ERROR("Too many parameter's %s values (device %s)", p, virt_dev->name); + res = -EINVAL; + goto out; +} + +static int vdev_load_mode_pages(struct scst_vdisk_dev *virt_dev) +{ + int res; + struct scst_device *dev = virt_dev->dev; + uint8_t *buf; + int size; + char *name, *name1, *params; + + TRACE_ENTRY(); + + size = VDEV_MODE_PAGES_BUF_SIZE; + + buf = vzalloc(size); + if (buf == NULL) { + PRINT_ERROR("Unable to alloc mode pages buffer (size %d)", size); + res = -ENOMEM; + goto out; + } + + name = kasprintf(GFP_KERNEL, "%s/%s", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name == NULL) { + PRINT_ERROR("Unable to create name %s/%s", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_vfree; + } + + name1 = kasprintf(GFP_KERNEL, "%s/%s1", VDEV_MODE_PAGES_DIR, virt_dev->name); + if (name1 == NULL) { + PRINT_ERROR("Unable to create name %s/%s1", VDEV_MODE_PAGES_DIR, + virt_dev->name); + res = -ENOMEM; + goto out_free_name; + } + + size = scst_read_file_transactional(name, name1, + virt_dev->name, strlen(virt_dev->name), buf, size-1); + if (size <= 0) { + res = size; + goto out_free_name1; + } + + buf[size-1] = '\0'; + + res = scst_restore_global_mode_pages(dev, &buf[strlen(virt_dev->name)+1], + ¶ms); + if ((res != 0) || (params == NULL)) + goto out_free_name1; + + res = __vdev_load_mode_pages(virt_dev, params); + +out_free_name1: + kfree(name1); + +out_free_name: + kfree(name); + +out_vfree: + vfree(buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + static int vdisk_attach(struct scst_device *dev) { int res = 0; @@ -1021,14 +1307,31 @@ static int vdisk_attach(struct scst_device *dev) dev->dh_priv = virt_dev; - dev->tst = DEF_TST; + dev->tst = virt_dev->tst; + dev->tmf_only = DEF_TMF_ONLY; + dev->tmf_only_saved = DEF_TMF_ONLY; + dev->tmf_only_default = DEF_TMF_ONLY; dev->d_sense = DEF_DSENSE; + dev->d_sense_saved = DEF_DSENSE; + dev->d_sense_default = DEF_DSENSE; if (virt_dev->wt_flag && !virt_dev->nv_cache) dev->queue_alg = DEF_QUEUE_ALG_WT; else dev->queue_alg = DEF_QUEUE_ALG; + dev->queue_alg_saved = dev->queue_alg; + dev->queue_alg_default = dev->queue_alg; + dev->qerr = DEF_QERR; + dev->qerr_saved = DEF_QERR; + dev->qerr_default = DEF_QERR; dev->swp = DEF_SWP; + dev->swp_saved = DEF_SWP; + dev->swp_default = DEF_SWP; dev->tas = DEF_TAS; + dev->tas_saved = DEF_TAS; + dev->tas_default = DEF_TAS; + + if (vdev_saved_mode_pages_enabled) + vdev_load_mode_pages(virt_dev); out: TRACE_EXIT(); @@ -1258,7 +1561,7 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) PRINT_ERROR("FORMAT UNIT: too small parameters list " "header %d (dev %s)", length, dev->virt_name); scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); goto out_put; } @@ -1278,8 +1581,8 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) PRINT_ERROR("FORMAT UNIT: too small long " "parameters list header %d (dev %s)", length, dev->virt_name); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 1, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 5); goto out_put; } if ((buf[3] & 0xF0) != 0) { @@ -1404,7 +1707,7 @@ out_put: static enum compl_status_e vdisk_invalid_opcode(struct vdisk_cmd_params *p) { - TRACE_DBG("Invalid opcode %d", p->cmd->cdb[0]); + TRACE_DBG("Invalid opcode %s", scst_get_opcode_name(p->cmd)); return INVALID_OPCODE; } @@ -1883,9 +2186,8 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) TRACE_ENTRY(); if (unlikely(!(cmd->op_flags & SCST_INFO_VALID))) { - TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + TRACE(TRACE_MINOR, "Unknown opcode %s", scst_get_opcode_name(cmd)); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); res = false; goto out; } @@ -1899,10 +2201,12 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) switch (cmd->queue_type) { case SCST_CMD_QUEUE_ORDERED: - TRACE(TRACE_ORDER, "ORDERED cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE(TRACE_ORDER, "ORDERED cmd %p (op %s)", cmd, + scst_get_opcode_name(cmd)); break; case SCST_CMD_QUEUE_HEAD_OF_QUEUE: - TRACE(TRACE_ORDER, "HQ cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE(TRACE_ORDER, "HQ cmd %p (op %s)", cmd, + scst_get_opcode_name(cmd)); break; default: break; @@ -1921,14 +2225,18 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd) if (unlikely((loff + data_len) > virt_dev->file_size) && (!(cmd->op_flags & SCST_LBA_NOT_VALID))) { - PRINT_INFO("Access beyond the end of device %s " - "(%lld of %lld, data len %lld)", - virt_dev->name, - (long long unsigned int)loff, - (long long unsigned int)virt_dev->file_size, - (long long unsigned int)data_len); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + if (virt_dev->cdrom_empty) { + TRACE_DBG("%s", "CDROM empty"); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_no_medium)); + } else { + PRINT_INFO("Access beyond the end of device %s " + "(%lld of %lld, data len %lld)", virt_dev->name, + (long long unsigned int)loff, + (long long unsigned int)virt_dev->file_size, + (long long unsigned int)data_len); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( scst_sense_block_out_range_error)); + } res = false; goto out; } @@ -2479,7 +2787,7 @@ out_thr: return res; out_invalid_opcode: - TRACE_DBG("Invalid opcode 0x%x", opcode); + TRACE_DBG("Invalid opcode %s", scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_compl; } @@ -2923,17 +3231,18 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) if (cmd->cdb[1] & EVPD) { if (0 == cmd->cdb[2]) { /* supported vital product data pages */ - buf[3] = 3; + buf[3] = 4; buf[4] = 0x0; /* this page */ buf[5] = 0x80; /* unit serial number */ buf[6] = 0x83; /* device identification */ + buf[7] = 0x86; /* extended inquiry */ if (dev->type == TYPE_DISK) { buf[3] += 2; - buf[7] = 0xB0; /* block limits */ - buf[8] = 0xB1; /* block limits */ + buf[8] = 0xB0; /* block limits */ + buf[9] = 0xB1; /* block device charachteristics */ if (virt_dev->thin_provisioned) { buf[3] += 1; - buf[9] = 0xB2; /* thin provisioning */ + buf[10] = 0xB2; /* thin provisioning */ } } resp_len = buf[3] + 4; @@ -3044,6 +3353,14 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) resp_len = num; put_unaligned_be16(resp_len, &buf[2]); resp_len += 4; + } else if (0x86 == cmd->cdb[2]) { + /* Extended INQUIRY */ + buf[1] = 0x86; + buf[3] = 0x3C; + buf[5] = 7; /* HEADSUP=1, ORDSUP=1, SIMPSUP=1 */ + buf[6] = (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : 1; /* V_SUP */ + buf[7] = 1; /* LUICLR=1 */ + resp_len = buf[3] + 4; } else if ((0xB0 == cmd->cdb[2]) && (dev->type == TYPE_DISK)) { /* Block Limits */ int max_transfer; @@ -3383,55 +3700,92 @@ static int vdisk_format_pg(unsigned char *p, int pcontrol, static int vdisk_caching_pg(unsigned char *p, int pcontrol, struct scst_vdisk_dev *virt_dev) { /* Caching page for mode_sense */ - const unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0, + unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; - memcpy(p, caching_pg, sizeof(caching_pg)); - p[2] |= !(virt_dev->wt_flag || virt_dev->nv_cache) ? WCE : 0; - if (1 == pcontrol) + if (!virt_dev->nv_cache && vdev_saved_mode_pages_enabled) + caching_pg[0] |= 0x80; + + switch (pcontrol) { + case 0: /* current */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : WCE; + break; + case 1: /* changeable */ memset(p + 2, 0, sizeof(caching_pg) - 2); + if (!virt_dev->nv_cache) + p[2] |= WCE; + break; + case 2: /* default */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (DEF_WRITE_THROUGH || virt_dev->nv_cache) ? 0 : WCE; + break; + case 3: /* saved */ + memcpy(p, caching_pg, sizeof(caching_pg)); + p[2] |= (virt_dev->wt_flag_saved || virt_dev->nv_cache) ? 0 : WCE; + break; + default: + sBUG_ON(1); + break; + } + return sizeof(caching_pg); } static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol, struct scst_vdisk_dev *virt_dev) { /* Control mode page for mode_sense */ - const unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0, + unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0x2, 0x4b}; + if (vdev_saved_mode_pages_enabled) + ctrl_m_pg[0] |= 0x80; + memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg)); switch (pcontrol) { case 0: /* current */ p[2] |= virt_dev->dev->tst << 5; + p[2] |= virt_dev->dev->tmf_only << 4; p[2] |= virt_dev->dev->d_sense << 2; p[3] |= virt_dev->dev->queue_alg << 4; + p[3] |= virt_dev->dev->qerr << 1; p[4] |= virt_dev->dev->swp << 3; p[5] |= virt_dev->dev->tas << 6; break; case 1: /* changeable */ memset(p + 2, 0, sizeof(ctrl_m_pg) - 2); #if 0 /* - * It's too early to implement it, since we can't control the - * backstorage device parameters. ToDo + * See comment in struct scst_device definition. + * + * If enable it, fix the default and saved cases below! */ p[2] |= 7 << 5; /* TST */ - p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ #endif p[2] |= 1 << 2; /* D_SENSE */ + p[2] |= 1 << 4; /* TMF_ONLY */ + p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */ + p[3] |= 3 << 1; /* QErr */ p[4] |= 1 << 3; /* SWP */ p[5] |= 1 << 6; /* TAS */ break; case 2: /* default */ - p[2] |= DEF_TST << 5; - p[2] |= DEF_DSENSE << 2; - if (virt_dev->wt_flag || virt_dev->nv_cache) - p[3] |= DEF_QUEUE_ALG_WT << 4; - else - p[3] |= DEF_QUEUE_ALG << 4; - p[4] |= DEF_SWP << 3; - p[5] |= DEF_TAS << 6; + p[2] |= virt_dev->tst << 5; + p[2] |= virt_dev->dev->d_sense_default << 2; + p[2] |= virt_dev->dev->tmf_only_default << 4; + p[3] |= virt_dev->dev->queue_alg_default << 4; + p[3] |= virt_dev->dev->qerr_default << 1; + p[4] |= virt_dev->dev->swp_default << 3; + p[5] |= virt_dev->dev->tas_default << 6; + break; + case 3: /* saved */ + p[2] |= virt_dev->dev->tst << 5; + p[2] |= virt_dev->dev->d_sense_saved << 2; + p[2] |= virt_dev->dev->tmf_only_default << 4; + p[3] |= virt_dev->dev->queue_alg_saved << 4; + p[3] |= virt_dev->dev->qerr_saved << 1; + p[4] |= virt_dev->dev->swp_saved << 3; + p[5] |= virt_dev->dev->tas_saved << 6; break; - case 3: /* saved, blocked by the caller */ default: sBUG(); } @@ -3491,7 +3845,7 @@ static enum compl_status_e vdisk_exec_mode_sense(struct vdisk_cmd_params *p) if (unlikely(length <= 0)) goto out_free; - if (0x3 == pcontrol) { + if (!vdev_saved_mode_pages_enabled && (0x3 == pcontrol)) { TRACE_DBG("%s", "MODE SENSE: Saving values not supported"); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_saving_params_unsup)); @@ -3648,31 +4002,198 @@ out: } static void vdisk_ctrl_m_pg_select(unsigned char *p, - struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd) + struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save, + int param_offset) { struct scst_device *dev = virt_dev->dev; int old_swp = dev->swp, old_tas = dev->tas, old_dsense = dev->d_sense; + int old_queue_alg = dev->queue_alg; + int rc, old_tmf_only = dev->tmf_only, old_qerr = dev->qerr; + int queue_alg, swp, tas, tmf_only, qerr, d_sense; -#if 0 /* Not implemented yet, see comment in vdisk_ctrl_m_pg() */ - dev->tst = (p[2] >> 5) & 1; - dev->queue_alg = p[3] >> 4; + TRACE_ENTRY(); + + if (save && !vdev_saved_mode_pages_enabled) { + TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: saved control page " + "not supported"); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); + goto out; + } + + /* + * MODE SELECT is a strictly serialized cmd, so it is safe to + * perform direct assignment here. + */ + +#if 0 /* Not implemented yet, see comment in struct scst_device */ + dev->tst = (p[2] >> 5) & 7; + /* ToDo: check validity of the new value */ #else - if ((dev->tst != ((p[2] >> 5) & 1)) || (dev->queue_alg != (p[3] >> 4))) { + if (dev->tst != ((p[2] >> 5) & 7)) { TRACE(TRACE_MINOR|TRACE_SCSI, "%s", "MODE SELECT: Changing of " - "TST and QUEUE ALGORITHM not supported"); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); - return; + "TST not supported"); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 5); + goto out; } #endif - dev->swp = (p[4] & 0x8) >> 3; - dev->tas = (p[5] & 0x40) >> 6; - dev->d_sense = (p[2] & 0x4) >> 2; + queue_alg = p[3] >> 4; + if ((queue_alg != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (queue_alg != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) { + PRINT_WARNING("Attempt to set invalid Control mode page QUEUE " + "ALGORITHM MODIFIER value %d (initiator %s, dev %s)", + queue_alg, cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 3, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); + goto out; + } + + swp = (p[4] & 0x8) >> 3; + if (swp > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page SWP " + "value %d (initiator %s, dev %s)", swp, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 4, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); + goto out; + } + + tas = (p[5] & 0x40) >> 6; + if (tas > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page TAS " + "value %d (initiator %s, dev %s)", tas, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 5, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 6); + goto out; + } + + tmf_only = (p[2] & 0x10) >> 4; + if (tmf_only > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page " + "TMF_ONLY value %d (initiator %s, dev %s)", tmf_only, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); + goto out; + } + + qerr = (p[3] & 0x6) >> 1; + if ((qerr == SCST_QERR_2_RESERVED) || + (qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) { + PRINT_WARNING("Attempt to set invalid Control mode page QErr " + "value %d (initiator %s, dev %s)", qerr, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 3, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); + goto out; + } + + d_sense = (p[2] & 0x4) >> 2; + if (d_sense > 1) { + PRINT_WARNING("Attempt to set invalid Control mode page D_SENSE " + "value %d (initiator %s, dev %s)", d_sense, + cmd->sess->initiator_name, dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, param_offset + 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 2); + goto out; + } + + dev->queue_alg = queue_alg; + dev->swp = swp; + dev->tas = tas; + dev->tmf_only = tmf_only; + dev->qerr = qerr; + dev->d_sense = d_sense; + + if ((dev->swp == old_swp) && (dev->tas == old_tas) && + (dev->d_sense == old_dsense) && (dev->queue_alg == old_queue_alg) && + (dev->qerr == old_qerr) && (dev->tmf_only == old_tmf_only)) + goto out; + + if (!save) + goto out_ok; + + rc = vdev_save_mode_pages(virt_dev); + if (rc != 0) { + dev->swp = old_swp; + dev->tas = old_tas; + dev->d_sense = old_dsense; + dev->queue_alg = old_queue_alg; + dev->tmf_only = old_tmf_only; + dev->qerr = old_qerr; + /* Hopefully, the error is temporary */ + scst_set_busy(cmd); + goto out; + } + + dev->swp_saved = dev->swp; + dev->tas_saved = dev->tas; + dev->d_sense_saved = dev->d_sense; + dev->queue_alg_saved = dev->queue_alg; + dev->tmf_only_saved = dev->tmf_only; + dev->qerr_saved = dev->qerr; + +out_ok: PRINT_INFO("Device %s: new control mode page parameters: SWP %x " - "(was %x), TAS %x (was %x), D_SENSE %d (was %d)", + "(was %x), TAS %x (was %x), TMF_ONLY %d (was %x), QErr %x " + "(was %x), D_SENSE %d (was %d), QUEUE ALG %d (was %d)", virt_dev->name, dev->swp, old_swp, dev->tas, old_tas, - dev->d_sense, old_dsense); + dev->tmf_only, old_tmf_only, dev->qerr, old_qerr, + dev->d_sense, old_dsense, dev->queue_alg, old_queue_alg); + +out: + TRACE_EXIT(); + return; +} + +static void vdisk_caching_m_pg_select(unsigned char *p, + struct scst_vdisk_dev *virt_dev, struct scst_cmd *cmd, bool save, + bool read_only) +{ + int old_wt = virt_dev->wt_flag, new_wt, rc; + + TRACE_ENTRY(); + + if (save && (!vdev_saved_mode_pages_enabled || virt_dev->nv_cache)) { + TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: saved cache page " + "not supported"); + scst_set_invalid_field_in_cdb(cmd, 1, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); + goto out; + } + + new_wt = (p[2] & WCE) ? 0 : 1; + + if (new_wt == old_wt) + goto out; + + if (vdisk_set_wt(virt_dev, new_wt, read_only) != 0) { + scst_set_busy(cmd); + goto out; + } + + if (!save) + goto out_ok; + + rc = vdev_save_mode_pages(virt_dev); + if (rc != 0) { + vdisk_set_wt(virt_dev, old_wt, read_only); + /* Hopefully, the error is temporary */ + scst_set_busy(cmd); + goto out; + } + + virt_dev->wt_flag_saved = virt_dev->wt_flag; + +out_ok: + PRINT_INFO("Device %s: new wt_flag: %x (was %x)", virt_dev->name, + virt_dev->wt_flag, old_wt); + +out: + TRACE_EXIT(); return; } @@ -3694,10 +4215,9 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) if (unlikely(length <= 0)) goto out; - if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) { + if (!(cmd->cdb[1] & PF)) { TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: Unsupported " - "value(s) of PF and/or SP bits (cdb[1]=%x)", - cmd->cdb[1]); + "PF bit zero (cdb[1]=%x)", cmd->cdb[1]); scst_set_invalid_field_in_cdb(cmd, 1, 0); goto out_put; } @@ -3730,11 +4250,8 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) scst_set_invalid_field_in_parm_list(cmd, offset+1, 0); goto out_put; } - if (vdisk_set_wt(virt_dev, (address[offset + 2] & WCE) ? 0 : 1, - cmd->tgt_dev->tgt_dev_rd_only) != 0) { - scst_set_busy(cmd); - goto out_put; - } + vdisk_caching_m_pg_select(&address[offset], virt_dev, + cmd, cmd->cdb[1] & SP, cmd->tgt_dev->tgt_dev_rd_only); break; } else if ((address[offset] & 0x3f) == 0xA) { /* Control page */ @@ -3744,7 +4261,8 @@ static enum compl_status_e vdisk_exec_mode_select(struct vdisk_cmd_params *p) scst_set_invalid_field_in_parm_list(cmd, offset+1, 0); goto out_put; } - vdisk_ctrl_m_pg_select(&address[offset], virt_dev, cmd); + vdisk_ctrl_m_pg_select(&address[offset], virt_dev, cmd, + cmd->cdb[1] & SP, offset); } else { TRACE(TRACE_MINOR, "MODE SELECT: Invalid request %x", address[offset] & 0x3f); @@ -3796,8 +4314,7 @@ static enum compl_status_e vdisk_exec_read_capacity(struct vdisk_cmd_params *p) uint32_t lba = get_unaligned_be32(&cmd->cdb[2]); if (lba != 0) { TRACE_DBG("PMI zero and LBA not zero (cmd %p)", cmd); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 2, 0); goto out; } } @@ -3857,8 +4374,7 @@ static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p uint32_t lba = get_unaligned_be32(&cmd->cdb[2]); if (lba != 0) { TRACE_DBG("PMI zero and LBA not zero (cmd %p)", cmd); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 2, 0); goto out; } } @@ -5220,15 +5736,14 @@ static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd, struct scst_vdisk_dev *virt_dev = dev->dh_priv; int rc; - dev->tst = DEF_TST; - dev->d_sense = DEF_DSENSE; - dev->swp = DEF_SWP; - dev->tas = DEF_TAS; + dev->tmf_only = dev->tmf_only_saved; + dev->d_sense = dev->d_sense_saved; + dev->swp = dev->swp_saved; + dev->tas = dev->tas_saved; + dev->queue_alg = dev->queue_alg_saved; + dev->qerr = dev->qerr_saved; - if (virt_dev->wt_flag && !virt_dev->nv_cache) - dev->queue_alg = DEF_QUEUE_ALG_WT; - else - dev->queue_alg = DEF_QUEUE_ALG; + dev->tst = virt_dev->tst; rc = vdisk_set_wt(virt_dev, DEF_WRITE_THROUGH, tgt_dev->tgt_dev_rd_only); @@ -5288,6 +5803,10 @@ static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev) i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE", (j == i) ? "(" : ", "); + if (virt_dev->tst != DEF_TST) + i += snprintf(&buf[i], sizeof(buf) - i, "%sTST %d", + (j == i) ? "(" : ", ", virt_dev->tst); + if (virt_dev->rotational) i += snprintf(&buf[i], sizeof(buf) - i, "%sROTATIONAL", (j == i) ? "(" : ", "); @@ -5383,6 +5902,7 @@ static int vdev_create(struct scst_dev_type *devt, virt_dev->removable = DEF_REMOVABLE; virt_dev->rotational = DEF_ROTATIONAL; virt_dev->thin_provisioned = DEF_THIN_PROVISIONED; + virt_dev->tst = DEF_TST; virt_dev->blk_shift = DEF_DISK_BLOCK_SHIFT; @@ -5571,6 +6091,15 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } else if (!strcasecmp("rotational", p)) { virt_dev->rotational = val; TRACE_DBG("ROTATIONAL %d", virt_dev->rotational); + } else if (!strcasecmp("tst", p)) { + if ((val != SCST_TST_0_SINGLE_TASK_SET) && + (val != SCST_TST_1_SEP_TASK_SETS)) { + PRINT_ERROR("Invalid TST value %d", (int)val); + res = -EINVAL; + goto out; + } + virt_dev->tst = val; + TRACE_DBG("TST %d", virt_dev->tst); } else if (!strcasecmp("thin_provisioned", p)) { virt_dev->thin_provisioned = val; virt_dev->thin_provisioned_manually_set = 1; @@ -5676,7 +6205,7 @@ static int vdev_blockio_add_device(const char *device_name, char *params) int res = 0; const char *const allowed_params[] = { "filename", "read_only", "write_through", "removable", "blocksize", "nv_cache", - "rotational", "thin_provisioned", NULL }; + "rotational", "thin_provisioned", "tst", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -5734,7 +6263,7 @@ static int vdev_nullio_add_device(const char *device_name, char *params) int res = 0; static const char *const allowed_params[] = { "read_only", "dummy", "removable", "blocksize", "rotational", - "size", "size_mb", NULL + "size", "size_mb", "tst", NULL }; struct scst_vdisk_dev *virt_dev; @@ -5891,7 +6420,7 @@ out: static ssize_t __vcdrom_add_device(const char *device_name, char *params) { int res = 0; - const char *allowed_params[] = { NULL }; /* no params */ + const char *allowed_params[] = { "tst", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -6462,6 +6991,27 @@ static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj, return pos; } +static ssize_t vdisk_sysfs_tst_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->tst); + + if (virt_dev->tst != DEF_TST) + pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK); + + TRACE_EXIT_RES(pos); + return pos; +} + static ssize_t vdisk_sysfs_rotational_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -7848,6 +8398,46 @@ static void init_ops(vdisk_op_fn *ops, int count) return; } +static int __init vdev_check_mode_pages_path(void) +{ + int res; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + struct nameidata nd; +#else + struct path path; +#endif + mm_segment_t old_fs = get_fs(); + + TRACE_ENTRY(); + + set_fs(KERNEL_DS); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + res = path_lookup(VDEV_MODE_PAGES_DIR, 0, &nd); + if (res == 0) + scst_path_put(&nd); +#else + res = kern_path(VDEV_MODE_PAGES_DIR, 0, &path); + if (res == 0) + path_put(&path); +#endif + if (res != 0) { + PRINT_WARNING("Unable to find %s (err %d), saved mode pages " + "disabled. You should create this directory manually " + "or reinstall SCST", VDEV_MODE_PAGES_DIR, res); + vdev_saved_mode_pages_enabled = false; + goto out_setfs; + } + +out_setfs: + set_fs(old_fs); + + res = 0; /* always succeed */ + + TRACE_EXIT_RES(res); + return res; +} + static int __init init_scst_vdisk_driver(void) { int res; @@ -7856,6 +8446,10 @@ static int __init init_scst_vdisk_driver(void) init_ops(blockio_ops, ARRAY_SIZE(blockio_ops)); init_ops(nullio_ops, ARRAY_SIZE(nullio_ops)); + res = vdev_check_mode_pages_path(); + if (res != 0) + goto out; + vdisk_cmd_param_cachep = KMEM_CACHE(vdisk_cmd_params, SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN); if (vdisk_cmd_param_cachep == NULL) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index b90f82447..cd7f98eac 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -1595,8 +1595,8 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic) cmd->sense = mempool_alloc(scst_sense_mempool, gfp_mask); if (cmd->sense == NULL) { - PRINT_CRIT_ERROR("Sense memory allocation failed (op %x). " - "The sense data will be lost!!", cmd->cdb[0]); + PRINT_CRIT_ERROR("Sense memory allocation failed (op %s). " + "The sense data will be lost!!", scst_get_opcode_name(cmd)); res = -ENOMEM; goto out; } @@ -1641,7 +1641,8 @@ int scst_alloc_set_sense(struct scst_cmd *cmd, int atomic, cmd->sense_valid_len = len; if (cmd->sense_buflen < len) { PRINT_WARNING("Sense truncated (needed %d), shall you increase " - "SCST_SENSE_BUFFERSIZE? Op: %x", len, cmd->cdb[0]); + "SCST_SENSE_BUFFERSIZE? Op: %s", len, + scst_get_opcode_name(cmd)); cmd->sense_valid_len = cmd->sense_buflen; } @@ -2894,9 +2895,10 @@ void scst_check_reassign_sessions(void) return; } -int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) +int scst_get_cmd_abnormal_done_state(struct scst_cmd *cmd) { int res; + bool trace = false; TRACE_ENTRY(); @@ -2907,12 +2909,14 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) if (cmd->preprocessing_only) { res = SCST_CMD_STATE_PREPROCESSING_DONE; break; - } /* else go through */ + } + trace = true; + /* go through */ case SCST_CMD_STATE_DEV_DONE: if (cmd->internal) res = SCST_CMD_STATE_FINISHED_INTERNAL; else - res = SCST_CMD_STATE_PRE_XMIT_RESP; + res = SCST_CMD_STATE_PRE_XMIT_RESP1; break; case SCST_CMD_STATE_PRE_DEV_DONE: @@ -2920,15 +2924,20 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) res = SCST_CMD_STATE_DEV_DONE; break; - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + res = SCST_CMD_STATE_PRE_XMIT_RESP2; + break; + + case SCST_CMD_STATE_PRE_XMIT_RESP2: res = SCST_CMD_STATE_XMIT_RESP; break; case SCST_CMD_STATE_PREPROCESSING_DONE: case SCST_CMD_STATE_PREPROCESSING_DONE_CALLED: - if (cmd->tgt_dev == NULL) - res = SCST_CMD_STATE_PRE_XMIT_RESP; - else + if (cmd->tgt_dev == NULL) { + trace = true; + res = SCST_CMD_STATE_PRE_XMIT_RESP1; + } else res = SCST_CMD_STATE_PRE_DEV_DONE; break; @@ -2949,11 +2958,29 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) break; default: - PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)", - cmd->state, cmd, cmd->cdb[0]); + PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %s)", + cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 /* Invalid state to suppress a compiler warning */ res = SCST_CMD_STATE_LAST_ACTIVE; +#endif + } + + if (trace) { + /* + * Little hack to trace completion of commands, which are + * going to bypass normal tracing on SCST_CMD_STATE_PRE_DEV_DONE + */ + TRACE(TRACE_SCSI, "cmd %p, status %x, msg_status %x, host_status %x, " + "driver_status %x, resp_data_len %d", cmd, cmd->status, + cmd->msg_status, cmd->host_status, cmd->driver_status, + cmd->resp_data_len); + if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) && + scst_sense_valid(cmd->sense)) { + PRINT_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense, + cmd->sense_valid_len); + } } TRACE_EXIT_RES(res); @@ -2977,8 +3004,8 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) case SCST_CMD_STATE_FINISHED: case SCST_CMD_STATE_FINISHED_INTERNAL: case SCST_CMD_STATE_XMIT_WAIT: - PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)", - cmd->state, cmd, cmd->cdb[0]); + PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %s)", + cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); } #endif @@ -3006,22 +3033,23 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) case SCST_CMD_STATE_DEV_DONE: case SCST_CMD_STATE_PRE_DEV_DONE: case SCST_CMD_STATE_MODE_SELECT_CHECKS: - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_FINISHED_INTERNAL: break; default: - PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %x)", - cmd->state, cmd, cmd->cdb[0]); + PRINT_CRIT_ERROR("Wrong cmd state %d (cmd %p, op %s)", + cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); break; } #ifdef CONFIG_SCST_EXTRACHECKS - if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) && + if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP1) && (cmd->state != SCST_CMD_STATE_PREPROCESSING_DONE)) && (cmd->tgt_dev == NULL) && !cmd->internal) { PRINT_CRIT_ERROR("Wrong not inited cmd state %d (cmd %p, " - "op %x)", cmd->state, cmd, cmd->cdb[0]); + "op %s)", cmd->state, cmd, scst_get_opcode_name(cmd)); sBUG(); } #endif @@ -3031,6 +3059,20 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd) } EXPORT_SYMBOL_GPL(scst_set_cmd_abnormal_done_state); +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +const char *scst_get_opcode_name(struct scst_cmd *cmd) +{ + if (cmd->op_name) + return cmd->op_name; + else { + scnprintf(cmd->not_parsed_op_name, + sizeof(cmd->not_parsed_op_name), "0x%x", cmd->cdb[0]); + return cmd->not_parsed_op_name; + } +} +EXPORT_SYMBOL(scst_get_opcode_name); +#endif + void scst_zero_write_rest(struct scst_cmd *cmd) { int len, offs = 0; @@ -3674,7 +3716,7 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev) INIT_LIST_HEAD(&dev->dev_tgt_dev_list); INIT_LIST_HEAD(&dev->dev_acg_dev_list); dev->dev_double_ua_possible = 1; - dev->queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER; + dev->queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; mutex_init(&dev->dev_pr_mutex); dev->pr_generation = 0; @@ -4358,7 +4400,8 @@ out_deinit: /* * scst_mutex supposed to be held, there must not be parallel activity in this - * session. + * session. May be invoked from inside scst_check_reassign_sessions() which + * means that sess->acg can be NULL. */ static int scst_alloc_add_tgt_dev(struct scst_session *sess, struct scst_acg_dev *acg_dev, struct scst_tgt_dev **out_tgt_dev) @@ -4386,7 +4429,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev->tgt_dev_rd_only = acg_dev->acg_dev_rd_only || dev->dev_rd_only; tgt_dev->sess = sess; atomic_set(&tgt_dev->tgt_dev_cmd_count, 0); - if (sess->acg->acg_black_hole_type != SCST_ACG_BLACK_HOLE_NONE) + if (acg_dev->acg->acg_black_hole_type != SCST_ACG_BLACK_HOLE_NONE) set_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); else clear_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); @@ -4419,7 +4462,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, INIT_LIST_HEAD(&tgt_dev->UA_list); scst_init_order_data(&tgt_dev->tgt_dev_order_data); - if (dev->tst == SCST_CONTR_MODE_SEP_TASK_SETS) + if (dev->tst == SCST_TST_1_SEP_TASK_SETS) tgt_dev->curr_order_data = &tgt_dev->tgt_dev_order_data; else tgt_dev->curr_order_data = &dev->dev_order_data; @@ -4799,7 +4842,8 @@ static struct scst_cmd *scst_create_prepare_internal_cmd( scst_set_start_time(res); - TRACE(TRACE_SCSI, "New internal cmd %p (op 0x%x)", res, res->cdb[0]); + TRACE(TRACE_SCSI, "New internal cmd %p (op %s)", res, + scst_get_opcode_name(res)); rc = scst_pre_parse(res); sBUG_ON(rc != 0); @@ -4889,10 +4933,10 @@ static void scst_complete_request_sense(struct scst_cmd *req_cmd) if (scsi_status_is_good(req_cmd->status) && (len > 0) && scst_sense_valid(buf) && !scst_no_sense(buf)) { - PRINT_BUFF_FLAG(TRACE_SCSI, "REQUEST SENSE returned", + TRACE(TRACE_SCSI, "REQUEST SENSE %p returned valid sense", + req_cmd); + scst_alloc_set_sense(orig_cmd, scst_cmd_atomic(req_cmd), buf, len); - scst_alloc_set_sense(orig_cmd, scst_cmd_atomic(req_cmd), buf, - len); } else { PRINT_ERROR("%s", "Unable to get the sense via " "REQUEST SENSE, returning HARDWARE ERROR"); @@ -5325,7 +5369,7 @@ static void scst_send_release(struct scst_device *dev) , NULL #endif ); - TRACE_DBG("MODE_SENSE done: %x", rc); + TRACE_DBG("RELEASE done: %x", rc); if (scsi_status_is_good(rc)) { break; @@ -5759,8 +5803,8 @@ void scst_free_cmd(struct scst_cmd *cmd) } if (likely(cmd->tgt_dev != NULL)) { - EXTRACHECKS_BUG_ON(!test_bit(SCST_CMD_INC_EXPECTED_SN_PASSED, - &cmd->cmd_flags) && cmd->sn_set && !cmd->out_of_sn); + EXTRACHECKS_BUG_ON(cmd->sn_set && !cmd->out_of_sn && + !test_bit(SCST_CMD_INC_EXPECTED_SN_PASSED, &cmd->cmd_flags)); if (unlikely(cmd->out_of_sn)) { destroy = test_and_set_bit(SCST_CMD_CAN_BE_DESTROYED, &cmd->cmd_flags); @@ -5882,7 +5926,7 @@ void scst_free_mgmt_cmd(struct scst_mgmt_cmd *mcmd) scst_sess_put(mcmd->sess); - if (mcmd->mcmd_tgt_dev != NULL) + if ((mcmd->mcmd_tgt_dev != NULL) || mcmd->scst_get_called) scst_put(mcmd->cpu_cmd_counter); mempool_free(mcmd, scst_mgmt_mempool); @@ -6409,7 +6453,7 @@ int scst_get_buf_full(struct scst_cmd *cmd, uint8_t **buf) *buf = vmalloc(full_size); if (*buf == NULL) { TRACE(TRACE_OUT_OF_MEM, "vmalloc() failed for opcode " - "%x", cmd->cdb[0]); + "%s", scst_get_opcode_name(cmd)); res = -ENOMEM; goto out; } @@ -6594,7 +6638,8 @@ out: return res; out_inval_bufflen10: - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, 10, 0); res = 1; goto out; @@ -6796,8 +6841,8 @@ static int get_cdb_info_verify12(struct scst_cmd *cmd, if (cmd->cdb[1] & 2) { /* BYTCHK 01 */ cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", - cmd->bufflen, cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", + cmd->bufflen, scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6826,8 +6871,8 @@ static int get_cdb_info_verify16(struct scst_cmd *cmd, if (cmd->cdb[1] & 2) { /* BYTCHK 01 */ cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", - cmd->bufflen, cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", + cmd->bufflen, scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6900,8 +6945,8 @@ static int get_cdb_info_len_4(struct scst_cmd *cmd, cmd->lba = 0; cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, - cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6973,8 +7018,8 @@ static int get_cdb_info_lba_4_len_4(struct scst_cmd *cmd, cmd->lba = get_unaligned_be32(cmd->cdb + sdbops->info_lba_off); cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, - cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -6988,8 +7033,8 @@ static int get_cdb_info_lba_8_len_4(struct scst_cmd *cmd, cmd->lba = get_unaligned_be64(cmd->cdb + sdbops->info_lba_off); cmd->bufflen = get_unaligned_be32(cmd->cdb + sdbops->info_len_off); if (unlikely(cmd->bufflen & SCST_MAX_VALID_BUFFLEN_MASK)) { - PRINT_ERROR("Too big bufflen %d (op %x)", cmd->bufflen, - cmd->cdb[0]); + PRINT_ERROR("Too big bufflen %d (op %s)", cmd->bufflen, + scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, sdbops->info_len_off, 0); return 1; } @@ -7135,8 +7180,9 @@ static int get_cdb_info_min(struct scst_cmd *cmd, break; case MI_REPORT_SUPPORTED_OPERATION_CODES: cmd->op_name = "REPORT SUPPORTED OPERATION CODES"; - cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED | - SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; + cmd->op_flags |= SCST_WRITE_EXCL_ALLOWED; + if (cmd->devt->get_supported_opcodes != NULL) + cmd->op_flags |= SCST_LOCAL_CMD | SCST_FULLY_LOCAL_CMD; break; case MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS: cmd->op_name = "REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS"; @@ -7343,9 +7389,10 @@ EXPORT_SYMBOL_GPL(scst_calc_block_shift); #define shift_left_overflows(a, b) \ ({ \ typeof(a) _minus_one = -1LL; \ + typeof(a) _plus_one = 1; \ bool _a_is_signed = _minus_one < 0; \ - int _shift = sizeof(1ULL) * 8 - ((b) + _a_is_signed); \ - _shift < 0 || ((a) & ~((1ULL << _shift) - 1)) != 0; \ + int _shift = sizeof(a) * 8 - ((b) + _a_is_signed); \ + _shift < 0 || ((a) & ~((_plus_one << _shift) - 1)) != 0;\ }) /** @@ -7370,6 +7417,20 @@ static inline int scst_generic_parse(struct scst_cmd *cmd, const int timeout[3]) * No need for locks here, since *_detach() can not be * called, when there are existing commands. */ + bool overflow = shift_left_overflows(cmd->bufflen, block_shift) || + shift_left_overflows(cmd->data_len, block_shift) || + shift_left_overflows(cmd->out_bufflen, block_shift); + if (unlikely(overflow)) { + PRINT_WARNING("bufflen %u, data_len %llu or out_bufflen" + " %u too large for device %s (block size" + " %u)", cmd->bufflen, cmd->data_len, + cmd->out_bufflen, cmd->dev->virt_name, + 1 << block_shift); + PRINT_BUFFER("CDB", cmd->cdb, cmd->cdb_len); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + scst_sense_block_out_range_error)); + goto out; + } cmd->bufflen = cmd->bufflen << block_shift; cmd->data_len = cmd->data_len << block_shift; cmd->out_bufflen = cmd->out_bufflen << block_shift; @@ -8009,7 +8070,7 @@ void scst_process_reset(struct scst_device *dev, dev->dev_double_ua_possible = 1; list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { + dev_tgt_dev_list_entry) { struct scst_session *sess = tgt_dev->sess; #if 0 /* Clearing UAs and last sense isn't required by SAM and it @@ -8044,6 +8105,17 @@ void scst_process_reset(struct scst_device *dev, uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; int sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), dev->d_sense, SCST_LOAD_SENSE(scst_sense_reset_UA)); + /* + * Potentially, setting UA here, when the aborted commands are + * still running, can lead to a situation that one of them could + * take it, then that would be detected and the UA requeued. + * But, meanwhile, one or more subsequent, i.e. not aborted, + * commands can "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". However, + * that should be OK, because, if multiple commands are being + * executed in parallel, you can't control exact order of UA + * delivery anyway. + */ scst_dev_check_set_local_UA(dev, exclude_cmd, sense_buffer, sl); } @@ -8401,7 +8473,7 @@ struct scst_cmd *__scst_check_deferred_commands_locked( restart: list_for_each_entry_safe(cmd, t, &order_data->deferred_cmd_list, - sn_cmd_list_entry) { + deferred_cmd_list_entry) { EXTRACHECKS_BUG_ON((cmd->queue_type != SCST_CMD_QUEUE_SIMPLE) && (cmd->queue_type != SCST_CMD_QUEUE_ORDERED)); if (cmd->sn == expected_sn) { @@ -8411,7 +8483,7 @@ restart: cmd, cmd->sn, cmd->sn_set); order_data->def_cmd_count--; - list_del(&cmd->sn_cmd_list_entry); + list_del(&cmd->deferred_cmd_list_entry); if (activate) { spin_lock(&cmd->cmd_threads->cmd_list_lock); @@ -8443,18 +8515,19 @@ restart: goto out; list_for_each_entry(cmd, &order_data->skipped_sn_list, - sn_cmd_list_entry) { + deferred_cmd_list_entry) { EXTRACHECKS_BUG_ON(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE); if (cmd->sn == expected_sn) { /* * !! At this point any pointer in cmd, except !! - * !! cur_order_data, sn_slot and sn_cmd_list_entry, !! - * !! could be already destroyed! !! + * !! cur_order_data, sn_slot and !! + * !! deferred_cmd_list_entry, could be already !! + * !! destroyed! !! */ TRACE_SN("cmd %p (tag %llu) with skipped sn %d found", cmd, (long long unsigned int)cmd->tag, cmd->sn); order_data->def_cmd_count--; - list_del(&cmd->sn_cmd_list_entry); + list_del(&cmd->deferred_cmd_list_entry); spin_unlock_irq(&order_data->sn_lock); scst_inc_expected_sn(cmd); if (test_and_set_bit(SCST_CMD_CAN_BE_DESTROYED, @@ -8505,7 +8578,7 @@ void scst_unblock_deferred(struct scst_order_data *order_data, out_of_sn_cmd->out_of_sn = 1; spin_lock_irq(&order_data->sn_lock); order_data->def_cmd_count++; - list_add_tail(&out_of_sn_cmd->sn_cmd_list_entry, + list_add_tail(&out_of_sn_cmd->deferred_cmd_list_entry, &order_data->skipped_sn_list); TRACE_SN("out_of_sn_cmd %p with sn %d added to skipped_sn_list" " (expected_sn %d)", out_of_sn_cmd, out_of_sn_cmd->sn, @@ -8551,15 +8624,15 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd) if (dev->block_count > 0) { TRACE_BLOCK("Delaying cmd %p due to blocking " - "(tag %llu, op %x, dev %s)", cmd, - (long long unsigned int)cmd->tag, cmd->cdb[0], - dev->virt_name); + "(tag %llu, op %s, dev %s)", cmd, + (long long unsigned int)cmd->tag, + scst_get_opcode_name(cmd), dev->virt_name); goto out_block; } else if ((cmd->op_flags & SCST_STRICTLY_SERIALIZED) == SCST_STRICTLY_SERIALIZED) { - TRACE_BLOCK("cmd %p (tag %llu, op %x): blocking further " + TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further " "cmds on dev %s due to strict serialization", cmd, - (long long unsigned int)cmd->tag, cmd->cdb[0], - dev->virt_name); + (long long unsigned int)cmd->tag, + scst_get_opcode_name(cmd), dev->virt_name); scst_block_dev(dev); if (dev->on_dev_cmd_count > 1) { TRACE_BLOCK("Delaying strictly serialized cmd %p " @@ -8572,9 +8645,9 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd) cmd->unblock_dev = 1; } else if ((dev->dev_double_ua_possible) || ((cmd->op_flags & SCST_SERIALIZED) != 0)) { - TRACE_BLOCK("cmd %p (tag %llu, op %x): blocking further cmds " + TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further cmds " "on dev %s due to %s", cmd, (long long unsigned int)cmd->tag, - cmd->cdb[0], dev->virt_name, + scst_get_opcode_name(cmd), dev->virt_name, dev->dev_double_ua_possible ? "possible double reset UA" : "serialized cmd"); scst_block_dev(dev); @@ -8707,12 +8780,16 @@ int scst_obtain_device_parameters(struct scst_device *dev, "page data", buffer, sizeof(buffer)); dev->tst = buffer[4+2] >> 5; + dev->tmf_only = (buffer[4+2] & 0x10) >> 4; q = buffer[4+3] >> 4; - if (q > SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER) { - PRINT_ERROR("Too big QUEUE ALG %x, dev %s", + if (q > SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER) { + PRINT_ERROR("Too big QUEUE ALG %x, dev %s, " + "using default: unrestricted reorder", dev->queue_alg, dev->virt_name); + q = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; } dev->queue_alg = q; + dev->qerr = (buffer[4+3] & 0x6) >> 1; dev->swp = (buffer[4+4] & 0x8) >> 3; dev->tas = (buffer[4+5] & 0x40) >> 6; dev->d_sense = (buffer[4+2] & 0x4) >> 2; @@ -8725,10 +8802,11 @@ int scst_obtain_device_parameters(struct scst_device *dev, */ dev->has_own_order_mgmt = !dev->queue_alg; - PRINT_INFO("Device %s: TST %x, QUEUE ALG %x, SWP %x, " - "TAS %x, D_SENSE %d, has_own_order_mgmt %d", - dev->virt_name, dev->tst, dev->queue_alg, - dev->swp, dev->tas, dev->d_sense, + PRINT_INFO("Device %s: TST %x, TMF_ONLY %x, QUEUE ALG %x, " + "QErr %x, SWP %x, TAS %x, D_SENSE %d, " + "has_own_order_mgmt %d", dev->virt_name, + dev->tst, dev->tmf_only, dev->queue_alg, + dev->qerr, dev->swp, dev->tas, dev->d_sense, dev->has_own_order_mgmt); goto out; @@ -8745,9 +8823,8 @@ int scst_obtain_device_parameters(struct scst_device *dev, */ if (scst_sense_valid(sense_buffer)) { #endif - PRINT_BUFF_FLAG(TRACE_SCSI, "Returned sense " - "data", sense_buffer, - sizeof(sense_buffer)); + PRINT_BUFF_FLAG(TRACE_SCSI, "MODE SENSE returned " + "sense", sense_buffer, sizeof(sense_buffer)); if (scst_analyze_sense(sense_buffer, sizeof(sense_buffer), SCST_SENSE_KEY_VALID, @@ -8768,8 +8845,6 @@ int scst_obtain_device_parameters(struct scst_device *dev, PRINT_INFO("Internal MODE SENSE to " "device %s failed: %x", dev->virt_name, rc); - PRINT_BUFF_FLAG(TRACE_SCSI, "MODE SENSE sense", - sense_buffer, sizeof(sense_buffer)); switch (host_byte(rc)) { case DID_RESET: case DID_ABORT: @@ -8790,9 +8865,10 @@ int scst_obtain_device_parameters(struct scst_device *dev, } brk: PRINT_WARNING("Unable to get device's %s control mode page, using " - "existing values/defaults: TST %x, QUEUE ALG %x, SWP %x, " - "TAS %x, D_SENSE %d, has_own_order_mgmt %d", dev->virt_name, - dev->tst, dev->queue_alg, dev->swp, dev->tas, dev->d_sense, + "existing values/defaults: TST %x, TMF_ONLY %x, QUEUE ALG %x, " + "QErr %x, SWP %x, TAS %x, D_SENSE %d, has_own_order_mgmt %d", + dev->virt_name, dev->tst, dev->tmf_only, dev->queue_alg, + dev->qerr, dev->swp, dev->tas, dev->d_sense, dev->has_own_order_mgmt); out: @@ -8860,6 +8936,151 @@ void scst_store_sense(struct scst_cmd *cmd) return; } +/* dev_lock supposed to be locked and BHs off */ +static void scst_abort_cmds_tgt_dev(struct scst_tgt_dev *tgt_dev, + struct scst_cmd *exclude_cmd) +{ + struct scst_session *sess = tgt_dev->sess; + struct scst_cmd *cmd; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("QErr: aborting commands for tgt_dev %p " + "(exclude_cmd %p), if there are any", tgt_dev, exclude_cmd); + + spin_lock_irq(&sess->sess_list_lock); + + list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) { + if (cmd == exclude_cmd) + continue; + if ((cmd->tgt_dev == tgt_dev) || + ((cmd->tgt_dev == NULL) && + (cmd->lun == tgt_dev->lun))) { + scst_abort_cmd(cmd, NULL, (tgt_dev != exclude_cmd->tgt_dev), 0); + } + } + spin_unlock_irq(&sess->sess_list_lock); + + TRACE_EXIT(); + return; +} + +/* dev_lock supposed to be locked and BHs off */ +static void scst_abort_cmds_dev(struct scst_device *dev, + struct scst_cmd *exclude_cmd) +{ + struct scst_tgt_dev *tgt_dev; + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + int sl = 0; + bool set_ua = (dev->tas == 0); + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("QErr: Aborting commands for dev %p (exclude_cmd %p, " + "set_ua %d), if there are any", dev, exclude_cmd, set_ua); + + if (set_ua) + sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), dev->d_sense, + SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA)); + + list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { + scst_abort_cmds_tgt_dev(tgt_dev, exclude_cmd); + /* + * Potentially, setting UA here, when the aborted commands are + * still running, can lead to a situation that one of them could + * take it, then that would be detected and the UA requeued. + * But, meanwhile, one or more subsequent, i.e. not aborted, + * commands can "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". However, + * that should be OK, because, if multiple commands are being + * executed in parallel, you can't control exact order of UA + * delivery anyway. + */ + if (set_ua && (tgt_dev != exclude_cmd->tgt_dev)) + scst_check_set_UA(tgt_dev, sense_buffer, sl, 0); + } + + TRACE_EXIT(); + return; +} + +/* No locks */ +static void scst_process_qerr(struct scst_cmd *cmd) +{ + bool unblock = false; + struct scst_device *dev = cmd->dev; + unsigned int qerr, q; + + TRACE_ENTRY(); + + /* dev->qerr can be changed behind our back */ + q = dev->qerr; + qerr = ACCESS_ONCE(q); /* ACCESS_ONCE doesn't work for bit fields */ + + TRACE_DBG("Processing QErr %d for cmd %p", qerr, cmd); + + spin_lock_bh(&dev->dev_lock); + + switch (qerr) { + case SCST_QERR_2_RESERVED: + default: + PRINT_WARNING("Invalid QErr value %x for device %s, process as " + "0", qerr, dev->virt_name); + /* go through */ + case SCST_QERR_0_ALL_RESUME: + /* Nothing to do */ + break; + case SCST_QERR_1_ABORT_ALL: + if (dev->tst == SCST_TST_0_SINGLE_TASK_SET) + scst_abort_cmds_dev(dev, cmd); + else + scst_abort_cmds_tgt_dev(cmd->tgt_dev, cmd); + unblock = true; + break; + case SCST_QERR_3_ABORT_THIS_NEXUS_ONLY: + scst_abort_cmds_tgt_dev(cmd->tgt_dev, cmd); + unblock = true; + break; + } + + spin_unlock_bh(&dev->dev_lock); + + if (unblock) + scst_unblock_aborted_cmds(cmd->tgt, cmd->sess, dev, false); + + TRACE_EXIT(); + return; +} + +/* + * No locks. Returns -1, if processing should be switched to another cmd, 1 + * if cmd was aborted, 0 if cmd processing should continue. + */ +int scst_process_check_condition(struct scst_cmd *cmd) +{ + int res; + struct scst_order_data *order_data; + struct scst_device *dev; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags)); + + order_data = cmd->cur_order_data; + dev = cmd->dev; + + TRACE_DBG("CHECK CONDITION for cmd %p (tgt_dev %p)", cmd, cmd->tgt_dev); + + scst_process_qerr(cmd); + + scst_store_sense(cmd); + + res = 0; + + TRACE_EXIT_RES(res); + return res; +} + void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd) { TRACE_ENTRY(); @@ -9139,8 +9360,7 @@ static int scst_parse_unmap_descriptors(struct scst_cmd *cmd) ((descriptor_len % 16) != 0))) { PRINT_ERROR("Bad descriptor length: %d < %d - 8", descriptor_len, total_len); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + scst_set_invalid_field_in_parm_list(cmd, 2, 0); goto out_abn_put; } @@ -9236,6 +9456,318 @@ static void scst_free_descriptors(struct scst_cmd *cmd) return; } +/** + ** We currently have only few saved parameters and it is impossible to get + ** pointer on a bit field, so let's have a simple straightforward + ** implementation. + **/ + +#define SCST_TAS_LABEL "TAS" +#define SCST_QERR_LABEL "QERR" +#define SCST_TMF_ONLY_LABEL "TMF_ONLY" +#define SCST_SWP_LABEL "SWP" +#define SCST_DSENSE_LABEL "D_SENSE" +#define SCST_QUEUE_ALG_LABEL "QUEUE_ALG" + +int scst_save_global_mode_pages(const struct scst_device *dev, + uint8_t *buf, int size) +{ + int res = 0; + + TRACE_ENTRY(); + + if (dev->tas != dev->tas_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_TAS_LABEL, dev->tas); + if (res >= size-1) + goto out_overflow; + } + + if (dev->qerr != dev->qerr_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_QERR_LABEL, dev->qerr); + if (res >= size-1) + goto out_overflow; + } + + if (dev->tmf_only != dev->tmf_only_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_TMF_ONLY_LABEL, dev->tmf_only); + if (res >= size-1) + goto out_overflow; + } + + if (dev->swp != dev->swp_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_SWP_LABEL, dev->swp); + if (res >= size-1) + goto out_overflow; + } + + if (dev->d_sense != dev->d_sense_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_DSENSE_LABEL, dev->d_sense); + if (res >= size-1) + goto out_overflow; + } + + if (dev->queue_alg != dev->queue_alg_default) { + res += scnprintf(&buf[res], size - res, "%s=%d\n", + SCST_QUEUE_ALG_LABEL, dev->queue_alg); + if (res >= size-1) + goto out_overflow; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_overflow: + PRINT_ERROR("Global mode pages buffer overflow (size %d)", size); + res = -EOVERFLOW; + goto out; +} +EXPORT_SYMBOL_GPL(scst_save_global_mode_pages); + +static int scst_restore_tas(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_TAS_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->tas = val; + dev->tas_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_TAS_LABEL, + dev->tas, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_qerr(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if ((val == SCST_QERR_2_RESERVED) || + (val > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_QERR_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->qerr = val; + dev->qerr_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_QERR_LABEL, + dev->qerr, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_tmf_only(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_TMF_ONLY_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->tmf_only = val; + dev->tmf_only_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_TMF_ONLY_LABEL, + dev->tmf_only, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_swp(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_SWP_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->swp = val; + dev->swp_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_SWP_LABEL, + dev->swp, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_dsense(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if (val > 1) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_DSENSE_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->d_sense = val; + dev->d_sense_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_DSENSE_LABEL, + dev->d_sense, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_restore_queue_alg(struct scst_device *dev, unsigned int val) +{ + int res; + + TRACE_ENTRY(); + + if ((val != SCST_QUEUE_ALG_0_RESTRICTED_REORDER) && + (val != SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER)) { + PRINT_ERROR("Invalid value %d for parameter %s (device %s)", + val, SCST_QUEUE_ALG_LABEL, dev->virt_name); + res = -EINVAL; + goto out; + } + + dev->queue_alg = val; + dev->queue_alg_saved = val; + + PRINT_INFO("%s restored to %d for device %s", SCST_QUEUE_ALG_LABEL, + dev->queue_alg, dev->virt_name); + + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Params are NULL-terminated */ +int scst_restore_global_mode_pages(struct scst_device *dev, char *params, + char **last_param) +{ + int res; + char *param, *p, *pp; + unsigned long val; + + TRACE_ENTRY(); + + while (1) { + param = scst_get_next_token_str(¶ms); + if (param == NULL) + break; + + p = scst_get_next_lexem(¶m); + if (*p == '\0') + break; + + pp = scst_get_next_lexem(¶m); + if (*pp == '\0') + goto out_need_param; + + if (scst_get_next_lexem(¶m)[0] != '\0') + goto out_too_many; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtoul(pp, 0, &val); +#else + res = strict_strtoul(pp, 0, &val); +#endif + if (res != 0) + goto out_strtoul_failed; + + if (strcasecmp(SCST_TAS_LABEL, p) == 0) + res = scst_restore_tas(dev, val); + else if (strcasecmp(SCST_QERR_LABEL, p) == 0) + res = scst_restore_qerr(dev, val); + else if (strcasecmp(SCST_TMF_ONLY_LABEL, p) == 0) + res = scst_restore_tmf_only(dev, val); + else if (strcasecmp(SCST_SWP_LABEL, p) == 0) + res = scst_restore_swp(dev, val); + else if (strcasecmp(SCST_DSENSE_LABEL, p) == 0) + res = scst_restore_dsense(dev, val); + else if (strcasecmp(SCST_QUEUE_ALG_LABEL, p) == 0) + res = scst_restore_queue_alg(dev, val); + else { + TRACE_DBG("Unknown parameter %s", p); + scst_restore_token_str(p, param); + *last_param = p; + goto out; + } + if (res != 0) + goto out; + } + + *last_param = NULL; + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; + +out_strtoul_failed: + PRINT_ERROR("strtoul() for %s failed: %d (device %s)", pp, res, + dev->virt_name); + goto out; + +out_need_param: + PRINT_ERROR("Parameter %s value missed for device %s", p, dev->virt_name); + res = -EINVAL; + goto out; + +out_too_many: + PRINT_ERROR("Too many parameter's %s values (device %s)", p, dev->virt_name); + res = -EINVAL; + goto out; +} +EXPORT_SYMBOL_GPL(scst_restore_global_mode_pages); + + /* Abstract vfs_unlink() for different kernel versions (as possible) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) void scst_vfs_unlink_and_put(struct nameidata *nd) @@ -9272,6 +9804,7 @@ void scst_path_put(struct nameidata *nd) path_put(&nd->path); #endif } +EXPORT_SYMBOL(scst_path_put); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) @@ -9420,6 +9953,195 @@ int scst_remove_file(const char *name) TRACE_EXIT_RES(res); return res; } +EXPORT_SYMBOL_GPL(scst_remove_file); + +/* Returns 0 on success, error code otherwise */ +int scst_write_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, const uint8_t *buf, int size) +{ + int res; + struct file *file; + mm_segment_t old_fs = get_fs(); + loff_t pos = 0; + char n = '\n'; + + TRACE_ENTRY(); + + res = scst_copy_file(name, name1); + if ((res != 0) && (res != -ENOENT)) + goto out; + + set_fs(KERNEL_DS); + + file = filp_open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(file)) { + res = PTR_ERR(file); + PRINT_ERROR("Unable to (re)create file '%s' - error %d", + name, res); + goto out_set_fs; + } + + TRACE_DBG("Writing file '%s'", name); + + pos = signature_len+1; + + res = vfs_write(file, (void __force __user *)buf, size, &pos); + if (res != size) + goto write_error; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_vfs_fsync(file, 0, pos); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + res = vfs_fsync(file, file->f_path.dentry, 1); +#else + res = vfs_fsync(file, 1); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of file %s failed: %d", name, res); + goto write_error_close; + } + + pos = 0; + res = vfs_write(file, (void __force __user *)signature, signature_len, &pos); + if (res != signature_len) + goto write_error; + + res = vfs_write(file, (void __force __user *)&n, sizeof(n), &pos); + if (res != sizeof(n)) + goto write_error; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_vfs_fsync(file, 0, sizeof(signature)); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + res = vfs_fsync(file, file->f_path.dentry, 1); +#else + res = vfs_fsync(file, 1); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of file %s failed: %d", name, res); + goto write_error_close; + } + + res = 0; + + filp_close(file, NULL); + +out_set_fs: + set_fs(old_fs); + + if (res == 0) + scst_remove_file(name1); + else + scst_remove_file(name); + +out: + TRACE_EXIT_RES(res); + return res; + +write_error: + PRINT_ERROR("Error writing to '%s' - error %d", name, res); + +write_error_close: + filp_close(file, NULL); + if (res > 0) + res = -EIO; + goto out_set_fs; +} +EXPORT_SYMBOL_GPL(scst_write_file_transactional); + +static int __scst_read_file_transactional(const char *file_name, + const char *signature, int signature_len, uint8_t *buf, int size) +{ + int res; + struct file *file = NULL; + struct inode *inode; + loff_t file_size, pos; + mm_segment_t old_fs; + + TRACE_ENTRY(); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + TRACE_DBG("Loading file '%s'", file_name); + + file = filp_open(file_name, O_RDONLY, 0); + if (IS_ERR(file)) { + res = PTR_ERR(file); + TRACE_DBG("Unable to open file '%s' - error %d", file_name, res); + goto out; + } + + inode = file->f_dentry->d_inode; + + if (S_ISREG(inode->i_mode)) + /* Nothing to do */; + else if (S_ISBLK(inode->i_mode)) + inode = inode->i_bdev->bd_inode; + else { + PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); + res = -EINVAL; + goto out_close; + } + + file_size = inode->i_size; + + if (file_size > size) { + PRINT_ERROR("Supplied buffer (%d) too small (need %d)", size, + (int)file_size); + res = -EOVERFLOW; + goto out_close; + } + + pos = 0; + res = vfs_read(file, (void __force __user *)buf, file_size, &pos); + if (res != file_size) { + PRINT_ERROR("Unable to read file '%s' - error %d", file_name, res); + if (res > 0) + res = -EIO; + goto out_close; + } + + if (memcmp(buf, signature, signature_len) != 0) { + res = -EINVAL; + PRINT_ERROR("Invalid signature in file %s", file_name); + goto out_close; + } + +out_close: + filp_close(file, NULL); + +out: + set_fs(old_fs); + + TRACE_EXIT_RES(res); + return res; +} + +/* + * Returns read data size on success, error code otherwise. The first + * signature_len+1 bytes of the read data contain signature, so should be + * skipped. + */ +int scst_read_file_transactional(const char *name, const char *name1, + const char *signature, int signature_len, uint8_t *buf, int size) +{ + int res; + + TRACE_ENTRY(); + + res = __scst_read_file_transactional(name, signature, signature_len, buf, size); + if (res <= 0) + res = __scst_read_file_transactional(name1, signature, + signature_len, buf, size); + + if (res > 0) + TRACE_BUFFER("Read data", buf, res); + + TRACE_EXIT_RES(res); + return res; +} +EXPORT_SYMBOL_GPL(scst_read_file_transactional); static void __init scst_scsi_op_list_init(void) { diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index d3e99cbac..d3f24e383 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -960,8 +960,8 @@ int scst_suspend_activity(unsigned long timeout) set_bit(SCST_FLAG_SUSPENDED, &scst_flags); /* * Assignment of SCST_FLAG_SUSPENDING and SCST_FLAG_SUSPENDED must be - * ordered with cpu_cmd_count in scst_get(). Otherwise lockless logic in - * scst_translate_lun() and scst_mgmt_translate_lun() won't work. + * ordered with cpu_cmd_count in scst_get(). Otherwise, lockless logic + * of scst_get() users won't work. */ smp_mb__after_set_bit(); diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 48c246d3d..61017a4a8 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -565,6 +565,27 @@ static void scst_pr_abort_reg(struct scst_device *dev, */ PRINT_ERROR("SCST_PR_ABORT_ALL failed %d (sess %p)", rc, sess); + goto out; + } + + if ((reg->tgt_dev != pr_cmd->tgt_dev) && !dev->tas) { + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + int sl; + sl = scst_set_sense(sense_buffer, sizeof(sense_buffer), + dev->d_sense, + SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA)); + /* + * Potentially, setting UA here, when the aborted commands are + * still running, can lead to a situation that one of them could + * take it, then that would be detected and the UA requeued. + * But, meanwhile, one or more subsequent, i.e. not aborted, + * commands can "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". However, + * that should be OK, because, if multiple commands are being + * executed in parallel, you can't control exact order of UA + * delivery anyway. + */ + scst_check_set_UA(reg->tgt_dev, sense_buffer, sl, 0); } out: @@ -1559,8 +1580,8 @@ void scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) } if (spec_i_pt) { TRACE_PR("%s", "spec_i_pt must be zero in this case"); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE( - scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_parm_list(cmd, 20, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); goto out; } if (action_key == 0) { @@ -1700,8 +1721,7 @@ void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, if (tid_buffer_size < 24) { TRACE_PR("%s", "Transport id buffer too small"); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + scst_set_invalid_field_in_parm_list(cmd, 20, 0); goto out; } @@ -1730,9 +1750,8 @@ void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, */ if (!scst_pr_is_holder(dev, reg)) { TRACE_PR("Registrant %s/%d (%p) is not a holder (tgt_dev %p)", - debug_transport_id_to_initiator_name( - reg->transport_id), reg->rel_tgt_id, - reg, tgt_dev); + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, tgt_dev); scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); goto out; } @@ -2267,8 +2286,9 @@ bool scst_pr_crh_case(struct scst_cmd *cmd) TRACE_ENTRY(); - TRACE_DBG("Test if there is a CRH case for command %s (0x%x) from " - "%s", cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + TRACE_DBG("Test if there is a CRH case for command %s (%s) from " + "%s", cmd->op_name, scst_get_opcode_name(cmd), + cmd->sess->initiator_name); if (!dev->pr_is_set) { TRACE_PR("%s", "PR not set"); @@ -2303,12 +2323,12 @@ bool scst_pr_crh_case(struct scst_cmd *cmd) } if (!allowed) - TRACE_PR("Command %s (0x%x) from %s rejected due to not CRH " - "reservation", cmd->op_name, cmd->cdb[0], + TRACE_PR("Command %s (%s) from %s rejected due to not CRH " + "reservation", cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); else - TRACE_DBG("Command %s (0x%x) from %s is allowed to execute " - "due to CRH", cmd->op_name, cmd->cdb[0], + TRACE_DBG("Command %s (%s) from %s is allowed to execute " + "due to CRH", cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); out: @@ -2330,8 +2350,8 @@ bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd) scst_pr_read_lock(dev); - TRACE_DBG("Testing if command %s (0x%x) from %s allowed to execute", - cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + TRACE_DBG("Testing if command %s (%s) from %s allowed to execute", + cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); /* Recheck, because it can change while we were waiting for the lock */ if (unlikely(!dev->pr_is_set)) { @@ -2380,12 +2400,13 @@ bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd) } if (!allowed) - TRACE_PR("Command %s (0x%x) from %s rejected due " - "to PR", cmd->op_name, cmd->cdb[0], + TRACE_PR("Command %s (%s) from %s rejected due " + "to PR", cmd->op_name, scst_get_opcode_name(cmd), cmd->sess->initiator_name); else - TRACE_DBG("Command %s (0x%x) from %s is allowed to execute", - cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + TRACE_DBG("Command %s (%s) from %s is allowed to execute", + cmd->op_name, scst_get_opcode_name(cmd), + cmd->sess->initiator_name); out_unlock: scst_pr_read_unlock(dev); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index f56d185be..7df673ee8 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -375,6 +375,8 @@ int scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense, unsigned int len); void scst_store_sense(struct scst_cmd *cmd); +int scst_process_check_condition(struct scst_cmd *cmd); + int scst_assign_dev_handler(struct scst_device *dev, struct scst_dev_type *handler); @@ -770,16 +772,11 @@ void scst_vfs_unlink_and_put(struct nameidata *nd); void scst_vfs_unlink_and_put(struct path *path); #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) -void scst_path_put(struct nameidata *nd); -#endif - #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) int scst_vfs_fsync(struct file *file, loff_t loff, loff_t len); #endif int scst_copy_file(const char *src, const char *dest); -int scst_remove_file(const char *name); #ifdef CONFIG_SCST_DEBUG_TM extern void tm_dbg_check_released_cmds(void); diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index 91e4101c6..2a3bc1981 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -397,7 +397,12 @@ int scst_proc_log_entry_write(struct file *file, const char __user *buf, list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { if (strcmp(dev->virt_name, p) == 0) { - scst_pr_dump_prs(dev, true); + if (mutex_lock_interruptible(&dev->dev_pr_mutex) == 0) { + scst_pr_dump_prs(dev, true); + mutex_unlock(&dev->dev_pr_mutex); + } else { + res = -EINTR; + } goto out_up; } } diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 8ca303484..5187d104d 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -2762,15 +2762,23 @@ static ssize_t scst_dev_sysfs_dump_prs(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct scst_device *dev; + int res; TRACE_ENTRY(); dev = container_of(kobj, struct scst_device, dev_kobj); + res = mutex_lock_interruptible(&dev->dev_pr_mutex); + if (res != 0) + goto out; scst_pr_dump_prs(dev, true); + mutex_unlock(&dev->dev_pr_mutex); - TRACE_EXIT_RES(count); - return count; + res = count; + +out: + TRACE_EXIT_RES(res); + return res; } static struct kobj_attribute dev_dump_prs_attr = @@ -5414,8 +5422,8 @@ static ssize_t scst_tg_preferred_show(struct kobject *kobj, struct scst_target_group *tg; tg = container_of(kobj, struct scst_target_group, kobj); - return scnprintf(buf, PAGE_SIZE, "%u\n%s", - tg->preferred, SCST_SYSFS_KEY_MARK "\n"); + return scnprintf(buf, PAGE_SIZE, "%u\n%s", tg->preferred, + tg->preferred ? SCST_SYSFS_KEY_MARK "\n" : ""); } static int scst_tg_preferred_store_work_fn(struct scst_sysfs_work_item *w) diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index ff80f55d1..bde560fa3 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -413,12 +413,12 @@ void scst_cmd_init_done(struct scst_cmd *cmd, scst_set_start_time(cmd); TRACE_DBG("Preferred context: %d (cmd %p)", pref_context, cmd); - TRACE(TRACE_SCSI, "lun=%lld, initiator %s, target %s, CDB len=%d, " - "queue_type=%x, tag=%llu (cmd %p, sess %p)", - (long long unsigned int)cmd->lun, cmd->sess->initiator_name, - cmd->tgt->tgt_name, cmd->cdb_len, cmd->queue_type, + TRACE(TRACE_SCSI, "NEW CDB: len %d, lun %lld, initiator %s, " + "target %s, queue_type %x, tag %llu (cmd %p, sess %p)", + cmd->cdb_len, (long long unsigned int)cmd->lun, + cmd->sess->initiator_name, cmd->tgt->tgt_name, cmd->queue_type, (long long unsigned int)cmd->tag, cmd, sess); - PRINT_BUFF_FLAG(TRACE_SCSI, "Receiving CDB", cmd->cdb, cmd->cdb_len); + PRINT_BUFF_FLAG(TRACE_SCSI, "CDB", cmd->cdb, cmd->cdb_len); #ifdef CONFIG_SCST_EXTRACHECKS if (unlikely((in_irq() || irqs_disabled())) && @@ -570,19 +570,19 @@ int scst_pre_parse(struct scst_cmd *cmd) #else cmd->inc_expected_sn_on_done = devt->exec_sync || (!dev->has_own_order_mgmt && - (dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER || + (dev->queue_alg == SCST_QUEUE_ALG_0_RESTRICTED_REORDER || cmd->queue_type == SCST_CMD_QUEUE_ORDERED)); #endif TRACE_DBG("op_name <%s> (cmd %p), direction=%d " "(expected %d, set %s), lba %lld, bufflen=%d, data_len %lld, " "out_bufflen=%d (expected len %d, out expected len %d), " - "flags=0x%x", cmd->op_name, cmd, cmd->data_direction, + "flags=0x%x, naca %d", cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", (long long)cmd->lba, cmd->bufflen, (long long)cmd->data_len, cmd->out_bufflen, cmd->expected_transfer_len, - cmd->expected_out_transfer_len, cmd->op_flags); + cmd->expected_out_transfer_len, cmd->op_flags, cmd->cmd_naca); res = 0; @@ -700,8 +700,8 @@ static int scst_parse_cmd(struct scst_cmd *cmd) if (unlikely(cmd->cdb_len == 0)) { PRINT_ERROR("Unable to get CDB length for " - "opcode 0x%02x. Returning INVALID " - "OPCODE", cmd->cdb[0]); + "opcode %s. Returning INVALID " + "OPCODE", scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_done; @@ -726,8 +726,9 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } else { if (cmd->bufflen == 0) { PRINT_ERROR("Unknown data transfer length for opcode " - "0x%x (handler %s, target %s)", cmd->cdb[0], - devt->name, cmd->tgtt->name); + "%s (handler %s, target %s)", + scst_get_opcode_name(cmd), devt->name, + cmd->tgtt->name); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_message)); @@ -747,7 +748,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) if (unlikely(cmd->cmd_linked)) { PRINT_ERROR("Linked commands are not supported " - "(opcode 0x%02x)", cmd->cdb[0]); + "(opcode %s)", scst_get_opcode_name(cmd)); scst_set_invalid_field_in_cdb(cmd, cmd->cdb_len-1, SCST_INVAL_FIELD_BIT_OFFS_VALID | 0); goto out_done; @@ -768,9 +769,9 @@ static int scst_parse_cmd(struct scst_cmd *cmd) ((cmd->sg == NULL) && (state > SCST_CMD_STATE_PREPARE_SPACE)))) { PRINT_ERROR("Dev handler %s parse() returned " "invalid cmd data_direction %d, bufflen %d, state %d " - "or sg %p (opcode 0x%x)", devt->name, + "or sg %p (opcode %s)", devt->name, cmd->data_direction, cmd->bufflen, state, cmd->sg, - cmd->cdb[0]); + scst_get_opcode_name(cmd)); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); goto out_hw_error; } @@ -805,10 +806,10 @@ static int scst_parse_cmd(struct scst_cmd *cmd) (cmd->bufflen != 0)) && !scst_is_allowed_to_mismatch_cmd(cmd)) { PRINT_ERROR("Expected data direction %d for " - "opcode 0x%02x (handler %s, target %s) " + "opcode %s (handler %s, target %s) " "doesn't match decoded value %d", cmd->expected_data_direction, - cmd->cdb[0], devt->name, + scst_get_opcode_name(cmd), devt->name, cmd->tgtt->name, cmd->data_direction); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); @@ -819,10 +820,10 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } if (unlikely(cmd->bufflen != cmd->expected_transfer_len)) { TRACE(TRACE_MINOR, "Warning: expected " - "transfer length %d for opcode 0x%02x " + "transfer length %d for opcode %s " "(handler %s, target %s) doesn't match " "decoded value %d", - cmd->expected_transfer_len, cmd->cdb[0], + cmd->expected_transfer_len, scst_get_opcode_name(cmd), devt->name, cmd->tgtt->name, cmd->bufflen); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); @@ -832,12 +833,12 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } if (unlikely(cmd->out_bufflen != cmd->expected_out_transfer_len)) { TRACE(TRACE_MINOR, "Warning: expected bidirectional OUT " - "transfer length %d for opcode 0x%02x " + "transfer length %d for opcode %s " "(handler %s, target %s) doesn't match " "decoded value %d", - cmd->expected_out_transfer_len, cmd->cdb[0], - devt->name, cmd->tgtt->name, - cmd->out_bufflen); + cmd->expected_out_transfer_len, + scst_get_opcode_name(cmd), devt->name, + cmd->tgtt->name, cmd->out_bufflen); PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb, cmd->cdb_len); cmd->resid_possible = 1; @@ -846,16 +847,17 @@ static int scst_parse_cmd(struct scst_cmd *cmd) } if (unlikely(cmd->data_direction == SCST_DATA_UNKNOWN)) { - PRINT_ERROR("Unknown data direction. Opcode 0x%x, handler %s, " - "target %s", cmd->cdb[0], devt->name, + PRINT_ERROR("Unknown data direction (opcode %s, handler %s, " + "target %s)", scst_get_opcode_name(cmd), devt->name, cmd->tgtt->name); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); goto out_hw_error; } if (unlikely(cmd->op_flags & SCST_UNKNOWN_LBA)) { - PRINT_ERROR("Unknown LBA (opcode 0x%x, handler %s, " - "target %s)", cmd->cdb[0], devt->name, cmd->tgtt->name); + PRINT_ERROR("Unknown LBA (opcode %s, handler %s, " + "target %s)", scst_get_opcode_name(cmd), devt->name, + cmd->tgtt->name); PRINT_BUFFER("Failed CDB", cmd->cdb, cmd->cdb_len); goto out_hw_error; } @@ -872,13 +874,13 @@ set_res: TRACE(TRACE_SCSI, "op_name <%s> (cmd %p), direction=%d " "(expected %d, set %s), lba=%lld, bufflen=%d, data len %lld, " "out_bufflen=%d, (expected len %d, out expected len %d), " - "flags=0x%x, internal %d", cmd->op_name, cmd, + "flags=0x%x, internal %d, naca %d", cmd->op_name, cmd, cmd->data_direction, cmd->expected_data_direction, scst_cmd_is_expected_set(cmd) ? "yes" : "no", (unsigned long long)cmd->lba, cmd->bufflen, (long long)cmd->data_len, cmd->out_bufflen, cmd->expected_transfer_len, cmd->expected_out_transfer_len, - cmd->op_flags, cmd->internal); + cmd->op_flags, cmd->internal, cmd->cmd_naca); #ifdef CONFIG_SCST_EXTRACHECKS switch (state) { @@ -893,7 +895,8 @@ set_res: case SCST_CMD_STATE_REAL_EXEC: case SCST_CMD_STATE_PRE_DEV_DONE: case SCST_CMD_STATE_DEV_DONE: - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_XMIT_RESP: case SCST_CMD_STATE_FINISHED: case SCST_CMD_STATE_FINISHED_INTERNAL: @@ -906,12 +909,12 @@ set_res: default: if (state >= 0) { PRINT_ERROR("Dev handler %s parse() returned " - "invalid cmd state %d (opcode %d)", - devt->name, state, cmd->cdb[0]); + "invalid cmd state %d (opcode %s)", + devt->name, state, scst_get_opcode_name(cmd)); } else { PRINT_ERROR("Dev handler %s parse() returned " - "error %d (opcode %d)", devt->name, - state, cmd->cdb[0]); + "error %d (opcode %s)", devt->name, + state, scst_get_opcode_name(cmd)); } goto out_hw_error; } @@ -989,12 +992,10 @@ out: break; } if (abort) { - TRACE_MGMT_DBG("Black hole: aborting cmd %p (op %x, " - "initiator %s)", cmd, cmd->cdb[0], + TRACE_MGMT_DBG("Black hole: aborting cmd %p (op %s, " + "initiator %s)", cmd, scst_get_opcode_name(cmd), sess->initiator_name); - spin_lock_irq(&sess->sess_list_lock); scst_abort_cmd(cmd, NULL, false, false); - spin_unlock_irq(&sess->sess_list_lock); } } @@ -1304,7 +1305,9 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status, break; case SCST_PREPROCESS_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_PREPROCESS_STATUS_ERROR: if (cmd->sense != NULL) @@ -1515,7 +1518,6 @@ void scst_rx_data(struct scst_cmd *cmd, int status, scst_set_rdy_to_xfer_time(cmd); TRACE_DBG("Preferred context: %d", pref_context); - TRACE(TRACE_SCSI, "cmd %p, status %#x", cmd, status); cmd->cmd_hw_pending = 0; @@ -1540,7 +1542,10 @@ void scst_rx_data(struct scst_cmd *cmd, int status, break; #endif - /* Small context optimization */ + /* + * Make sure that the exec phase runs in thread context since + * invoking I/O functions from atomic context is not allowed. + */ if ((pref_context == SCST_CONTEXT_TASKLET) || (pref_context == SCST_CONTEXT_DIRECT_ATOMIC) || ((pref_context == SCST_CONTEXT_SAME) && @@ -1549,6 +1554,7 @@ void scst_rx_data(struct scst_cmd *cmd, int status, break; case SCST_RX_STATUS_ERROR_SENSE_SET: + TRACE(TRACE_SCSI, "cmd %p, RX data error status %#x", cmd, status); if (!cmd->write_not_received_set) scst_cmd_set_write_no_data_received(cmd); scst_set_cmd_abnormal_done_state(cmd); @@ -1556,9 +1562,12 @@ void scst_rx_data(struct scst_cmd *cmd, int status, break; case SCST_RX_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_RX_STATUS_ERROR: + TRACE(TRACE_SCSI, "cmd %p, RX data error status %#x", cmd, status); if (!cmd->write_not_received_set) scst_cmd_set_write_no_data_received(cmd); scst_set_cmd_error(cmd, @@ -1676,7 +1685,9 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd) scst_set_cmd_abnormal_done_state(cmd); goto out; case SCST_PREPROCESS_STATUS_ERROR_FATAL: + set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); set_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags); + cmd->delivery_status = SCST_CMD_DELIVERY_FAILED; /* go through */ case SCST_PREPROCESS_STATUS_ERROR: scst_set_cmd_error(cmd, @@ -1718,7 +1729,7 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result, scst_set_resp_data_len(cmd, cmd->resp_data_len - resid); /* * We ignore write direction residue, because from the - * initiator's POV we already transferred all the data. + * initiator's POV we have already transferred all the data. */ } @@ -1805,11 +1816,12 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state, #ifdef CONFIG_SCST_EXTRACHECKS if ((next_state != SCST_CMD_STATE_PRE_DEV_DONE) && - (next_state != SCST_CMD_STATE_PRE_XMIT_RESP) && + (next_state != SCST_CMD_STATE_PRE_XMIT_RESP1) && + (next_state != SCST_CMD_STATE_PRE_XMIT_RESP2) && (next_state != SCST_CMD_STATE_FINISHED) && (next_state != SCST_CMD_STATE_FINISHED_INTERNAL)) { - PRINT_ERROR("%s() received invalid cmd state %d (opcode %d)", - __func__, next_state, cmd->cdb[0]); + PRINT_ERROR("%s() received invalid cmd state %d (opcode %s)", + __func__, next_state, scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); scst_set_cmd_abnormal_done_state(cmd); @@ -1842,15 +1854,18 @@ static int scst_report_luns_local(struct scst_cmd *cmd) if ((cmd->cdb[2] != 0) && (cmd->cdb[2] != 2)) { PRINT_ERROR("Unsupported SELECT REPORT value %x in REPORT " "LUNS command", cmd->cdb[2]); - goto out_err; + scst_set_invalid_field_in_cdb(cmd, 2, 0); + goto out_compl; } buffer_size = scst_get_buf_full_sense(cmd, &buffer); if (unlikely(buffer_size <= 0)) goto out_compl; - if (buffer_size < 16) + if (buffer_size < 16) { + scst_set_invalid_field_in_cdb(cmd, 6, 0); goto out_put_err; + } memset(buffer, 0, buffer_size); offs = 8; @@ -1928,10 +1943,6 @@ out_compl: out_put_err: scst_put_buf_full(cmd, buffer); - -out_err: - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); goto out_compl; } @@ -2025,8 +2036,8 @@ static int scst_request_sense_local(struct scst_cmd *cmd) sl = tgt_dev->tgt_dev_valid_sense_len; else { sl = buffer_size; - TRACE(TRACE_MINOR, "%s: Being returned sense truncated " - "to size %d (needed %d)", cmd->op_name, + TRACE(TRACE_SCSI|TRACE_MINOR, "%s: Being returned sense " + "truncated to size %d (needed %d)", cmd->op_name, buffer_size, tgt_dev->tgt_dev_valid_sense_len); } memcpy(buffer, tgt_dev->tgt_dev_sense, sl); @@ -2124,20 +2135,15 @@ static int scst_report_supported_opcodes(struct scst_cmd *cmd) int req_sa = get_unaligned_be16(&cmd->cdb[4]); const struct scst_opcode_descriptor *op = NULL; const struct scst_opcode_descriptor **supp_opcodes = NULL; - int supp_opcodes_cnt; + int supp_opcodes_cnt, rc; TRACE_ENTRY(); - if (cmd->devt->get_supported_opcodes == NULL) { - TRACE(TRACE_MINOR, "Unknown opcode 0x%02x", cmd->cdb[0]); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + /* get_cdb_info_min() ensures that get_supported_opcodes is not NULL here */ + + rc = cmd->devt->get_supported_opcodes(cmd, &supp_opcodes, &supp_opcodes_cnt); + if (rc != 0) goto out_compl; - } else { - int rc = cmd->devt->get_supported_opcodes(cmd, &supp_opcodes, - &supp_opcodes_cnt); - if (rc != 0) - goto out_compl; - } TRACE_DBG("cmd %p, options %d, req_opcode %x, req_sa %x, rctd %d", cmd, options, req_opcode, req_sa, rctd); @@ -2243,7 +2249,7 @@ static int scst_report_supported_opcodes(struct scst_cmd *cmd) switch (options) { case 0: /* all */ - put_unaligned_be32(buf_len - 3, &buf[0]); + put_unaligned_be32(buf_len - 4, &buf[0]); offs = 4; for (i = 0; i < supp_opcodes_cnt; i++) { op = supp_opcodes[i]; @@ -2354,7 +2360,7 @@ static int scst_reserve_local(struct scst_cmd *cmd) /* * There's no need to block this device, even for - * SCST_CONTR_MODE_ONE_TASK_SET, or anyhow else protect reservations + * SCST_TST_0_SINGLE_TASK_SET, or anyhow else protect reservations * changes, because: * * 1. The reservation changes are (rather) atomic, i.e., in contrast @@ -2363,7 +2369,7 @@ static int scst_reserve_local(struct scst_cmd *cmd) * * 2. It's a duty of initiators to ensure order of regular commands * around the reservation command either by ORDERED attribute, or by - * queue draining, or etc. For case of SCST_CONTR_MODE_ONE_TASK_SET + * queue draining, or etc. For case of SCST_TST_0_SINGLE_TASK_SET * there are no target drivers which can ensure even for ORDERED * commands order of their delivery, so, because initiators know * it, also there's no point to do any extra protection actions. @@ -2487,9 +2493,10 @@ static int scst_persistent_reserve_in_local(struct scst_cmd *cmd) session = cmd->sess; if (unlikely(dev->not_pr_supporting_tgt_devs_num != 0)) { - PRINT_WARNING("Persistent Reservation command %x refused for " + PRINT_WARNING("Persistent Reservation command %s refused for " "device %s, because the device has not supporting PR " - "transports connected", cmd->cdb[0], dev->virt_name); + "transports connected", scst_get_opcode_name(cmd), + dev->virt_name); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_done; @@ -2524,8 +2531,8 @@ static int scst_persistent_reserve_in_local(struct scst_cmd *cmd) action = cmd->cdb[1] & 0x1f; - TRACE(TRACE_SCSI, "PR action %x for '%s' (LUN %llx) from '%s'", action, - dev->virt_name, tgt_dev->lun, session->initiator_name); + TRACE(TRACE_SCSI, "PR IN action %x for '%s' (LUN %llx) from '%s'", + action, dev->virt_name, tgt_dev->lun, session->initiator_name); switch (action) { case PR_READ_KEYS: @@ -2586,18 +2593,18 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) session = cmd->sess; if (unlikely(dev->not_pr_supporting_tgt_devs_num != 0)) { - PRINT_WARNING("Persistent Reservation command %x refused for " + PRINT_WARNING("Persistent Reservation command %s refused for " "device %s, because the device has not supporting PR " - "transports connected", cmd->cdb[0], dev->virt_name); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + "transports connected", scst_get_opcode_name(cmd), + dev->virt_name); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_done; } action = cmd->cdb[1] & 0x1f; - TRACE(TRACE_SCSI, "PR action %x for '%s' (LUN %llx) from '%s'", action, - dev->virt_name, tgt_dev->lun, session->initiator_name); + TRACE(TRACE_SCSI, "PR OUT action %x for '%s' (LUN %llx) from '%s'", + action, dev->virt_name, tgt_dev->lun, session->initiator_name); if (scst_dev_reserved(dev)) { TRACE_PR("PR command rejected, because device %s holds regular " @@ -2630,8 +2637,8 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && (action != PR_CLEAR) && (cmd->cdb[2] >> 4) != SCOPE_LU) { TRACE_PR("Scope must be SCOPE_LU for action %x", action); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_cdb(cmd, 2, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 4); goto out_unlock; } @@ -2639,8 +2646,8 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_MOVE) && ((buffer[20] >> 3) & 0x01)) { TRACE_PR("SPEC_I_PT must be zero for action %x", action); - scst_set_cmd_error(cmd, SCST_LOAD_SENSE( - scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_parm_list(cmd, 20, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 3); goto out_unlock; } @@ -2648,8 +2655,8 @@ static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && (action != PR_REGISTER_AND_MOVE) && ((buffer[20] >> 2) & 0x01)) { TRACE_PR("ALL_TG_PT must be zero for action %x", action); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + scst_set_invalid_field_in_parm_list(cmd, 20, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 2); goto out_unlock; } @@ -3000,10 +3007,11 @@ static int scst_do_real_exec(struct scst_cmd *cmd) if (rc == -EINVAL && (cmd->bufflen >> 9) > queue_max_hw_sectors(scsi_dev->request_queue)) PRINT_ERROR("Too low max_hw_sectors %d sectors on %s " - "to serve command %#x with bufflen %d bytes." + "to serve command %s with bufflen %d bytes." "See README for more details.", queue_max_hw_sectors(scsi_dev->request_queue), - dev->virt_name, cmd->cdb[0], cmd->bufflen); + dev->virt_name, scst_get_opcode_name(cmd), + cmd->bufflen); goto out_error; } @@ -3090,8 +3098,9 @@ static int scst_do_local_exec(struct scst_cmd *cmd) if ((cmd->op_flags & SCST_WRITE_MEDIUM) && (tgt_dev->tgt_dev_rd_only || cmd->dev->swp)) { PRINT_WARNING("Attempt of write access to read-only device: " - "initiator %s, LUN %lld, op %x", - cmd->sess->initiator_name, cmd->lun, cmd->cdb[0]); + "initiator %s, LUN %lld, op %s", + cmd->sess->initiator_name, cmd->lun, + scst_get_opcode_name(cmd)); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_data_protect)); goto out_done; @@ -3294,7 +3303,7 @@ static int scst_exec_check_sn(struct scst_cmd **active_cmd) if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) goto exec; - sBUG_ON(!cmd->sn_set); + EXTRACHECKS_BUG_ON(!cmd->sn_set); expected_sn = ACCESS_ONCE(order_data->expected_sn); /* Optimized for lockless fast path */ @@ -3330,7 +3339,7 @@ static int scst_exec_check_sn(struct scst_cmd **active_cmd) TRACE_SN("Deferring cmd %p (sn=%d, set %d, " "expected_sn=%d)", cmd, cmd->sn, cmd->sn_set, expected_sn); - list_add_tail(&cmd->sn_cmd_list_entry, + list_add_tail(&cmd->deferred_cmd_list_entry, &order_data->deferred_cmd_list); res = SCST_CMD_STATE_RES_CONT_NEXT; } @@ -3360,8 +3369,11 @@ static int scst_check_sense(struct scst_cmd *cmd) TRACE_ENTRY(); - if (unlikely(cmd->ua_ignore)) + if (unlikely(cmd->ua_ignore)) { + PRINT_BUFF_FLAG(TRACE_SCSI, "Local UA sense", cmd->sense, + cmd->sense_valid_len); goto out; + } /* If we had internal bus reset behind us, set the command error UA */ if ((dev->scsi_dev != NULL) && @@ -3466,8 +3478,8 @@ static bool scst_check_auto_sense(struct scst_cmd *cmd) } else { TRACE(TRACE_SCSI|TRACE_MINOR_AND_MGMT_DBG, "Host " "status 0x%x received, returning HARDWARE ERROR " - "instead (cmd %p, op 0x%x, target %s, device " - "%s)", cmd->host_status, cmd, cmd->cdb[0], + "instead (cmd %p, op %s, target %s, device " + "%s)", cmd->host_status, cmd, scst_get_opcode_name(cmd), cmd->tgt->tgt_name, cmd->dev->virt_name); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); @@ -3484,10 +3496,11 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) TRACE_ENTRY(); - if (unlikely(scst_check_auto_sense(cmd))) { + rc = scst_check_auto_sense(cmd); + if (unlikely(rc)) { PRINT_INFO("Command finished with CHECK CONDITION, but " - "without sense data (opcode 0x%x), issuing " - "REQUEST SENSE", cmd->cdb[0]); + "without sense data (opcode %s), issuing " + "REQUEST SENSE", scst_get_opcode_name(cmd)); rc = scst_prepare_request_sense(cmd); if (rc == 0) res = SCST_CMD_STATE_RES_CONT_NEXT; @@ -3498,7 +3511,10 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) SCST_LOAD_SENSE(scst_sense_hardw_error)); } goto out; - } else if (unlikely(scst_check_sense(cmd))) { + } + + rc = scst_check_sense(cmd); + if (unlikely(rc)) { /* * We can't allow atomic command on the exec stages, so * restart to the thread @@ -3507,7 +3523,8 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) goto out; } - if (likely(scsi_status_is_good(cmd->status))) { + rc = scsi_status_is_good(cmd->status); + if (likely(rc)) { unsigned char type = cmd->dev->type; if (unlikely((cmd->cdb[0] == MODE_SENSE || cmd->cdb[0] == MODE_SENSE_10)) && @@ -3580,33 +3597,12 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) if (unlikely((cmd->cdb[0] == MODE_SELECT) || (cmd->cdb[0] == MODE_SELECT_10) || (cmd->cdb[0] == LOG_SELECT))) { - TRACE(TRACE_SCSI, - "MODE/LOG SELECT succeeded (LUN %lld)", + TRACE(TRACE_SCSI, "MODE/LOG SELECT succeeded (LUN %lld)", (long long unsigned int)cmd->lun); cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS; goto out; } } else { - TRACE(TRACE_SCSI, "cmd %p not succeeded with status %x", - cmd, cmd->status); - - if ((cmd->cdb[0] == RESERVE) || (cmd->cdb[0] == RESERVE_10)) { - struct scst_device *dev = cmd->dev; - - if (scst_is_reservation_holder(dev, cmd->sess)) { - TRACE(TRACE_SCSI, "RESERVE failed lun=%lld, " - "status=%x", - (long long unsigned int)cmd->lun, - cmd->status); - PRINT_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense, - cmd->sense_valid_len); - - spin_lock_bh(&dev->dev_lock); - scst_clear_dev_reservation(dev); - spin_unlock_bh(&dev->dev_lock); - } - } - /* Check for MODE PARAMETERS CHANGED UA */ if ((cmd->dev->scsi_dev != NULL) && (cmd->status == SAM_STAT_CHECK_CONDITION) && @@ -3719,7 +3715,7 @@ static int scst_dev_done(struct scst_cmd *cmd) TRACE_ENTRY(); - state = SCST_CMD_STATE_PRE_XMIT_RESP; + state = SCST_CMD_STATE_PRE_XMIT_RESP1; if (likely((cmd->op_flags & SCST_FULLY_LOCAL_CMD) == 0) && likely(devt->dev_done != NULL)) { @@ -3750,7 +3746,8 @@ static int scst_dev_done(struct scst_cmd *cmd) switch (state) { #ifdef CONFIG_SCST_EXTRACHECKS - case SCST_CMD_STATE_PRE_XMIT_RESP: + case SCST_CMD_STATE_PRE_XMIT_RESP1: + case SCST_CMD_STATE_PRE_XMIT_RESP2: case SCST_CMD_STATE_PARSE: case SCST_CMD_STATE_PREPARE_SPACE: case SCST_CMD_STATE_RDY_TO_XFER: @@ -3805,7 +3802,8 @@ static int scst_dev_done(struct scst_cmd *cmd) cmd->state = SCST_CMD_STATE_FINISHED_INTERNAL; #ifndef CONFIG_SCST_TEST_IO_IN_SIRQ - if (cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) { +#ifdef CONFIG_SCST_EXTRACHECKS + if (cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP1) { /* We can't allow atomic command on the exec stages */ if (scst_cmd_atomic(cmd)) { switch (state) { @@ -3822,13 +3820,58 @@ static int scst_dev_done(struct scst_cmd *cmd) } } #endif +#endif out: TRACE_EXIT_HRES(res); return res; } -static int scst_pre_xmit_response(struct scst_cmd *cmd) +static int scst_pre_xmit_response2(struct scst_cmd *cmd) +{ + int res; + + TRACE_ENTRY(); + +again: + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) + scst_xmit_process_aborted_cmd(cmd); + else if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) { + if (cmd->tgt_dev != NULL) { + int rc = scst_process_check_condition(cmd); + /* !! At this point cmd can be already dead !! */ + if (rc == -1) { + res = SCST_CMD_STATE_RES_CONT_NEXT; + goto out; + } else if (rc == 1) + goto again; + } + } + + if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { + EXTRACHECKS_BUG_ON(!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)); + TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), " + "skipping", cmd, (long long unsigned int)cmd->tag); + cmd->state = SCST_CMD_STATE_FINISHED; + goto out_same; + } + + if (unlikely(cmd->resid_possible)) + scst_adjust_resp_data_len(cmd); + else + cmd->adjusted_resp_data_len = cmd->resp_data_len; + + cmd->state = SCST_CMD_STATE_XMIT_RESP; + +out_same: + res = SCST_CMD_STATE_RES_CONT_SAME; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_pre_xmit_response1(struct scst_cmd *cmd) { int res; @@ -3838,7 +3881,7 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd) #ifdef CONFIG_SCST_DEBUG_TM if (cmd->tm_dbg_delayed && - !test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + !test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { if (scst_cmd_atomic(cmd)) { TRACE_MGMT_DBG("%s", "DEBUG_TM delayed cmd needs a thread"); @@ -3876,29 +3919,10 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd) cmd->done = 1; smp_mb(); /* to sync with scst_abort_cmd() */ - if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) - scst_xmit_process_aborted_cmd(cmd); - else if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) - scst_store_sense(cmd); + cmd->state = SCST_CMD_STATE_PRE_XMIT_RESP2; + res = scst_pre_xmit_response2(cmd); - if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { - TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), " - "skipping", cmd, (long long unsigned int)cmd->tag); - cmd->state = SCST_CMD_STATE_FINISHED; - res = SCST_CMD_STATE_RES_CONT_SAME; - goto out; - } - - if (unlikely(cmd->resid_possible)) - scst_adjust_resp_data_len(cmd); - else - cmd->adjusted_resp_data_len = cmd->resp_data_len; - - cmd->state = SCST_CMD_STATE_XMIT_RESP; - res = SCST_CMD_STATE_RES_CONT_SAME; - -out: - TRACE_EXIT_HRES(res); + TRACE_EXIT_RES(res); return res; } @@ -4069,6 +4093,7 @@ static int scst_finish_cmd(struct scst_cmd *cmd) if (unlikely(cmd->delivery_status != SCST_CMD_DELIVERY_SUCCESS)) { if ((cmd->tgt_dev != NULL) && + (cmd->status == SAM_STAT_CHECK_CONDITION) && scst_is_ua_sense(cmd->sense, cmd->sense_valid_len)) { /* This UA delivery failed, so we need to requeue it */ if (scst_cmd_atomic(cmd) && @@ -4173,7 +4198,7 @@ static void scst_cmd_set_sn(struct scst_cmd *cmd) cmd->queue_type = SCST_CMD_QUEUE_ORDERED; #endif - if (cmd->dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) { + if (cmd->dev->queue_alg == SCST_QUEUE_ALG_0_RESTRICTED_REORDER) { if (likely(cmd->queue_type != SCST_CMD_QUEUE_HEAD_OF_QUEUE)) { /* * Not the best way, but good enough until there is a @@ -4228,7 +4253,7 @@ again: goto again; case SCST_CMD_QUEUE_ORDERED: - TRACE_SN("ORDERED cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE_SN("ORDERED cmd %p (op %s)", cmd, scst_get_opcode_name(cmd)); ordered: order_data->curr_sn++; TRACE_SN("Incremented curr_sn %d", order_data->curr_sn); @@ -4271,7 +4296,7 @@ ordered: break; case SCST_CMD_QUEUE_HEAD_OF_QUEUE: - TRACE_SN("HQ cmd %p (op %x)", cmd, cmd->cdb[0]); + TRACE_SN("HQ cmd %p (op %s)", cmd, scst_get_opcode_name(cmd)); spin_lock_irqsave(&order_data->sn_lock, flags); order_data->hq_cmd_count++; spin_unlock_irqrestore(&order_data->sn_lock, flags); @@ -4762,10 +4787,14 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) res = scst_dev_done(cmd); break; - case SCST_CMD_STATE_PRE_XMIT_RESP: - res = scst_pre_xmit_response(cmd); - EXTRACHECKS_BUG_ON(res == - SCST_CMD_STATE_RES_NEED_THREAD); + case SCST_CMD_STATE_PRE_XMIT_RESP1: + res = scst_pre_xmit_response1(cmd); + EXTRACHECKS_BUG_ON(res == SCST_CMD_STATE_RES_NEED_THREAD); + break; + + case SCST_CMD_STATE_PRE_XMIT_RESP2: + res = scst_pre_xmit_response2(cmd); + EXTRACHECKS_BUG_ON(res == SCST_CMD_STATE_RES_NEED_THREAD); break; case SCST_CMD_STATE_XMIT_RESP: @@ -4784,8 +4813,11 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) PRINT_CRIT_ERROR("cmd (%p) in state %d, but shouldn't " "be", cmd, cmd->state); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 + /* For suppressing a gcc compiler warning */ res = SCST_CMD_STATE_RES_CONT_NEXT; break; +#endif } } while (res == SCST_CMD_STATE_RES_CONT_SAME); @@ -4817,8 +4849,10 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) cmd->state); spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 spin_lock_irq(&cmd->cmd_threads->cmd_list_lock); break; +#endif } #endif wake_up(&cmd->cmd_threads->cmd_list_waitQ); @@ -4918,21 +4952,16 @@ void scst_cmd_tasklet(long p) } /* - * Returns 0 on success, < 0 if there is no device handler or - * > 0 if SCST_FLAG_SUSPENDED set and SCST_FLAG_SUSPENDING - not. - * No locks, protection is done by the suspended activity. + * Returns 0 on success, or > 0 if SCST_FLAG_SUSPENDED set and + * SCST_FLAG_SUSPENDING - not. No locks, protection is done by the + * suspended activity. */ -static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) +static int scst_get_mgmt(struct scst_mgmt_cmd *mcmd) { - struct scst_tgt_dev *tgt_dev; - struct list_head *head; - int res = -1; + int res = 0; TRACE_ENTRY(); - TRACE_DBG("Finding tgt_dev for mgmt cmd %p (lun %lld)", mcmd, - (long long unsigned int)mcmd->lun); - mcmd->cpu_cmd_counter = scst_get(); if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags) && @@ -4943,6 +4972,33 @@ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) goto out; } +out: + TRACE_EXIT_HRES(res); + return res; +} + +/* + * Returns 0 on success, < 0 if there is no device handler or + * > 0 if SCST_FLAG_SUSPENDED set and SCST_FLAG_SUSPENDING - not. + * No locks, protection is done by the suspended activity. + */ +static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) +{ + struct scst_tgt_dev *tgt_dev; + struct list_head *head; + int res; + + TRACE_ENTRY(); + + TRACE_DBG("Finding tgt_dev for mgmt cmd %p (lun %lld)", mcmd, + (long long unsigned int)mcmd->lun); + + res = scst_get_mgmt(mcmd); + if (unlikely(res != 0)) + goto out; + + res = -1; + head = &mcmd->sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(mcmd->lun)]; list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { if (tgt_dev->lun == mcmd->lun) { @@ -5208,8 +5264,8 @@ static inline int scst_is_strict_mgmt_fn(int mgmt_fn) } /* - * Must be called under sess_list_lock to sync with finished flag assignment in - * scst_finish_cmd() + * If mcmd != NULL, must be called under sess_list_lock to sync with "finished" + * flag assignment in scst_finish_cmd() */ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, bool other_ini, bool call_dev_task_mgmt_fn_received) @@ -5226,8 +5282,8 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, if (call_dev_task_mgmt_fn_received) EXTRACHECKS_BUG_ON(!mcmd); - TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "Aborting cmd %p (tag %llu, op %x)", - cmd, (long long unsigned int)cmd->tag, cmd->cdb[0]); + TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "Aborting cmd %p (tag %llu, op %s)", + cmd, (long long unsigned int)cmd->tag, scst_get_opcode_name(cmd)); /* To protect from concurrent aborts */ spin_lock_irqsave(&other_ini_lock, flags); @@ -5331,12 +5387,12 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, t = TRACE_MGMT_DEBUG; TRACE(t, "cmd %p (tag %llu, " "sn %u) being executed/xmitted (state %d, " - "op %x, proc time %ld sec., timeout %d sec.), " + "op %s, proc time %ld sec., timeout %d sec.), " "deferring ABORT (cmd_done_wait_count %d, " "cmd_finish_wait_count %d, internal %d, mcmd " "fn %d (mcmd %p))", cmd, (long long unsigned int)cmd->tag, - cmd->sn, cmd->state, cmd->cdb[0], + cmd->sn, cmd->state, scst_get_opcode_name(cmd), (long)(jiffies - cmd->start_time) / HZ, cmd->timeout / HZ, mcmd->cmd_done_wait_count, mcmd->cmd_finish_wait_count, cmd->internal, @@ -5484,7 +5540,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, spin_lock(&order_data->sn_lock); list_for_each_entry_safe(cmd, tcmd, &order_data->deferred_cmd_list, - sn_cmd_list_entry) { + deferred_cmd_list_entry) { if ((tgt != NULL) && (tgt != cmd->tgt)) continue; @@ -5492,7 +5548,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt, continue; if (__scst_check_unblock_aborted_cmd(cmd, - &cmd->sn_cmd_list_entry)) { + &cmd->deferred_cmd_list_entry)) { TRACE_MGMT_DBG("Unblocked aborted SN " "cmd %p (sn %u)", cmd, cmd->sn); order_data->def_cmd_count--; @@ -5679,6 +5735,18 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd) list_for_each_entry(tgt_dev, &UA_tgt_devs, extra_tgt_dev_list_entry) { + /* + * Potentially, setting UA here, when the aborted + * commands are still running, can lead to a situation + * that one of them could take it, then that would be + * detected and the UA requeued. But, meanwhile, one or + * more subsequent, i.e. not aborted, commands can + * "leak" executed normally. So, as result, the + * UA would be delivered one or more commands "later". + * However, that should be OK, because, if multiple + * commands are being executed in parallel, you can't + * control exact order of UA delivery anyway. + */ scst_check_set_UA(tgt_dev, sense_buffer, sl, 0); } } @@ -5742,6 +5810,21 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) } case SCST_TARGET_RESET: + /* + * Needed to protect against race, when a device added after + * blocking, so unblocking then will make dev->block_count + * of the new device negative. + */ + rc = scst_get_mgmt(mcmd); + if (rc == 0) { + mcmd->state = SCST_MCMD_STATE_EXEC; + mcmd->scst_get_called = 1; + } else { + EXTRACHECKS_BUG_ON(rc < 0); + res = rc; + } + break; + case SCST_NEXUS_LOSS_SESS: case SCST_ABORT_ALL_TASKS_SESS: case SCST_NEXUS_LOSS: @@ -5950,7 +6033,7 @@ static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd) /* Returns 0 if the command processing should be continued, <0 otherwise */ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd, - int nexus_loss) + int nexus_loss_unreg_sess) { int res; int i; @@ -5959,8 +6042,8 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd, TRACE_ENTRY(); - if (nexus_loss) { - TRACE_MGMT_DBG("Nexus loss for sess %p (mcmd %p)", + if (nexus_loss_unreg_sess) { + TRACE_MGMT_DBG("Nexus loss or UNREG SESS for sess %p (mcmd %p)", sess, mcmd); } else { TRACE_MGMT_DBG("Aborting all from sess %p (mcmd %p)", @@ -6125,8 +6208,7 @@ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd) break; case SCST_CLEAR_TASK_SET: - if (mcmd->mcmd_tgt_dev->dev->tst == - SCST_CONTR_MODE_SEP_TASK_SETS) + if (mcmd->mcmd_tgt_dev->dev->tst == SCST_TST_1_SEP_TASK_SETS) res = scst_abort_task_set(mcmd); else res = scst_clear_task_set(mcmd); @@ -6416,8 +6498,11 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd) mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count); sBUG(); +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 + /* For suppressing a gcc compiler warning */ res = -1; goto out; +#endif } } diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t index 28db54d06..10f88b048 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t @@ -274,10 +274,10 @@ sub lunTest { Dumper(undef, "luns(): Target 'no-such-target' is not available")); ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ '0' => 'disk01', '1' => 'disk02', '2' => 'disk01', - '3' => 'disk02' }, undef)); + '3' => 'disk02' })); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), Dumper({ '0' => 'disk01', '2' => 'disk02', '4' => 'disk01', - '6' => 'disk02' }, undef)); + '6' => 'disk02' })); ok(lunReadOnly($SCST, 'scst_local', 'local1', 0), '0'); ok($SCST->setLunAttribute(), $SCST->SCST_C_LUN_SETATTR_FAIL); @@ -302,11 +302,11 @@ sub lunTest { ok($SCST->replaceLun('scst_local', 'local1', 0, 'disk02', {}), 0); ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ '0' => 'disk02', '1' => 'disk02', '2' => 'disk01', - '3' => 'disk02' }, undef)); + '3' => 'disk02' })); ok($SCST->replaceLun('scst_local', 'local1', 0, 'disk01', {}), 0); ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ '0' => 'disk01', '1' => 'disk02', '2' => 'disk01', - '3' => 'disk02' }, undef)); + '3' => 'disk02' })); ok($SCST->clearLuns(undef, undef), $SCST->SCST_C_TGT_CLR_LUN_FAIL); ok($SCST->clearLuns(undef, undef, 'group1'), @@ -322,16 +322,16 @@ sub lunTest { ok($SCST->removeLun('scst_local', 'local1', '8', 'group1'), $SCST->SCST_C_GRP_NO_LUN); ok($SCST->clearLuns('scst_local', 'local1'), 0); - ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ }, undef)); + ok(Dumper($SCST->luns('scst_local', 'local1')), Dumper({ })); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), Dumper({ '0' => 'disk01', '2' => 'disk02', '4' => 'disk01', - '6' => 'disk02' }, undef)); + '6' => 'disk02' })); ok($SCST->removeLun('scst_local', 'local1', '4', 'group1'), 0); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), - Dumper({ '0' => 'disk01', '2' => 'disk02', '6' => 'disk02' }, undef)); + Dumper({ '0' => 'disk01', '2' => 'disk02', '6' => 'disk02' })); ok($SCST->clearLuns('scst_local', 'local1', 'group1'), 0); ok(Dumper($SCST->luns('scst_local', 'local1', 'group1')), - Dumper({ }, undef)); + Dumper({ })); ok($SCST->removeInitiator('scst_local', 'local1', 'group1', 'ini1'), 0); ok($SCST->removeGroup('scst_local', 'local1', 'group1'), 0); diff --git a/srpt/Makefile b/srpt/Makefile index 2f0e28c96..a97d930df 100644 --- a/srpt/Makefile +++ b/srpt/Makefile @@ -47,19 +47,30 @@ SRC_FILES=$(wildcard */*.[ch]) MODULE_SYMVERS:=$(shell if [ -e $(KDIR)/Module.symvers ]; then \ echo Module.symvers; else echo Modules.symvers; fi) -# Whether or not the OFED kernel modules have been installed. -OFED_KERNEL_IB_RPM_INSTALLED:=$(shell if rpm -q kernel-ib 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi) +# Name of the OFED kernel RPM. +OFED_KERNEL_IB_RPM:=$(shell for r in kernel-ib mlnx-ofa_kernel compat-rdma; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) -# Whether or not the OFED kernel-ib-devel RPM has been installed. -OFED_KERNEL_IB_DEVEL_RPM_INSTALLED:=$(shell if rpm -q kernel-ib-devel 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi) +# Name of the OFED kernel development RPM. +OFED_KERNEL_IB_DEVEL_RPM:=$(shell for r in kernel-ib-devel mlnx-ofa_kernel-devel compat-rdma-devel; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) -ifeq ($(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED),true) -# Read OFED's config.mk, which contains the definition of the variable +ifeq ($(OFED_KERNEL_IB_RPM),kernel-ib) +OFED_KERNEL_DIR:=/usr/src/ofa_kernel +# Read OFED 1.x's config.mk, which contains the definition of the variable # BACKPORT_INCLUDES. -include /usr/src/ofa_kernel/config.mk +include $(OFED_KERNEL_DIR)/config.mk +OFED_CFLAGS:=$(BACKPORT_INCLUDES) -I$(OFED_KERNEL_DIR)/include +endif +ifeq ($(OFED_KERNEL_IB_RPM),mlnx-ofa_kernel) +OFED_KERNEL_DIR:=/usr/src/ofa_kernel/default +OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/default/include +endif +ifeq ($(OFED_KERNEL_IB_RPM),compat-rdma) +OFED_KERNEL_DIR:=/usr/src/compat-rdma +OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/include +endif +ifneq ($(OFED_KERNEL_IB_RPM),) +OFED_MODULE_SYMVERS:=$(OFED_KERNEL_DIR)/Module.symvers endif - -OFED_CFLAGS:=$(shell if $(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED); then echo $(BACKPORT_INCLUDES) -I/usr/src/ofa_kernel/include; fi) # Path of the OFED ib_srpt.ko kernel module. OFED_SRPT_PATH:=/lib/modules/$(KVER)/updates/kernel/drivers/infiniband/ulp/srpt/ib_srpt.ko @@ -82,14 +93,15 @@ uninstall: -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) - @if $(OFED_KERNEL_IB_RPM_INSTALLED); then \ - if ! $(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED); then \ - echo "Error: the OFED package kernel-ib-devel has not yet been" \ - "installed."; \ + @if [ -n "$(OFED_KERNEL_IB_RPM)" ]; then \ + if [ -z "$(OFED_KERNEL_IB_DEVEL_RPM)" ]; then \ + echo "Error: the OFED package $(OFED_KERNEL_IB_RPM)-devel has" \ + "not yet been installed."; \ false; \ elif [ -e /lib/modules/$(KVER)/kernel/drivers/infiniband ]; then \ echo "Error: the distro-provided InfiniBand kernel drivers" \ - "must be removed first."; \ + "must be removed first" \ + " (/lib/modules/$(KVER)/kernel/drivers/infiniband)."; \ false; \ elif $(OFED_SRPT_INSTALLED); then \ echo "Error: OFED has been built with srpt=y in ofed.conf."; \ @@ -97,21 +109,22 @@ src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) false; \ elif [ -e $(KDIR)/scripts/Makefile.lib ] \ && ! grep -wq '^c_flags .*PRE_CFLAGS' \ - $(KDIR)/scripts/Makefile.lib \ + $(KDIR)/scripts/Makefile.lib \ && ! grep -wq '^LINUXINCLUDE .*PRE_CFLAGS' \ - $(KDIR)/Makefile; then \ + $(KDIR)/Makefile; then \ echo "Error: the kernel build system has not yet been patched.";\ false; \ else \ - echo " Building against OFED InfiniBand kernel headers."; \ + echo " Building against $(OFED_KERNEL_IB_RPM) InfiniBand" \ + "kernel headers."; \ ( \ grep -v drivers/infiniband/ $<; \ - cat /usr/src/ofa_kernel/Module.symvers \ + cat $(OFED_MODULE_SYMVERS) \ ) >$@; \ fi \ else \ - if $(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED); then \ - echo "Error: the OFED package kernel-ib has not yet been" \ + if [ -n "$(OFED_KERNEL_IB_DEVEL_RPM)" ]; then \ + echo "Error: the OFED kernel package has not yet been" \ "installed."; \ false; \ else \ diff --git a/srpt/README b/srpt/README index f605758b8..f5940962c 100644 --- a/srpt/README +++ b/srpt/README @@ -156,7 +156,7 @@ When using RoCE or iWARP, log in to the target system to determine the id_ext and ioc_guid parameters and use these to log in. An example: [ target system ] - # sed 's/,\(pkey\|dgid\|ioc_guid\)=[^,]*//g' $(find /sys/kernel/scst_tgt/targets/ib_srpt -name login_info) | uniq + # sed 's/,\(pkey\|dgid\|service_id\)=[^,]*//g' $(find /sys/kernel/scst_tgt/targets/ib_srpt -name login_info) | uniq id_ext=0002c90300a34270,ioc_guid=0002c90300a34270 [ initiator system ] diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 17ad77e70..62acead56 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -335,6 +335,8 @@ static const char *get_ch_state_name(enum rdma_ch_state s) return "live"; case CH_DISCONNECTING: return "disconnecting"; + case CH_DRAINING: + return "draining"; case CH_DISCONNECTED: return "disconnected"; } @@ -2175,10 +2177,57 @@ static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) ib_destroy_cq(ch->cq); } +/** + * srpt_close_ch() - Close an RDMA channel. + * + * Make sure all resources associated with the channel will be deallocated at + * an appropriate time. + * + * Returns true if and only if the channel state has been modified into + * CH_DRAINING. + */ +static bool srpt_close_ch(struct srpt_rdma_ch *ch) +{ + int ret; + + if (!srpt_set_ch_state(ch, CH_DRAINING)) + return false; + + kref_get(&ch->kref); + + ret = srpt_ch_qp_err(ch); + if (ret < 0) + PRINT_ERROR("%s: changing queue pair into error state" + " failed: %d", ch->sess_name, ret); + + ret = srpt_zerolength_write(ch); + if (ret < 0) { + PRINT_ERROR("%s: queuing zero-length write failed: %d", + ch->sess_name, ret); + WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); + } + + kref_put(&ch->kref, srpt_free_ch); + + return true; +} + +/* + * Change the channel state into CH_DISCONNECTING. If a channel has not yet + * reached the connected state, close it. If a channel is in the connected + * state, send a DREQ. If a DREQ has been received, send a DREP. Note: it is + * the responsibility of the caller to ensure that this function is not + * invoked concurrently with the code that accepts a connection. This means + * that this function must either be invoked from inside a CM callback + * function or that it must be invoked with the srpt_tgt.mutex held. + */ static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) { int ret; + if (!srpt_set_ch_state(ch, CH_DISCONNECTING)) + return -ENOTCONN; + if (ch->using_rdma_cm) { ret = rdma_disconnect(ch->rdma_cm.cm_id); } else { @@ -2187,45 +2236,12 @@ static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0); } + if (ret < 0 && srpt_close_ch(ch)) + ret = 0; + return ret; } -/** - * srpt_close_ch() - Close an RDMA channel. - * - * Make sure all resources associated with the channel will be deallocated at - * an appropriate time. - * - * Returns true if and only if the channel state has been modified from - * CH_CONNECTING or CH_LIVE into CH_DISCONNECTING. - */ -static bool srpt_close_ch(struct srpt_rdma_ch *ch) -{ - int ret; - bool was_live; - - was_live = srpt_set_ch_state(ch, CH_DISCONNECTING); - if (was_live) { - kref_get(&ch->kref); - - ret = srpt_ch_qp_err(ch); - if (ret < 0) - PRINT_ERROR("%s: changing queue pair into error state" - " failed: %d", ch->sess_name, ret); - - ret = srpt_zerolength_write(ch); - if (ret < 0) { - PRINT_ERROR("%s: queuing zero-length write failed: %d", - ch->sess_name, ret); - WARN_ON_ONCE(!srpt_set_ch_state(ch, CH_DISCONNECTED)); - } - - kref_put(&ch->kref, srpt_free_ch); - } - - return was_live; -} - static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) { struct srpt_nexus *nexus; @@ -2354,7 +2370,8 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, struct ib_cm_id *ib_cm_id, struct rdma_cm_id *rdma_cm_id, u8 port_num, __be16 pkey, - const struct srp_login_req *req) + const struct srp_login_req *req, + const char *src_addr) { struct srpt_port *const sport = &sdev->port[port_num - 1]; const __be16 *const raw_port_gid = (__be16 *)sport->gid.raw; @@ -2514,16 +2531,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, } if (one_target_per_port) { - snprintf(ch->sess_name, sizeof(ch->sess_name), - "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", - be16_to_cpu(raw_port_gid[0]), - be16_to_cpu(raw_port_gid[1]), - be16_to_cpu(raw_port_gid[2]), - be16_to_cpu(raw_port_gid[3]), - be16_to_cpu(raw_port_gid[4]), - be16_to_cpu(raw_port_gid[5]), - be16_to_cpu(raw_port_gid[6]), - be16_to_cpu(raw_port_gid[7])); + strlcpy(ch->sess_name, src_addr, sizeof(ch->sess_name)); } else if (use_port_guid_in_session_name) { /* * If the kernel module parameter use_port_guid_in_session_name @@ -2644,10 +2652,20 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, rep_param->ib_cm.initiator_depth = 4; } - if (ch->using_rdma_cm) - ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); - else - ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); + /* + * Hold the srpt_tgt mutex while accepting a connection to avoid that + * srpt_disconnect_ch() is invoked concurrently with this code. + */ + mutex_lock(&srpt_tgt->mutex); + if (srpt_tgt->enabled && ch->state == CH_CONNECTING) { + if (ch->using_rdma_cm) + ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); + else + ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); + } else { + ret = -EINVAL; + } + mutex_unlock(&srpt_tgt->mutex); switch (ret) { case 0: @@ -2717,9 +2735,36 @@ static int srpt_ib_cm_req_recv(struct ib_cm_id *cm_id, struct ib_cm_req_event_param *param, void *private_data) { + __be16 *const raw_sgid = (__be16 *)param->primary_path->dgid.raw; + char sgid[40]; + + scnprintf(sgid, sizeof(sgid), "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", + be16_to_cpu(raw_sgid[0]), be16_to_cpu(raw_sgid[1]), + be16_to_cpu(raw_sgid[2]), be16_to_cpu(raw_sgid[3]), + be16_to_cpu(raw_sgid[4]), be16_to_cpu(raw_sgid[5]), + be16_to_cpu(raw_sgid[6]), be16_to_cpu(raw_sgid[7])); + return srpt_cm_req_recv(cm_id->context, cm_id, NULL, param->port, param->primary_path->pkey, - private_data); + private_data, sgid); +} + +static const char *inet_ntop(const void *sa, char *dst, unsigned size) +{ + switch (((struct sockaddr *)sa)->sa_family) { + case AF_INET: + snprintf(dst, size, "%pI4", + &((struct sockaddr_in *)sa)->sin_addr); + break; + case AF_INET6: + snprintf(dst, size, "%pI6", + &((struct sockaddr_in6 *)sa)->sin6_addr); + break; + default: + snprintf(dst, size, "???"); + break; + } + return dst; } static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, @@ -2728,6 +2773,7 @@ static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, struct srpt_device *sdev; struct srp_login_req req; const struct srp_login_req_rdma *req_rdma; + char src_addr[40]; sdev = ib_get_client_data(cm_id->device, &srpt_client); if (!sdev) @@ -2747,13 +2793,15 @@ static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id, memcpy(req.initiator_port_id, req_rdma->initiator_port_id, 16); memcpy(req.target_port_id, req_rdma->target_port_id, 16); + inet_ntop(&cm_id->route.addr.src_addr, src_addr, sizeof(src_addr)); + return srpt_cm_req_recv(sdev, NULL, cm_id, cm_id->port_num, - cm_id->route.path_rec->pkey, &req); + cm_id->route.path_rec->pkey, &req, src_addr); } -static void srpt_cm_rej_recv(struct ib_cm_id *cm_id) +static void srpt_cm_rej_recv(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand REJ packet for cm_id %p.", cm_id); + PRINT_INFO("Received CM REJ for ch %s.", ch->sess_name); } static void srpt_check_timeout(struct srpt_rdma_ch *ch) @@ -2825,13 +2873,13 @@ static void srpt_cm_rtu_recv(struct srpt_rdma_ch *ch) static void srpt_cm_timewait_exit(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand TimeWait exit for ch %p.", ch); + PRINT_INFO("Received CM TimeWait exit for ch %s.", ch->sess_name); srpt_close_ch(ch); } -static void srpt_cm_rep_error(struct ib_cm_id *cm_id) +static void srpt_cm_rep_error(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand REP error for cm_id %p.", cm_id); + PRINT_INFO("Received CM REP error for ch %s.", ch->sess_name); } /** @@ -2848,7 +2896,7 @@ static int srpt_cm_dreq_recv(struct srpt_rdma_ch *ch) */ static void srpt_cm_drep_recv(struct srpt_rdma_ch *ch) { - PRINT_INFO("Received InfiniBand DREP message for ch %p.", ch); + PRINT_INFO("Received CM DREP message for ch %s.", ch->sess_name); srpt_close_ch(ch); } @@ -2864,6 +2912,7 @@ static void srpt_cm_drep_recv(struct srpt_rdma_ch *ch) */ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) { + struct srpt_rdma_ch *ch = cm_id->context; int ret; BUG_ON(!cm_id->context); @@ -2875,29 +2924,29 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) event->private_data); break; case IB_CM_REJ_RECEIVED: - srpt_cm_rej_recv(cm_id); + srpt_cm_rej_recv(ch); break; case IB_CM_RTU_RECEIVED: case IB_CM_USER_ESTABLISHED: - srpt_cm_rtu_recv((struct srpt_rdma_ch *)cm_id->context); + srpt_cm_rtu_recv(ch); break; case IB_CM_DREQ_RECEIVED: - ret = srpt_cm_dreq_recv((struct srpt_rdma_ch *)cm_id->context); + ret = srpt_cm_dreq_recv(ch); break; case IB_CM_DREP_RECEIVED: - srpt_cm_drep_recv((struct srpt_rdma_ch *)cm_id->context); + srpt_cm_drep_recv(ch); break; case IB_CM_TIMEWAIT_EXIT: - srpt_cm_timewait_exit((struct srpt_rdma_ch *)cm_id->context); + srpt_cm_timewait_exit(ch); break; case IB_CM_REP_ERROR: - srpt_cm_rep_error(cm_id); + srpt_cm_rep_error(ch); break; case IB_CM_DREQ_ERROR: - PRINT_INFO("Received IB DREQ ERROR event."); + PRINT_INFO("Received CM DREQ ERROR event."); break; case IB_CM_MRA_RECEIVED: - PRINT_INFO("Received IB MRA event"); + PRINT_INFO("Received CM MRA event"); break; default: PRINT_ERROR("received unrecognized IB CM event %d", @@ -2911,23 +2960,32 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id, struct rdma_cm_event *event) { + struct srpt_rdma_ch *ch = cm_id->context; int ret = 0; switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: ret = srpt_rdma_cm_req_recv(cm_id, event); break; + case RDMA_CM_EVENT_REJECTED: + srpt_cm_rej_recv(ch); + break; case RDMA_CM_EVENT_ESTABLISHED: - srpt_cm_rtu_recv(cm_id->context); + srpt_cm_rtu_recv(ch); break; case RDMA_CM_EVENT_DISCONNECTED: - srpt_cm_dreq_recv(cm_id->context); + if (ch->state < CH_DISCONNECTING) + srpt_cm_dreq_recv(ch); + else + srpt_cm_drep_recv(ch); break; case RDMA_CM_EVENT_TIMEWAIT_EXIT: - srpt_cm_timewait_exit(cm_id->context); + srpt_cm_timewait_exit(ch); + break; + case RDMA_CM_EVENT_UNREACHABLE: + srpt_cm_rep_error(ch); break; case RDMA_CM_EVENT_DEVICE_REMOVAL: - break; case RDMA_CM_EVENT_ADDR_CHANGE: break; default: @@ -3579,8 +3637,11 @@ static int srpt_detect(struct scst_tgt_template *tp) static int srpt_close_session(struct scst_session *sess) { struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(sess); + struct srpt_tgt *srpt_tgt = ch->srpt_tgt; + mutex_lock(&srpt_tgt->mutex); srpt_disconnect_ch(ch); + mutex_unlock(&srpt_tgt->mutex); return 0; } @@ -4339,7 +4400,8 @@ static void __exit srpt_cleanup_module(void) { TRACE_ENTRY(); - rdma_destroy_id(rdma_cm_id); + if (rdma_cm_id) + rdma_destroy_id(rdma_cm_id); ib_unregister_client(&srpt_client); #ifdef CONFIG_SCST_PROC srpt_unregister_procfs_entry(&srpt_template); diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index e9b2b4913..96267bf5c 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -41,7 +41,6 @@ #include #include #include -#include #include #if defined(INSIDE_KERNEL_TREE) #include @@ -49,6 +48,14 @@ #include #include #endif +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 == 5 +#define vlan_dev_vlan_id(dev) (panic("RHEL 5 misses vlan_dev_vlan_id()"),0) +#endif +#if defined(RHEL_MAJOR) +#define __ethtool_get_settings(dev, cmd) (panic("RHEL misses __ethtool_get_settings()"),0) +#endif +#include +#include #include "ib_dm_mad.h" /* @@ -139,7 +146,8 @@ enum { LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76)) && \ !(defined(RHEL_MAJOR) && \ (RHEL_MAJOR -0 > 6 || \ - RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 5)) + RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 5 || \ + RHEL_MAJOR -0 == 5 && RHEL_MINOR -0 >= 9)) /* See also patch "IB/core: Add GID change event" (commit 761d90ed4). */ enum { IB_EVENT_GID_CHANGE = 18 }; #endif @@ -284,15 +292,17 @@ struct srpt_send_ioctx { * enum rdma_ch_state - SRP channel state. * @CH_CONNECTING: QP is in RTR state; waiting for RTU. * @CH_LIVE: QP is in RTS state. - * @CH_DISCONNECTING: DREQ has been received and waiting for DREP or DREQ has - * been sent and waiting for DREP or channel is being closed - * for another reason. - * @CH_DISCONNECTED: Last WQE has been received. + * @CH_DISCONNECTING: DREQ has been sent and waiting for DREP or DREQ has + * been received. + * @CH_DRAINING: DREP has been received or waiting for DREP timed out + * and last work request has been queued. + * @CH_DISCONNECTED: Last completion has been received. */ enum rdma_ch_state { CH_CONNECTING, CH_LIVE, CH_DISCONNECTING, + CH_DRAINING, CH_DISCONNECTED, }; diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index 7a30acebf..9d583c4e5 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -383,9 +383,11 @@ int start(int argc, char **argv) desc.opt.on_free_cmd_type = on_free_cmd_type; desc.opt.memory_reuse_type = memory_reuse_type; - desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS; - desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER; - desc.opt.d_sense = SCST_CONTR_MODE_FIXED_SENSE; + desc.opt.tst = SCST_TST_1_SEP_TASK_SETS; + desc.opt.tmf_only = 0; + desc.opt.queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER; + desc.opt.qerr = SCST_QERR_0_ALL_RESUME; + desc.opt.d_sense = SCST_D_SENSE_0_FIXED_SENSE; res = ioctl(devs[i].scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc); if (res != 0) { From e577f7aff449227e97bf02063866226e8e9d7c26 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 13 May 2014 08:25:02 +0000 Subject: [PATCH 046/128] iscsi: Make ofed detection logic less noisy as well as fix non MLNX_OFED compilation Proposed-by: Lev Vainblat Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5519 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index 9e3d9e2be..dc27a41af 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -54,7 +54,7 @@ all: include/iscsi_scst_itf_ver.h progs mods ISER_SYMVERS:=$(KMOD)/Module.symvers OFED_CFLAGS:= -MLNX_OFED:=$(shell if ofed_info | grep MLNX_OFED 2>/dev/null; then echo true; else echo false; fi) +MLNX_OFED:=$(shell if ofed_info | grep MLNX_OFED >/dev/null 2>/dev/null; then echo true; else echo false; fi) ifeq ($(MLNX_OFED),true) # Whether MLNX_OFED for ubuntu has been installed From 117925943dfa3a61abe2010905cb8e0233c0f4e9 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 19 May 2014 07:15:55 +0000 Subject: [PATCH 047/128] isert: Fix crash in scenario when initiator opened connection but failed to send login request Initialization of close_work was not done soon enough thus causing an error when timeout occured Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5528 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 6049e9584..ec158798b 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -116,6 +116,22 @@ static void isert_dev_release(struct isert_conn_dev *dev) kref_put(&dev->kref, isert_kref_release_dev); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void isert_close_conn_fn(void *ctx) +#else +static void isert_close_conn_fn(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct iscsi_conn *conn = ctx; +#else + struct iscsi_conn *conn = container_of(work, + struct iscsi_conn, close_work); +#endif + + isert_close_connection(conn); +} + static void isert_conn_timer_fn(unsigned long arg) { struct isert_conn_dev *conn_dev = (struct isert_conn_dev *)arg; @@ -146,6 +162,12 @@ static int add_new_connection(struct isert_listener_dev *dev, goto out; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&conn->close_work, isert_close_conn_fn, conn); +#else + INIT_WORK(&conn->close_work, isert_close_conn_fn); +#endif + init_timer(&conn_dev->tmo_timer); conn_dev->tmo_timer.function = isert_conn_timer_fn; conn_dev->tmo_timer.expires = jiffies + 120 * HZ; @@ -170,22 +192,6 @@ static bool have_new_connection(struct isert_listener_dev *dev) return ret; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -static void isert_close_conn_fn(void *ctx) -#else -static void isert_close_conn_fn(struct work_struct *work) -#endif -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - struct iscsi_conn *conn = ctx; -#else - struct iscsi_conn *conn = container_of(work, - struct iscsi_conn, close_work); -#endif - - isert_close_connection(conn); -} - int isert_conn_alloc(struct iscsi_session *session, struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn, @@ -231,12 +237,6 @@ int isert_conn_alloc(struct iscsi_session *session, conn->transport = t; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - INIT_WORK(&conn->close_work, isert_close_conn_fn, conn); -#else - INIT_WORK(&conn->close_work, isert_close_conn_fn); -#endif - res = iscsi_init_conn(session, info, conn); if (unlikely(res)) goto cleanup_conn; From 8d146f80edfbb7e6004c7d1245e9695ae2161283 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 19 May 2014 07:16:04 +0000 Subject: [PATCH 048/128] isert: Cleanup connection establishment prints Add from/to IP upon connection request print and remove unneeded print Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5529 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 33 ++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 8185847d6..000edbdb6 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1021,7 +1021,6 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id, kref_init(&isert_conn->kref); - pr_info("iser created connection cm_id:%p\n", cm_id); TRACE_EXIT(); return isert_conn; @@ -1179,7 +1178,37 @@ static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id, goto fail_accept; } - pr_info("iser accepted connection cm_id:%p\n", cm_id); + switch (isert_conn->peer_addr.ss_family) { + case AF_INET: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + pr_info("iser accepted connection cm_id:%p " + NIPQUAD_FMT "->" NIPQUAD_FMT "\n", cm_id, + NIPQUAD(((struct sockaddr_in *)&isert_conn->peer_addr)->sin_addr.s_addr), + NIPQUAD(((struct sockaddr_in *)&isert_conn->self_addr)->sin_addr.s_addr)); +#else + pr_info("iser accepted connection cm_id:%p " + "%pI4->%pI4\n", cm_id, + &((struct sockaddr_in *)&isert_conn->peer_addr)->sin_addr.s_addr, + &((struct sockaddr_in *)&isert_conn->self_addr)->sin_addr.s_addr); +#endif + break; + case AF_INET6: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + pr_info("iser accepted connection cm_id:%p " + NIP6_FMT "->" NIP6_FMT "\n", cm_id, + NIP6(((struct sockaddr_in6 *)&isert_conn->peer_addr)->sin6_addr.s_addr), + NIP6(((struct sockaddr_in6 *)&isert_conn->self_addr)->sin6_addr.s_addr)); +#else + pr_info("iser accepted connection cm_id:%p " + "%pI6->%pI6\n", cm_id, + &((struct sockaddr_in6 *)&isert_conn->peer_addr)->sin6_addr, + &((struct sockaddr_in6 *)&isert_conn->self_addr)->sin6_addr); +#endif + break; + default: + pr_info("iser accepted connection cm_id:%p\n", cm_id); + } + out: TRACE_EXIT_RES(err); return err; From 988fff849e639e8a248dbbdcc42846adbb88a0a6 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 19 May 2014 07:16:10 +0000 Subject: [PATCH 049/128] isert: Fix smatch error Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5530 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 000edbdb6..067791c3f 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -764,15 +764,15 @@ static struct isert_device *isert_device_create(struct ib_device *ib_dev) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) cq_desc->cq_workqueue = create_singlethread_workqueue(wq_name); #else +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) cq_desc->cq_workqueue = alloc_workqueue(wq_name, WQ_CPU_INTENSIVE| -#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) - WQ_RESCUER + WQ_RESCUER, 1); #else - WQ_MEM_RECLAIM + cq_desc->cq_workqueue = alloc_workqueue(wq_name, + WQ_CPU_INTENSIVE| + WQ_MEM_RECLAIM, 1); #endif - - , 1); #endif if (!cq_desc->cq_workqueue) { pr_err("Failed to alloc iser cq work queue for dev:%s\n", From 9bcf7bf6168dc5d54f42d8caf18e79d8f8b64ea5 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 19 May 2014 07:16:16 +0000 Subject: [PATCH 050/128] isert: Fix compilation for old kernels Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5531 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 067791c3f..ccf0fe285 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1082,10 +1082,18 @@ void isert_conn_free(struct isert_connection *isert_conn) kref_put(&isert_conn->kref, isert_kref_free); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void isert_conn_closed_do_work(void *ctx) +#else static void isert_conn_closed_do_work(struct work_struct *work) +#endif { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct isert_connection *isert_conn = ctx; +#else struct isert_connection *isert_conn = container_of(work, struct isert_connection, close_work); +#endif /* notify upper layer */ if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) @@ -1097,7 +1105,7 @@ static void isert_conn_closed_do_work(struct work_struct *work) static void isert_sched_conn_closed(struct isert_connection *isert_conn) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work, NULL); + INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work, isert_conn); #else INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work); #endif From adf364ca2e3462420d202f32d905124f60a9dff6 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 27 May 2014 11:49:21 +0000 Subject: [PATCH 051/128] Merged revisions 5510-5518,5520-5527,5532-5533 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5510 | vlnb | 2014-05-09 06:51:10 +0300 (Fri, 09 May 2014) | 8 lines scst: Make pr path configurable Make the path of the file in which persistent reservation information is stored configurable via sysfs. Signed-off-by: Bart Van Assche with some improvements and fixes ........ r5511 | vlnb | 2014-05-09 06:57:20 +0300 (Fri, 09 May 2014) | 9 lines scst_vdisk: Introduce three helper functions Introduce the vdisk_bio_alloc(), vdisk_bio_set_failfast() and vdisk_bio_set_hoq() helper functions. This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5512 | vlnb | 2014-05-09 07:14:39 +0300 (Fri, 09 May 2014) | 3 lines Cleanup ........ r5513 | vlnb | 2014-05-10 02:20:49 +0300 (Sat, 10 May 2014) | 7 lines Fix sense code for invalid service actions According to T10, multibyte opcode commands with not supported service actions must be refused with INVALID FIELD IN CDB instead of INVALID OPCODE ........ r5514 | vlnb | 2014-05-10 02:25:13 +0300 (Sat, 10 May 2014) | 3 lines Cleanup ........ r5515 | vlnb | 2014-05-10 05:10:22 +0300 (Sat, 10 May 2014) | 3 lines Improve tracing of Unit Attentions ........ r5516 | vlnb | 2014-05-10 06:55:11 +0300 (Sat, 10 May 2014) | 3 lines Follow up for r5513 ........ r5517 | bvassche | 2014-05-10 09:20:01 +0300 (Sat, 10 May 2014) | 6 lines scst_pres: Fix a recently introduced checkpatch warning Avoid that checkpatch reports the following: WARNING: do {} while (0) macros should not be semicolon terminated ........ r5518 | bvassche | 2014-05-12 18:56:45 +0300 (Mon, 12 May 2014) | 1 line ib_srpt: Source code comment spelling fix ........ r5520 | vlnb | 2014-05-15 04:39:12 +0300 (Thu, 15 May 2014) | 11 lines iscsi-scst: One major number per thread pool Assign one major number per thread pool instead of as many major numbers as there are threads in a thread pool. Do not increment 'major' if thread pool allocation fails. Micro-optimize iscsi_threads_pool_get() by eliminating the assignment to 'fn' and the write via snprintf() into name[]. Signed-off-by: Bart Van Assche ........ r5521 | vlnb | 2014-05-16 05:05:38 +0300 (Fri, 16 May 2014) | 3 lines Cleanups ........ r5522 | vlnb | 2014-05-16 05:37:29 +0300 (Fri, 16 May 2014) | 5 lines Cleanup Those functions might be called on some corner cases without pr_mutex held ........ r5523 | vlnb | 2014-05-17 03:18:42 +0300 (Sat, 17 May 2014) | 8 lines scst_lib: Clarify scst_init_cmd() documentation The possible return values of scst_init_cmd() are -1, 0 and 1. Mention this in the comment header above that function. Signed-off-by: Bart Van Assche ........ r5524 | vlnb | 2014-05-17 04:04:08 +0300 (Sat, 17 May 2014) | 29 lines scst_main: Fix race between scst_resume_activity() and scst_init_thread() After SCST_FLAG_SUSPENDED has been cleared it is essential that scst_do_job_init() reexamines scst_init_cmd_list to avoid that commands get stuck in the command init list. This patch fixes the following race condition that can occur if SCST_FLAG_SUSPENDED has been set and if scst_init_cmd_list is not empty: * scst_do_job_init() returns to scst_init_thread() and leaves the commands that were on the init list on that list. * scst_init_thread() invokes test_init_cmd_list(). * test_init_cmd_list() returns false because SCST_FLAG_SUSPENDED has been set. * scst_resume_activity() clears SCST_FLAG_SUSPENDED and invokes wake_up_all(&scst_init_cmd_list_waitQ). However, since scst_init_thread() has not yet added the init thread back to scst_init_cmd_list_waitQ this wake_up_all() call doesn't do anything. * scst_init_thread() adds the init thread to scst_init_cmd_list_waitQ and unlocks scst_init_lock. Additionally, remove an unneeded smp_mb__after_clear_bit() call. wake_up_all() guarantees that if it wakes up a thread that that thread sees all store operations that were performed by the thread that invoked wake_up_all() and that preceeded the wake_up_all() invocation. Signed-off-by: Bart Van Assche ........ r5525 | vlnb | 2014-05-17 04:16:33 +0300 (Sat, 17 May 2014) | 7 lines scst_vdisk: Introduce vdisk_reexamine() and vdisk_close_fd() This patch does not change any functionality Signed-off-by: Bart Van Assche ........ r5526 | bvassche | 2014-05-18 14:26:42 +0300 (Sun, 18 May 2014) | 1 line scst_vdisk: Handle attach failures properly (follow-up for r5525) ........ r5527 | bvassche | 2014-05-18 19:27:01 +0300 (Sun, 18 May 2014) | 1 line nightly build: Update kernel versions ........ r5532 | vlnb | 2014-05-21 02:39:57 +0300 (Wed, 21 May 2014) | 3 lines Prevent potential deadlock between scst_del_threads() and commands taking scst_mutex ........ r5533 | vlnb | 2014-05-22 05:56:20 +0300 (Thu, 22 May 2014) | 3 lines Improve handling of aborted internal commands ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5554 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/iscsi.c | 37 +--- nightly/conf/nightly.conf | 16 +- scst/README | 6 + scst/include/scst.h | 28 +++- scst/include/scst_const.h | 3 +- scst/src/dev_handlers/scst_vdisk.c | 261 +++++++++++++++++------------ scst/src/scst_lib.c | 173 ++++++++++--------- scst/src/scst_main.c | 26 ++- scst/src/scst_pres.c | 252 +++++++++++++++++++--------- scst/src/scst_pres.h | 3 + scst/src/scst_priv.h | 1 + scst/src/scst_sysfs.c | 153 ++++++++++++++++- scst/src/scst_targ.c | 34 ++-- srpt/src/ib_srpt.h | 2 +- usr/fileio/common.c | 10 +- 15 files changed, 674 insertions(+), 331 deletions(-) diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index bb724fb0b..fc72e23dd 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -4141,6 +4141,7 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, struct iscsi_thread_pool *p; struct iscsi_thread *t; int i, j, count; + static int major; /* Protected by iscsi_threads_pool_mutex */ TRACE_ENTRY(); @@ -4203,44 +4204,21 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, list_add_tail(&p->thread_pools_list_entry, &iscsi_thread_pools_list); for (j = 0; j < 2; j++) { - int (*fn)(void *); - char name[25]; - static int major; - - if (j == 0) - fn = istrd; - else - fn = istwr; - for (i = 0; i < count; i++) { - if (j == 0) { - major++; - if (cpu_mask == NULL) - snprintf(name, sizeof(name), "iscsird%d", i); - else - snprintf(name, sizeof(name), "iscsird%d_%d", - major, i); - } else { - if (cpu_mask == NULL) - snprintf(name, sizeof(name), "iscsiwr%d", i); - else - snprintf(name, sizeof(name), "iscsiwr%d_%d", - major, i); - } - t = kmalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) { res = -ENOMEM; - PRINT_ERROR("Failed to allocate thread %s " - "(size %zd)", name, sizeof(*t)); + PRINT_ERROR("Failed to allocate thread " + "(size %zd)", sizeof(*t)); goto out_free; } - t->thr = kthread_run(fn, p, name); + t->thr = kthread_run(j ? istwr : istrd, p, + "iscsi%s%d_%d", j ? "wr" : "rd", + major, i); if (IS_ERR(t->thr)) { res = PTR_ERR(t->thr); - PRINT_ERROR("kthread_run() for thread %s failed: %d", - name, res); + PRINT_ERROR("kthread_run() failed: %d", res); kfree(t); goto out_free; } @@ -4248,6 +4226,7 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, } } + major++; res = 0; TRACE_DBG("Created iSCSI thread pool %p", p); diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index ac7cd2ecc..268ce0bb2 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,19 +3,19 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.14.3 \ -3.13.10 \ -3.12.17-nc \ +3.14.4 \ +3.13.11 \ +3.12.20-nc \ 3.11.10-nc \ -3.10.39-nc \ +3.10.40-nc \ 3.9.11-nc \ -3.8.14-nc \ +3.8.13.14-nc \ 3.7.10-nc \ -3.6.11-nc \ +3.6.11.9-nc \ 3.5.7-nc \ -3.4.89-nc \ +3.4.91-nc \ 3.3.8-nc \ -3.2.57-nc \ +3.2.59-nc \ 3.1.10-nc \ 3.0.101-nc \ 2.6.39.4-nc \ diff --git a/scst/README b/scst/README index ba4c4f5d5..1f42e569e 100644 --- a/scst/README +++ b/scst/README @@ -1043,6 +1043,12 @@ Each vdisk_fileio's device has the following attributes in - size_mb - contains size of this virtual device in MB. + - pr_file_name - Full path of the file or block device in which to store + persistent reservation information. The default value for this attribute is + /var/lib/scst/pr/${device_name}. Writing a new value into this sysfs + attribute is only allowed if the device is not exported. Modifying this + sysfs attribute causes the persistent reservation state to be reloaded. + - t10_dev_id - contains and allows to set T10 vendor specific identifier for Device Identification VPD page (0x83) of INQUIRY data. By default VDISK handler always generates t10_dev_id for every new diff --git a/scst/include/scst.h b/scst/include/scst.h index 53142c430..66cb6d685 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2533,6 +2533,9 @@ struct scst_device { /* True if persist through power loss is activated. */ unsigned short pr_aptpl:1; + /* Whether or not pr_file_name has been modified via sysfs. */ + unsigned int pr_file_name_is_set:1; + /* Persistent reservation type */ uint8_t pr_type; @@ -2562,7 +2565,10 @@ struct scst_device { struct scst_order_data dev_order_data; - /* Persist through power loss files */ + /* + * Where to save persistent reservation information. Protected by + * dev_pr_mutex. + */ char *pr_file_name; char *pr_file_name1; @@ -4700,7 +4706,10 @@ struct scst_sysfs_work_item { }; struct { struct scst_device *dev; - int new_threads_num; + union { + int new_threads_num; + bool default_val; + }; enum scst_dev_type_threads_pool_type new_threads_pool_type; }; struct scst_session *sess; @@ -4743,6 +4752,21 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data, void (*done)(void *data, char *sense, int result, int resid)); #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && !defined(RHEL_MAJOR) +/* + * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit + * e1ca7788dec6773b1a2bce51b7141948f2b8bccf). + */ +static inline void *vzalloc(unsigned long size) +{ + return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, + PAGE_KERNEL); +} +#endif + +int scst_get_file_mode(const char *path); +bool scst_parent_dir_exists(const char *path); + struct scst_data_descriptor { uint64_t sdd_lba; uint64_t sdd_blocks; diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index de3dd38be..da40be827 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -628,7 +628,8 @@ enum scst_tg_sup { *************************************************************/ #define SCST_SYSFS_BLOCK_SIZE PAGE_SIZE -#define SCST_PR_DIR "/var/lib/scst/pr" +#define SCST_VAR_DIR "/var/lib/scst" +#define SCST_PR_DIR (SCST_VAR_DIR "/pr") #define TID_COMMON_SIZE 24 diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 8ca710993..e06591426 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -964,7 +964,7 @@ static struct scst_vdisk_dev *vdev_find(const char *name) #define VDEV_WT_LABEL "WRITE_THROUGH" #define VDEV_MODE_PAGES_BUF_SIZE (64*1024) -#define VDEV_MODE_PAGES_DIR "/var/lib/scst/vdev_mode_pages" +#define VDEV_MODE_PAGES_DIR (SCST_VAR_DIR "/vdev_mode_pages") static int __vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev, uint8_t *buf, int size) @@ -990,18 +990,6 @@ out_overflow: goto out; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && !defined(RHEL_MAJOR) -/* - * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit - * e1ca7788dec6773b1a2bce51b7141948f2b8bccf). - */ -static void *vzalloc(unsigned long size) -{ - return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, - PAGE_KERNEL); -} -#endif - static int vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev) { int res, rc, offs; @@ -1230,10 +1218,38 @@ out: return res; } +/* + * Reexamine size, flush support and thin provisioning support for + * vdisk_fileio, vdisk_blockio and vdisk_cdrom devices. Do not modify the size + * of vdisk_nullio devices. + */ +static int vdisk_reexamine(struct scst_vdisk_dev *virt_dev) +{ + int res = 0; + + if (!virt_dev->nullio && !virt_dev->cdrom_empty) { + loff_t file_size; + + res = vdisk_get_file_size(virt_dev->filename, virt_dev->blockio, + &file_size); + if (res < 0) + goto out; + virt_dev->file_size = file_size; + vdisk_blockio_check_flush_support(virt_dev); + vdisk_check_tp_support(virt_dev); + } else if (virt_dev->cdrom_empty) { + virt_dev->file_size = 0; + } + + virt_dev->nblocks = virt_dev->file_size >> virt_dev->blk_shift; + +out: + return res; +} + static int vdisk_attach(struct scst_device *dev) { int res = 0; - loff_t err; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -1271,23 +1287,9 @@ static int vdisk_attach(struct scst_device *dev) dev->dev_rd_only = virt_dev->rd_only; - if (!virt_dev->cdrom_empty) { - if (!virt_dev->nullio) { - res = vdisk_get_file_size(virt_dev->filename, - virt_dev->blockio, &err); - if (res != 0) - goto out; - virt_dev->file_size = err; - - TRACE_DBG("size of file: %lld", err); - } - - vdisk_blockio_check_flush_support(virt_dev); - vdisk_check_tp_support(virt_dev); - } else - virt_dev->file_size = 0; - - virt_dev->nblocks = virt_dev->file_size >> dev->block_shift; + res = vdisk_reexamine(virt_dev); + if (res < 0) + goto out; if (!virt_dev->cdrom_empty) { PRINT_INFO("Attached SCSI target virtual %s %s " @@ -1386,6 +1388,19 @@ out: return res; } +static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&scst_mutex); +#endif + + if (virt_dev->fd) { + filp_close(virt_dev->fd, NULL); + virt_dev->fd = NULL; + virt_dev->bdev = NULL; + } +} + /* Invoked with scst_mutex held, so no further locking is necessary here. */ static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev) { @@ -1426,16 +1441,9 @@ static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev) lockdep_assert_held(&scst_mutex); #endif - if (--virt_dev->tgt_dev_cnt > 0) - goto out; + if (--virt_dev->tgt_dev_cnt == 0) + vdisk_close_fd(virt_dev); - virt_dev->bdev = NULL; - if (virt_dev->fd) { - filp_close(virt_dev->fd, NULL); - virt_dev->fd = NULL; - } - -out: TRACE_EXIT(); return; } @@ -1509,7 +1517,9 @@ static enum compl_status_e vdisk_exec_srv_action_in(struct vdisk_cmd_params *p) case SAI_GET_LBA_STATUS: return vdisk_exec_get_lba_status(p); } - return INVALID_OPCODE; + scst_set_invalid_field_in_cdb(p->cmd, 1, + 0 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return CMD_SUCCEEDED; } static enum compl_status_e vdisk_exec_maintenance_in(struct vdisk_cmd_params *p) @@ -1519,7 +1529,9 @@ static enum compl_status_e vdisk_exec_maintenance_in(struct vdisk_cmd_params *p) vdisk_exec_report_tpgs(p); return CMD_SUCCEEDED; } - return INVALID_OPCODE; + scst_set_invalid_field_in_cdb(p->cmd, 1, + 0 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return CMD_SUCCEEDED; } static enum compl_status_e vdisk_exec_send_diagnostic(struct vdisk_cmd_params *p) @@ -2919,6 +2931,38 @@ static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name) #endif } +static int vdisk_unmap_file_range(struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev, loff_t off, loff_t len, + struct file *fd) +{ + int res; + + TRACE_ENTRY(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + TRACE_DBG("Fallocating range %lld, len %lld", + (unsigned long long)off, (unsigned long long)len); + + res = fd->f_op->fallocate(fd, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len); + if (unlikely(res != 0)) { + PRINT_ERROR("fallocate() for %lld, len %lld " + "failed: %d", (unsigned long long)off, + (unsigned long long)len, res); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_write_error)); + res = -EIO; + goto out; + } +#else + res = 0; +#endif + +out: + TRACE_EXIT_RES(res); + return res; +} + static int vdisk_unmap_range(struct scst_cmd *cmd, struct scst_vdisk_dev *virt_dev, uint64_t start_lba, uint32_t blocks) { @@ -2977,29 +3021,12 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, goto out; #endif } else { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) - struct scst_device *dev = cmd->dev; - const int block_shift = dev->block_shift; - const loff_t s = start_lba << block_shift; - const loff_t l = blocks << block_shift; + loff_t off = start_lba << cmd->dev->block_shift; + loff_t len = blocks << cmd->dev->block_shift; - TRACE_DBG("Fallocating range %lld, len %lld", - (unsigned long long)s, (unsigned long long)l); - - err = fd->f_op->fallocate(fd, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, s, l); - if (unlikely(err != 0)) { - PRINT_ERROR("fallocate() for LBA %lld len %lld " - "failed: %d", (unsigned long long)start_lba, - (unsigned long long)blocks, err); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_write_error)); - res = -EIO; + res = vdisk_unmap_file_range(cmd, virt_dev, off, len, fd); + if (unlikely(res != 0)) goto out; - } -#else - sBUG(); -#endif } success: @@ -4435,7 +4462,9 @@ out: static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p) { /* Changing it don't forget to add it to vdisk_opcode_descriptors! */ - return INVALID_OPCODE; + scst_set_invalid_field_in_cdb(p->cmd, 1, + 0 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return CMD_SUCCEEDED; } /* SPC-4 REPORT TARGET PORT GROUPS command */ @@ -5064,6 +5093,59 @@ static void blockio_endio(struct bio *bio, int error) #endif } +static struct bio *vdisk_bio_alloc(gfp_t gfp_mask, int max_nr_vecs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + return bio_kmalloc(gfp_mask, max_nr_vecs); +#else + return bio_alloc(gfp_mask, max_nr_vecs); +#endif +} + +static void vdisk_bio_set_failfast(struct bio *bio) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) + bio->bi_rw |= (1 << BIO_RW_FAILFAST); +#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) + bio->bi_rw |= (1 << BIO_RW_FAILFAST_DEV) | + (1 << BIO_RW_FAILFAST_TRANSPORT) | + (1 << BIO_RW_FAILFAST_DRIVER); +#else + bio->bi_rw |= REQ_FAILFAST_DEV | + REQ_FAILFAST_TRANSPORT | + REQ_FAILFAST_DRIVER; +#endif +} + +static void vdisk_bio_set_hoq(struct bio *bio) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + bio->bi_rw |= REQ_SYNC; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + bio->bi_rw |= 1 << BIO_RW_SYNCIO; +#else + bio->bi_rw |= 1 << BIO_RW_SYNC; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + bio->bi_rw |= REQ_META; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /* + * Priority boosting was separated from REQ_META in commit 65299a3b + * (kernel 3.1.0). + */ + bio->bi_rw |= REQ_PRIO; +#endif +#elif !defined(RHEL_MAJOR) || RHEL_MAJOR -0 >= 6 + /* + * BIO_* and REQ_* flags were unified in commit 7b6d91da (kernel + * 2.6.36). + */ + bio->bi_rw |= BIO_RW_META; +#endif +} + static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) { struct scst_cmd *cmd = p->cmd; @@ -5127,11 +5209,7 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) int rc; if (need_new_bio) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) - bio = bio_kmalloc(gfp_mask, max_nr_vecs); -#else - bio = bio_alloc(gfp_mask, max_nr_vecs); -#endif + bio = vdisk_bio_alloc(gfp_mask, max_nr_vecs); if (!bio) { PRINT_ERROR("Failed to create bio " "for data segment %d (cmd %p)", @@ -5153,51 +5231,16 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) * Better to fail fast w/o any local recovery * and retries. */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) - bio->bi_rw |= (1 << BIO_RW_FAILFAST); -#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) - bio->bi_rw |= (1 << BIO_RW_FAILFAST_DEV) | - (1 << BIO_RW_FAILFAST_TRANSPORT) | - (1 << BIO_RW_FAILFAST_DRIVER); -#else - bio->bi_rw |= REQ_FAILFAST_DEV | - REQ_FAILFAST_TRANSPORT | - REQ_FAILFAST_DRIVER; -#endif + vdisk_bio_set_failfast(bio); + #if 0 /* It could be win, but could be not, so a performance study is needed */ bio->bi_rw |= REQ_SYNC; #endif if (fua) bio->bi_rw |= REQ_FUA; - if (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ - defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 - bio->bi_rw |= REQ_SYNC; -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - bio->bi_rw |= 1 << BIO_RW_SYNCIO; -#else - bio->bi_rw |= 1 << BIO_RW_SYNC; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ - defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 - bio->bi_rw |= REQ_META; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) - /* - * Priority boosting was separated - * from REQ_META in commit 65299a3b - * (kernel 3.1.0). - */ - bio->bi_rw |= REQ_PRIO; -#endif -#elif !defined(RHEL_MAJOR) || RHEL_MAJOR -0 >= 6 - /* - * BIO_* and REQ_* flags were unified - * in commit 7b6d91da (kernel 2.6.36). - */ - bio->bi_rw |= BIO_RW_META; -#endif - } + if (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE) + vdisk_bio_set_hoq(bio); if (!hbio) hbio = tbio = bio; @@ -5209,7 +5252,7 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) rc = bio_add_page(bio, pg, bytes, off); if (rc < bytes) { - sBUG_ON(rc != 0); + WARN_ON(rc != 0); need_new_bio = 1; lba_start0 += thislen >> block_shift; thislen = 0; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index cd7f98eac..afc92707a 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -3759,6 +3759,17 @@ void scst_free_device(struct scst_device *dev) return; } +bool scst_device_is_exported(struct scst_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&scst_mutex); +#endif + + WARN_ON_ONCE(!dev->dev_tgt_dev_list.next); + + return !list_empty(&dev->dev_tgt_dev_list); +} + /** * scst_init_mem_lim - initialize memory limits structure * @@ -4855,24 +4866,6 @@ out: return res; } -static void scst_prelim_finish_internal_cmd(struct scst_cmd *cmd) -{ - unsigned long flags; - - TRACE_ENTRY(); - - sBUG_ON(!cmd->internal); - - spin_lock_irqsave(&cmd->sess->sess_list_lock, flags); - list_del(&cmd->sess_cmd_list_entry); - spin_unlock_irqrestore(&cmd->sess->sess_list_lock, flags); - - __scst_cmd_put(cmd); - - TRACE_EXIT(); - return; -} - int scst_prepare_request_sense(struct scst_cmd *orig_cmd) { int res = 0; @@ -4933,15 +4926,22 @@ static void scst_complete_request_sense(struct scst_cmd *req_cmd) if (scsi_status_is_good(req_cmd->status) && (len > 0) && scst_sense_valid(buf) && !scst_no_sense(buf)) { - TRACE(TRACE_SCSI, "REQUEST SENSE %p returned valid sense", - req_cmd); + TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "REQUEST SENSE %p returned " + "valid sense", req_cmd); + PRINT_BUFF_FLAG(TRACE_SCSI|TRACE_MGMT_DEBUG, "Sense", buf, len); scst_alloc_set_sense(orig_cmd, scst_cmd_atomic(req_cmd), buf, len); } else { - PRINT_ERROR("%s", "Unable to get the sense via " - "REQUEST SENSE, returning HARDWARE ERROR"); - scst_set_cmd_error(orig_cmd, - SCST_LOAD_SENSE(scst_sense_hardw_error)); + if (test_bit(SCST_CMD_ABORTED, &req_cmd->cmd_flags) && + !test_bit(SCST_CMD_ABORTED, &orig_cmd->cmd_flags)) { + TRACE_MGMT_DBG("REQUEST SENSE %p was aborted, but " + "orig_cmd %p - not, retry", req_cmd, orig_cmd); + } else { + PRINT_ERROR("%s", "Unable to get the sense via " + "REQUEST SENSE, returning HARDWARE ERROR"); + scst_set_cmd_error(orig_cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + } } if (len > 0) @@ -4983,16 +4983,15 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, struct scst_cmd *ws_cmd = wsp->ws_orig_cmd; struct scatterlist *ws_sg = wsp->ws_sg; int ws_sg_cnt = wsp->ws_sg_cnt; - int res, i; + int res; uint8_t write16_cdb[16]; - struct scatterlist *sg; - int sg_cnt, len = blocks << ws_cmd->dev->block_shift; - struct sgv_pool_obj *sgv = NULL; + int len = blocks << ws_cmd->dev->block_shift; struct scst_cmd *cmd; - int64_t cur_lba; TRACE_ENTRY(); + EXTRACHECKS_BUG_ON(blocks > ws_sg_cnt); + if (unlikely(test_bit(SCST_CMD_ABORTED, &ws_cmd->cmd_flags)) || unlikely(ws_cmd->completed)) { TRACE_DBG("ws cmd %p aborted or completed (%d), aborting " @@ -5020,44 +5019,8 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, cmd->tgt_i_priv = wsp; - if ((ws_cmd->cdb[1] & 0x6) == 0) { - TRACE_DBG("Using direct ws_sg %p (cnt %d)", ws_sg, ws_sg_cnt); - sg = ws_sg; - EXTRACHECKS_BUG_ON(blocks > ws_sg_cnt); - sg_cnt = blocks; - goto set_add; - } - - sg = sgv_pool_alloc(ws_cmd->tgt_dev->pool, len, GFP_KERNEL, 0, - &sg_cnt, &sgv, &cmd->dev->dev_mem_lim, NULL); - if (sg == NULL) { - PRINT_ERROR("Unable to alloc sg for %d blocks", blocks); - res = -ENOMEM; - goto out_free_cmd; - } - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - sg_copy(sg, ws_sg, ws_sg_cnt, len, KM_USER0, KM_USER1); -#else - sg_copy(sg, ws_sg, ws_sg_cnt, len); -#endif - - cur_lba = lba; - for (i = 0; i < sg_cnt; i++) { - int cur_offs = 0; - while (cur_offs < sg[i].length) { - uint8_t *q; - q = &((int8_t *)(page_address(sg_page(&sg[i]))))[cur_offs]; - *((uint64_t *)q) = cur_lba; - cur_offs += ws_cmd->dev->block_size; - cur_lba++; - } - } - -set_add: - cmd->tgt_i_sg = sg; - cmd->tgt_i_sg_cnt = sg_cnt; - cmd->out_sgv = sgv; /* hacky, but it isn't used for WRITE(16) */ + cmd->tgt_i_sg = ws_sg; + cmd->tgt_i_sg_cnt = blocks; cmd->tgt_i_data_buf_alloced = 1; wsp->ws_cur_lba += blocks; @@ -5075,9 +5038,6 @@ out: TRACE_EXIT_RES(res); return res; -out_free_cmd: - scst_prelim_finish_internal_cmd(cmd); - out_busy: scst_set_busy(ws_cmd); goto out; @@ -5115,9 +5075,6 @@ static void scst_ws_write_cmd_finished(struct scst_cmd *cmd) TRACE_DBG("Write cmd %p finished (ws cmd %p, ws_cur_in_flight %d)", cmd, ws_cmd, wsp->ws_cur_in_flight); - if ((ws_cmd->cdb[1] & 0x6) != 0) - sgv_pool_free(cmd->out_sgv, &cmd->dev->dev_mem_lim); - cmd->sg = NULL; cmd->sg_cnt = 0; @@ -5237,8 +5194,11 @@ void scst_write_same(struct scst_cmd *cmd) goto out_done; } - if (((cmd->cdb[1] & 0x6) == 0x6) || ((cmd->cdb[1] & 0xE0) != 0)) { - scst_set_invalid_field_in_cdb(cmd, 1, 0); + if (unlikely((cmd->cdb[1] & 0x6) != 0)) { + TRACE(TRACE_MINOR, "LBDATA and/or PBDATA (ctrl %x) are not " + "supported", cmd->cdb[1]); + scst_set_invalid_field_in_cdb(cmd, 1, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); goto out_done; } @@ -8172,6 +8132,8 @@ again: UA_entry = list_first_entry(&cmd->tgt_dev->UA_list, typeof(*UA_entry), UA_list_entry); + TRACE_MGMT_DBG("Setting pending UA %p to cmd %p", UA_entry, cmd); + TRACE_DBG("next %p UA_entry %p", cmd->tgt_dev->UA_list.next, UA_entry); @@ -8301,8 +8263,13 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, memset(UA_entry, 0, sizeof(*UA_entry)); UA_entry->global_UA = (flags & SCST_SET_UA_FLAG_GLOBAL) != 0; - if (UA_entry->global_UA) - TRACE_MGMT_DBG("Queueing global UA %p", UA_entry); + + TRACE(TRACE_MGMT_DEBUG|TRACE_SCSI, "Queuing new %sUA %p (%x:%x:%x, " + "d_sense %d) to tgt_dev %p (dev %s, initiator %s)", + UA_entry->global_UA ? "global " : "", UA_entry, sense[2], + sense[12], sense[13], tgt_dev->dev->d_sense, tgt_dev, + tgt_dev->dev->virt_name, tgt_dev->sess->initiator_name); + TRACE_BUFF_FLAG(TRACE_DEBUG, "UA sense", sense, sense_len); if (sense_len > (int)sizeof(UA_entry->UA_sense_buffer)) { PRINT_WARNING("Sense truncated (needed %d), shall you increase " @@ -8314,9 +8281,6 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, set_bit(SCST_TGT_DEV_UA_PENDING, &tgt_dev->tgt_dev_flags); - TRACE_MGMT_DBG("Adding new UA to tgt_dev %p (dev %s, initiator %s)", - tgt_dev, tgt_dev->dev->virt_name, tgt_dev->sess->initiator_name); - if (flags & SCST_SET_UA_FLAG_AT_HEAD) list_add(&UA_entry->UA_list_entry, &tgt_dev->UA_list); else @@ -10143,6 +10107,55 @@ int scst_read_file_transactional(const char *name, const char *name1, } EXPORT_SYMBOL_GPL(scst_read_file_transactional); +/* + * Return the file mode if @path exists or an error code if opening @path via + * filp_open() in read-only mode failed. + */ +int scst_get_file_mode(const char *path) +{ + struct file *file; + int res; + + file = filp_open(path, O_RDONLY, 0400); + if (IS_ERR(file)) { + res = PTR_ERR(file); + goto out; + } + res = file->f_dentry->d_inode->i_mode; + filp_close(file, NULL); + +out: + return res; +} +EXPORT_SYMBOL(scst_get_file_mode); + +/* + * Return true if either @path does not contain a slash or if the directory + * specified in @path exists. + */ +bool scst_parent_dir_exists(const char *path) +{ + const char *last_slash = strrchr(path, '/'); + const char *dir; + int dir_mode; + bool res = true; + + if (last_slash && last_slash > path) { + dir = kasprintf(GFP_KERNEL, "%.*s", (int)(last_slash - path), + path); + if (dir) { + dir_mode = scst_get_file_mode(dir); + kfree(dir); + res = dir_mode >= 0 && S_ISDIR(dir_mode); + } else { + res = false; + } + } + + return res; +} +EXPORT_SYMBOL(scst_parent_dir_exists); + static void __init scst_scsi_op_list_init(void) { int i; diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index d3f24e383..867e7f6b5 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1060,17 +1060,20 @@ static void __scst_resume_activity(void) goto out; clear_bit(SCST_FLAG_SUSPENDED, &scst_flags); - /* - * The barrier is needed to make sure all woken up threads see the - * cleared flag. Not sure if it's really needed, but let's be safe. - */ - smp_mb__after_clear_bit(); mutex_lock(&scst_cmd_threads_mutex); list_for_each_entry(l, &scst_cmd_threads_list, lists_list_entry) { wake_up_all(&l->cmd_list_waitQ); } mutex_unlock(&scst_cmd_threads_mutex); + + /* + * Wait until scst_init_thread() either is waiting or has reexamined + * scst_flags. + */ + spin_lock_irq(&scst_init_lock); + spin_unlock_irq(&scst_init_lock); + wake_up_all(&scst_init_cmd_list_waitQ); spin_lock_irq(&scst_mcmd_lock); @@ -1198,13 +1201,13 @@ out: #ifndef CONFIG_SCST_PROC out_del_unlocked: mutex_lock(&scst_mutex); - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); mutex_unlock(&scst_mutex); scst_free_device(dev); goto out; #else out_del_locked: - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); #endif out_free_dev: @@ -1266,7 +1269,7 @@ static void scst_unregister_device(struct scsi_device *scsidp) dev->dev_unregistering = 1; - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); scst_dg_dev_remove_by_dev(dev); @@ -1418,6 +1421,11 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, scst_virt_dev_last_id = 1; } + res = scst_pr_set_file_name(dev, NULL, "%s/%s", SCST_PR_DIR, + dev->virt_name); + if (res != 0) + goto out_free_dev; + res = scst_pr_init_dev(dev); if (res != 0) goto out_free_dev; @@ -1516,7 +1524,7 @@ void scst_unregister_virtual_device(int id) dev->dev_unregistering = 1; - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); scst_pr_clear_dev(dev); diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 61017a4a8..c20edd010 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -45,6 +45,7 @@ #endif #include #include +#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) #include @@ -74,6 +75,19 @@ #define isblank(c) ((c) == ' ' || (c) == '\t') #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && defined(CONFIG_LOCKDEP) +#define scst_assert_pr_mutex_held(dev) \ + do { \ + if (dev->dev_list_entry.next && \ + !list_empty(&dev->dev_list_entry)) \ + lockdep_assert_held(&dev->dev_pr_mutex); \ + } while (0) +#else +static inline void scst_assert_pr_mutex_held(struct scst_device *dev) +{ +} +#endif + static inline int tid_size(const uint8_t *tid) { sBUG_ON(tid == NULL); @@ -174,6 +188,8 @@ out_error: static inline void scst_pr_set_holder(struct scst_device *dev, struct scst_dev_registrant *holder, uint8_t scope, uint8_t type) { + scst_assert_pr_mutex_held(dev); + dev->pr_is_set = 1; dev->pr_scope = scope; dev->pr_type = type; @@ -190,6 +206,8 @@ static bool scst_pr_is_holder(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (!dev->pr_is_set) goto out; @@ -209,6 +227,8 @@ out: /* Must be called under dev_pr_mutex */ void scst_pr_dump_prs(struct scst_device *dev, bool force) { + scst_assert_pr_mutex_held(dev); + if (!force) { #if defined(CONFIG_SCST_DEBUG) if ((trace_flag & TRACE_PRES) == 0) @@ -265,6 +285,8 @@ static void scst_pr_find_registrants_list_all(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Finding all registered records for device '%s' " "with exclude reg key %016llx", dev->virt_name, be64_to_cpu(exclude_reg->key)); @@ -291,6 +313,8 @@ static void scst_pr_find_registrants_list_key(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Finding registrants for device '%s' with key %016llx", dev->virt_name, be64_to_cpu(key)); @@ -320,6 +344,8 @@ static struct scst_dev_registrant *scst_pr_find_reg( TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + list_for_each_entry(reg, &dev->dev_registrants_list, dev_registrants_list_entry) { if ((reg->rel_tgt_id == rel_tgt_id) && @@ -338,6 +364,8 @@ static void scst_pr_clear_reservation(struct scst_device *dev) { TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + WARN_ON(!dev->pr_is_set); dev->pr_is_set = 0; @@ -355,6 +383,8 @@ static void scst_pr_clear_holder(struct scst_device *dev) { TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + WARN_ON(!dev->pr_is_set); if (dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG || @@ -382,6 +412,8 @@ static struct scst_dev_registrant *scst_pr_add_registrant( TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + sBUG_ON(dev == NULL); sBUG_ON(transport_id == NULL); @@ -465,6 +497,8 @@ static void scst_pr_remove_registrant(struct scst_device *dev, { TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Removing registrant %s/%d (reg %p, tgt_dev %p, key %016llx, " "dev %s)", debug_transport_id_to_initiator_name(reg->transport_id), reg->rel_tgt_id, reg, reg->tgt_dev, be64_to_cpu(reg->key), @@ -485,6 +519,16 @@ static void scst_pr_remove_registrant(struct scst_device *dev, return; } +static void scst_pr_remove_registrants(struct scst_device *dev) +{ + struct scst_dev_registrant *reg, *tmp_reg; + + list_for_each_entry_safe(reg, tmp_reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + scst_pr_remove_registrant(dev, reg); + } +} + /* Must be called under dev_pr_mutex */ static void scst_pr_send_ua_reg(struct scst_device *dev, struct scst_dev_registrant *reg, @@ -494,6 +538,8 @@ static void scst_pr_send_ua_reg(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + scst_set_sense(ua, sizeof(ua), dev->d_sense, key, asc, ascq); TRACE_PR("Queueing UA [%x %x %x]: registrant %s/%d (%p), tgt_dev %p, " @@ -517,6 +563,8 @@ static void scst_pr_send_ua_all(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + list_for_each_entry(reg, &dev->dev_registrants_list, dev_registrants_list_entry) { if (reg != exclude_reg) @@ -537,6 +585,8 @@ static void scst_pr_abort_reg(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (reg->tgt_dev == NULL) { TRACE_PR("Registrant %s/%d (%p, key 0x%016llx) has no session", debug_transport_id_to_initiator_name(reg->transport_id), @@ -612,6 +662,10 @@ static int scst_pr_do_load_device_file(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + + scst_pr_remove_registrants(dev); + old_fs = get_fs(); set_fs(KERNEL_DS); @@ -767,10 +821,12 @@ out: static int scst_pr_load_device_file(struct scst_device *dev) { - int res; + int res, rc; TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (dev->pr_file_name == NULL || dev->pr_file_name1 == NULL) { PRINT_ERROR("Invalid file paths for '%s'", dev->virt_name); res = -EINVAL; @@ -779,12 +835,20 @@ static int scst_pr_load_device_file(struct scst_device *dev) res = scst_pr_do_load_device_file(dev, dev->pr_file_name); if (res == 0) - goto out; + goto out_dump; else if (res == -ENOMEM) goto out; - res = scst_pr_do_load_device_file(dev, dev->pr_file_name1); + rc = res; + res = scst_pr_do_load_device_file(dev, dev->pr_file_name1); + if (res != 0) { + if (res == -ENOENT) + res = rc; + goto out; + } + +out_dump: scst_pr_dump_prs(dev, false); out: @@ -799,6 +863,8 @@ static void scst_pr_remove_device_files(struct scst_tgt_dev *tgt_dev) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + res = dev->pr_file_name ? scst_remove_file(dev->pr_file_name) : -ENOENT; res = dev->pr_file_name1 ? scst_remove_file(dev->pr_file_name1) : -ENOENT; @@ -821,6 +887,8 @@ void scst_pr_sync_device_file(struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if ((dev->pr_aptpl == 0) || list_empty(&dev->dev_registrants_list)) { scst_pr_remove_device_files(tgt_dev); goto out; @@ -1003,107 +1071,105 @@ write_error_close: goto out_set_fs; } -static int scst_pr_check_pr_path(void) +#endif /* CONFIG_SCST_PROC */ + +/** + * scst_pr_set_file_name - set name of file in which to save PR information + * @dev: SCST device. + * @prev: If not NULL, the current path will be stored in *@prev. It is the + * responsibility of the caller to invoke kfree(*@prev) at an + * appropriate time. + * @fmt: Full path of the file in which to save PR info. + * + * This function must be called either while @dev is not on the device list + * or with scst_mutex held. + */ +int scst_pr_set_file_name(struct scst_device *dev, char **prev, + const char *fmt, ...) { - int res; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) - struct nameidata nd; -#else - struct path path; -#endif + va_list args; + char *pr_file_name = NULL, *bkp = NULL; + int file_mode, res = -EINVAL; - mm_segment_t old_fs = get_fs(); + scst_assert_pr_mutex_held(dev); - TRACE_ENTRY(); + sBUG_ON(!fmt); - set_fs(KERNEL_DS); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) - res = path_lookup(SCST_PR_DIR, 0, &nd); - if (res == 0) - scst_path_put(&nd); -#else - res = kern_path(SCST_PR_DIR, 0, &path); - if (res == 0) - path_put(&path); -#endif - if (res != 0) { - PRINT_ERROR("Unable to find %s (err %d), you should create " - "this directory manually or reinstall SCST", - SCST_PR_DIR, res); - goto out_setfs; + res = -ENOMEM; + va_start(args, fmt); + pr_file_name = kvasprintf(GFP_KERNEL, fmt, args); + va_end(args); + if (!pr_file_name) { + PRINT_ERROR("Unable to kvasprintf() new PR file name"); + goto out; } -out_setfs: - set_fs(old_fs); + res = -EINVAL; + if (pr_file_name[0] != '/') { + PRINT_ERROR("PR file name must be absolute!"); + goto out; + } - TRACE_EXIT_RES(res); + file_mode = scst_get_file_mode(pr_file_name); + if (file_mode >= 0 && !S_ISREG(file_mode) && !S_ISBLK(file_mode)) { + PRINT_ERROR("PR file name must be file or block device!"); + goto out; + } + + res = -ENOENT; + if (!scst_parent_dir_exists(pr_file_name)) { + PRINT_ERROR("PR file name parent directory doesn't exist"); + goto out; + } + + res = -ENOMEM; + bkp = kasprintf(GFP_KERNEL, "%s.1", pr_file_name); + if (!bkp) { + PRINT_ERROR("Unable to kasprintf() backup PR file name"); + goto out; + } + if (prev) { + *prev = dev->pr_file_name; + dev->pr_file_name = pr_file_name; + pr_file_name = NULL; + } else + swap(dev->pr_file_name, pr_file_name); + swap(dev->pr_file_name1, bkp); + res = 0; + +out: + kfree(pr_file_name); + kfree(bkp); return res; } -#endif /* CONFIG_SCST_PROC */ - -/* Called under scst_mutex */ +/* Must be called under dev_pr_mutex or before dev is on the device list. */ int scst_pr_init_dev(struct scst_device *dev) { int res = 0; TRACE_ENTRY(); - dev->pr_file_name = kasprintf(GFP_KERNEL, "%s/%s", SCST_PR_DIR, - dev->virt_name); - if (dev->pr_file_name == NULL) { - PRINT_ERROR("Allocation of device '%s' file path failed", - dev->virt_name); - res = -ENOMEM; - goto out; - } - dev->pr_file_name1 = kasprintf(GFP_KERNEL, "%s/%s.1", SCST_PR_DIR, - dev->virt_name); - if (dev->pr_file_name1 == NULL) { - PRINT_ERROR("Allocation of device '%s' backup file path failed", - dev->virt_name); - res = -ENOMEM; - goto out_free_name; - } + scst_assert_pr_mutex_held(dev); + + sBUG_ON(!dev->pr_file_name || !dev->pr_file_name1); #ifndef CONFIG_SCST_PROC - res = scst_pr_check_pr_path(); - if (res == 0) { - res = scst_pr_load_device_file(dev); - if (res == -ENOENT) - res = 0; - } + res = scst_pr_load_device_file(dev); + if (res == -ENOENT) + res = 0; #endif - if (res != 0) - goto out_free_name1; - -out: TRACE_EXIT_RES(res); return res; - -out_free_name1: - kfree(dev->pr_file_name1); - dev->pr_file_name1 = NULL; - -out_free_name: - kfree(dev->pr_file_name); - dev->pr_file_name = NULL; - goto out; } /* Called under scst_mutex */ void scst_pr_clear_dev(struct scst_device *dev) { - struct scst_dev_registrant *reg, *tmp_reg; - TRACE_ENTRY(); - list_for_each_entry_safe(reg, tmp_reg, &dev->dev_registrants_list, - dev_registrants_list_entry) { - scst_pr_remove_registrant(dev, reg); - } + scst_pr_remove_registrants(dev); kfree(dev->pr_file_name); kfree(dev->pr_file_name1); @@ -1198,6 +1264,8 @@ static int scst_pr_register_with_spec_i_pt(struct scst_cmd *cmd, struct scst_dev_registrant *reg; uint8_t *transport_id; + scst_assert_pr_mutex_held(cmd->dev); + action_key = get_unaligned((__be64 *)&buffer[8]); ext_size = get_unaligned_be32(&buffer[24]); @@ -1315,6 +1383,8 @@ static void scst_pr_unregister(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Unregistering key %0llx", reg->key); is_holder = scst_pr_is_holder(dev, reg); @@ -1346,6 +1416,8 @@ static void scst_pr_unregister_all_tg_pt(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + /* * We can't use scst_mutex here since the caller already holds * dev_pr_mutex. @@ -1388,6 +1460,8 @@ static int scst_pr_register_on_tgt_id(struct scst_cmd *cmd, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + TRACE_PR("rel_tgt_id %d, spec_i_pt %d", rel_tgt_id, spec_i_pt); if (spec_i_pt) { @@ -1433,6 +1507,8 @@ static int scst_pr_register_all_tg_pt(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + /* * We can't use scst_mutex here because the caller already holds * dev_pr_mutex. @@ -1478,6 +1554,8 @@ static int __scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + if (all_tg_pt) { res = scst_pr_register_all_tg_pt(cmd, buffer, buffer_size, spec_i_pt, &rollback_list); @@ -1524,6 +1602,8 @@ void scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + aptpl = buffer[20] & 0x01; spec_i_pt = (buffer[20] >> 3) & 0x01; all_tg_pt = (buffer[20] >> 2) & 0x01; @@ -1618,6 +1698,8 @@ void scst_pr_register_and_ignore(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + aptpl = buffer[20] & 0x01; all_tg_pt = (buffer[20] >> 2) & 0x01; action_key = get_unaligned((__be64 *)&buffer[8]); @@ -1696,6 +1778,8 @@ void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + aptpl = buffer[17] & 0x01; key = get_unaligned((__be64 *)&buffer[0]); action_key = get_unaligned((__be64 *)&buffer[8]); @@ -1839,6 +1923,8 @@ void scst_pr_reserve(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + key = get_unaligned((__be64 *)&buffer[0]); scope = cmd->cdb[2] >> 4; type = cmd->cdb[2] & 0x0f; @@ -1926,6 +2012,8 @@ void scst_pr_release(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + key = get_unaligned((__be64 *)&buffer[0]); scope = cmd->cdb[2] >> 4; type = cmd->cdb[2] & 0x0f; @@ -2001,6 +2089,8 @@ void scst_pr_clear(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + key = get_unaligned((__be64 *)&buffer[0]); if (buffer_size != 24) { @@ -2211,6 +2301,8 @@ void scst_pr_preempt(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) { TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + scst_pr_do_preempt(cmd, buffer, buffer_size, false); TRACE_EXIT(); @@ -2248,6 +2340,8 @@ void scst_pr_preempt_and_abort(struct scst_cmd *cmd, uint8_t *buffer, { TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + cmd->pr_abort_counter = kzalloc(sizeof(*cmd->pr_abort_counter), GFP_KERNEL); if (cmd->pr_abort_counter == NULL) { @@ -2424,6 +2518,8 @@ void scst_pr_read_keys(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (buffer_size < 8) { TRACE_PR("buffer_size too small: %d. expected >= 8 " "(buffer %p)", buffer_size, buffer); @@ -2475,6 +2571,8 @@ void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (buffer_size < 8) { TRACE_PR("buffer_size too small: %d. expected >= 8 " "(buffer %p)", buffer_size, buffer); @@ -2538,6 +2636,8 @@ void scst_pr_report_caps(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (buffer_size < 8) { TRACE_PR("buffer_size too small: %d. expected >= 8 " "(buffer %p)", buffer_size, buffer); @@ -2577,6 +2677,8 @@ void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + if (buffer_size < 8) goto skip; diff --git a/scst/src/scst_pres.h b/scst/src/scst_pres.h index acb0e7e49..98b8f6f33 100644 --- a/scst/src/scst_pres.h +++ b/scst/src/scst_pres.h @@ -89,6 +89,9 @@ static inline void scst_pr_write_unlock(struct scst_device *dev) mutex_unlock(&dev->dev_pr_mutex); } +int scst_pr_set_file_name(struct scst_device *dev, char **prev, + const char *fmt, ...) __printf(3, 4); + int scst_pr_init_dev(struct scst_device *dev); void scst_pr_clear_dev(struct scst_device *dev); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 7df673ee8..ac0eeee89 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -335,6 +335,7 @@ void scst_free_tgt(struct scst_tgt *tgt); int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev); void scst_free_device(struct scst_device *dev); +bool scst_device_is_exported(struct scst_device *dev); struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, const char *acg_name, bool tgt_acg); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 5187d104d..d9768fa5a 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -2756,6 +2756,135 @@ static ssize_t scst_dev_sysfs_type_show(struct kobject *kobj, static struct kobj_attribute dev_type_attr = __ATTR(type, S_IRUGO, scst_dev_sysfs_type_show, NULL); +static ssize_t scst_dev_sysfs_pr_file_name_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct scst_device *dev; + int res; + + dev = container_of(kobj, struct scst_device, dev_kobj); + + res = mutex_lock_interruptible(&dev->dev_pr_mutex); + if (res != 0) + goto out; + /* pr_file_name is NULL for SCSI pass-through devices */ + WARN_ON_ONCE(!dev->pr_file_name); + res = scnprintf(buf, PAGE_SIZE, "%s\n%s", dev->pr_file_name ? : "", + dev->pr_file_name_is_set ? SCST_SYSFS_KEY_MARK "\n" : + ""); + mutex_unlock(&dev->dev_pr_mutex); + +out: + return res; +} + +static int +scst_dev_sysfs_pr_file_name_process_store(struct scst_sysfs_work_item *work) +{ + struct scst_device *dev = work->dev; + char *pr_file_name = work->buf, *prev = NULL; + int res; + + res = mutex_lock_interruptible(&scst_mutex); + if (res != 0) + goto out; + + res = -EBUSY; + if (scst_device_is_exported(dev)) { + PRINT_ERROR("%s: not changing pr_file_name because the device" + " has already been exported", dev->virt_name); + goto unlock_scst; + } + + res = mutex_lock_interruptible(&dev->dev_pr_mutex); + if (res) + goto unlock_scst; + + if (strcmp(dev->pr_file_name, pr_file_name) == 0) + goto unlock_dev_pr; + + res = scst_pr_set_file_name(dev, &prev, "%s", pr_file_name); + if (res != 0) + goto unlock_dev_pr; + + res = scst_pr_init_dev(dev); + if (res != 0) { + PRINT_ERROR("%s: loading PR from %s failed (%d) - restoring %s", + dev->virt_name, dev->pr_file_name, res, + prev ? : ""); + scst_pr_set_file_name(dev, NULL, "%s", prev); + scst_pr_init_dev(dev); + goto unlock_dev_pr; + } + + dev->pr_file_name_is_set = !work->default_val; + +unlock_dev_pr: + mutex_unlock(&dev->dev_pr_mutex); + +unlock_scst: + mutex_unlock(&scst_mutex); + +out: + kobject_put(&dev->dev_kobj); + kfree(prev); + + return res; +} + +static ssize_t scst_dev_sysfs_pr_file_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct scst_sysfs_work_item *work; + struct scst_device *dev; + char *pr_file_name, *p; + int res = -ENOMEM; + bool def = false; + + dev = container_of(kobj, struct scst_device, dev_kobj); + + pr_file_name = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf); + if (!pr_file_name) { + PRINT_ERROR("Unable to kasprintf() PR file name"); + goto out; + } + p = pr_file_name; + strsep(&p, "\n"); /* strip trailing whitespace */ + if (pr_file_name[0] == '\0') { + kfree(pr_file_name); + pr_file_name = kasprintf(GFP_KERNEL, "%s/%s", SCST_PR_DIR, + dev->virt_name); + if (!pr_file_name) { + PRINT_ERROR("Unable to kasprintf() PR file name"); + goto out; + } + def = true; + } + + res = scst_alloc_sysfs_work(scst_dev_sysfs_pr_file_name_process_store, + false, &work); + if (res != 0) + goto out; + kobject_get(&dev->dev_kobj); + work->dev = dev; + work->default_val = def; + swap(work->buf, pr_file_name); + + res = scst_sysfs_queue_wait_work(work); + if (res == 0) + res = count; + +out: + kfree(pr_file_name); + return res; +} + +static struct kobj_attribute dev_pr_file_name_attr = + __ATTR(pr_file_name, S_IWUSR|S_IRUGO, + scst_dev_sysfs_pr_file_name_show, + scst_dev_sysfs_pr_file_name_store); + #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) static ssize_t scst_dev_sysfs_dump_prs(struct kobject *kobj, @@ -3205,6 +3334,15 @@ int scst_dev_sysfs_create(struct scst_device *dev) dev->virt_name); goto out_del; } + } else { + res = sysfs_create_file(&dev->dev_kobj, + &dev_pr_file_name_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't create attr %s for dev %s", + dev_pr_file_name_attr.attr.name, + dev->virt_name); + goto out_del; + } } #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -5922,10 +6060,20 @@ static int scst_process_threads_store(int newtn) TRACE_DBG("newtn %d", newtn); - res = mutex_lock_interruptible(&scst_mutex); + /* + * Some commands are taking scst_mutex on commands processing path, + * so we need to drain them, because otherwise we can fall into a + * deadlock with kthread_stop() in scst_del_threads() waiting for + * those commands to finish. + */ + res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER); if (res != 0) goto out; + res = mutex_lock_interruptible(&scst_mutex); + if (res != 0) + goto out_resume; + oldtn = scst_main_cmd_threads.nr_threads; delta = newtn - oldtn; @@ -5942,6 +6090,9 @@ static int scst_process_threads_store(int newtn) out_up: mutex_unlock(&scst_mutex); +out_resume: + scst_resume_activity(); + out: TRACE_EXIT_RES(res); return res; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index bde560fa3..c1465cd1d 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -294,8 +294,9 @@ out: EXPORT_SYMBOL(scst_rx_cmd); /* - * No locks, but might be on IRQ. Returns 0 on success, <0 if processing of - * this command should be stopped. + * No locks, but might be on IRQ. Returns: + * - < 0 if the caller must not perform any further processing of @cmd; + * - >= 0 if the caller must continue processing @cmd. */ static int scst_init_cmd(struct scst_cmd *cmd, enum scst_exec_context *context) { @@ -2744,6 +2745,11 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only) TRACE_ENTRY(); if (unlikely(cmd->internal)) { + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) { + TRACE_MGMT_DBG("ABORTED set, aborting internal " + "cmd %p", cmd); + goto out_uncomplete; + } /* * The original command passed all checks and not finished yet */ @@ -3463,11 +3469,14 @@ static bool scst_check_auto_sense(struct scst_cmd *cmd) if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) && (!scst_sense_valid(cmd->sense) || scst_no_sense(cmd->sense))) { - TRACE(TRACE_SCSI|TRACE_MINOR_AND_MGMT_DBG, "CHECK_CONDITION, " - "but no sense: cmd->status=%x, cmd->msg_status=%x, " - "cmd->host_status=%x, cmd->driver_status=%x (cmd %p)", - cmd->status, cmd->msg_status, cmd->host_status, - cmd->driver_status, cmd); + if (!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + TRACE(TRACE_SCSI|TRACE_MINOR_AND_MGMT_DBG, + "CHECK_CONDITION, but no sense: cmd->status=%x, " + "cmd->msg_status=%x, cmd->host_status=%x, " + "cmd->driver_status=%x (cmd %p)", + cmd->status, cmd->msg_status, cmd->host_status, + cmd->driver_status, cmd); + } res = true; } else if (unlikely(cmd->host_status)) { if ((cmd->host_status == DID_REQUEUE) || @@ -3498,9 +3507,11 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) rc = scst_check_auto_sense(cmd); if (unlikely(rc)) { + if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) + goto next; PRINT_INFO("Command finished with CHECK CONDITION, but " - "without sense data (opcode %s), issuing " - "REQUEST SENSE", scst_get_opcode_name(cmd)); + "without sense data (opcode %s), issuing " + "REQUEST SENSE", scst_get_opcode_name(cmd)); rc = scst_prepare_request_sense(cmd); if (rc == 0) res = SCST_CMD_STATE_RES_CONT_NEXT; @@ -3513,6 +3524,7 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) goto out; } +next: rc = scst_check_sense(cmd); if (unlikely(rc)) { /* @@ -4546,10 +4558,6 @@ restart: goto restart; } - /* It isn't really needed, but let's keep it */ - if (susp != test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) - goto restart; - TRACE_EXIT(); return; } diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 96267bf5c..135091311 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -317,7 +317,7 @@ enum rdma_ch_state { * @kref: Per-channel reference count. * @rq_size: IB receive queue size. * @max_sge: Maximum length of RDMA scatter list. - * @max_rsp_size: Maximum size of an SRP response messages in bytes. + * @max_rsp_size: Maximum size of an SRP response message in bytes. * @sq_wr_avail: number of work requests available in the send queue. * @sport: pointer to the information of the HCA port used by this * channel. diff --git a/usr/fileio/common.c b/usr/fileio/common.c index c756e7736..ac98120af 100644 --- a/usr/fileio/common.c +++ b/usr/fileio/common.c @@ -391,11 +391,15 @@ static int do_exec(struct vdisk_cmd *vcmd) exec_read_capacity(vcmd); break; case SERVICE_ACTION_IN: - if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16) { + if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16) exec_read_capacity16(vcmd); - break; + else { + TRACE_DBG("Invalid service action %d for SERVICE " + "ACTION IN", cmd->cdb[1] & 0x1f); + set_cmd_error(vcmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); } - /* else go through */ + break; case REPORT_LUNS: default: TRACE_DBG("Invalid opcode %d", opcode); From 2d2b75b49bec602250ecdd356be4abbb7f0e6923 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 2 Jun 2014 06:14:59 +0000 Subject: [PATCH 052/128] isert: Make sure we cleanup correctly when closeing connection device Avoid double free in rare corner cases such as initiator that keeps connecting, not sending login request and then disconnecting Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5559 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 1 + 1 file changed, 1 insertion(+) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index ec158798b..993d00df7 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -492,6 +492,7 @@ static int isert_release(struct inode *inode, struct file *filp) TRACE_ENTRY(); vunmap(dev->sg_virt); + dev->sg_virt = NULL; dev->is_discovery = 0; if (dev->conn) { From 771c980cd3b16bd49f0b6f0b4b043ece541be225 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 8 Jun 2014 14:06:53 +0000 Subject: [PATCH 053/128] isert: Make ofed detection logic more robust with regard to non-standard installations Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5576 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index dc27a41af..cb4f0a4f9 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -54,7 +54,7 @@ all: include/iscsi_scst_itf_ver.h progs mods ISER_SYMVERS:=$(KMOD)/Module.symvers OFED_CFLAGS:= -MLNX_OFED:=$(shell if ofed_info | grep MLNX_OFED >/dev/null 2>/dev/null; then echo true; else echo false; fi) +MLNX_OFED:=$(shell if ofed_info -s | grep MLNX >/dev/null 2>/dev/null; then echo true; else echo false; fi) ifeq ($(MLNX_OFED),true) # Whether MLNX_OFED for ubuntu has been installed From 426ff543312f9a159769c5d37a5426ba8fe84bbd Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 1 Jul 2014 12:05:46 +0000 Subject: [PATCH 054/128] isert: Handle login pdu reception in more robust way Properly handle case when login PDU is received before logi response has been sent Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5655 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 29 +++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 993d00df7..774dc72e6 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -759,7 +759,34 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req) goto out; } - sBUG_ON(dev->login_req != NULL); + switch (dev->state) { + case CS_INIT: + if (dev->login_req != NULL) { + sBUG(); + res = -EINVAL; + goto out; + } + break; + + case CS_REQ_BHS: /* Got login request before done handling old one */ + break; + + case CS_REQ_DATA: + case CS_REQ_FINISHED: + case CS_RSP_BHS: + case CS_RSP_DATA: + case CS_RSP_FINISHED: + PRINT_WARNING("%s", + "Received login PDU while handling previous one\n"); + res = -EINVAL; + goto out; + + default: + sBUG(); + res = -EINVAL; + goto out; + } + spin_lock(&dev->pdu_lock); dev->login_req = login_req; From cb5477249015fd608a88ef47b79c59b06deb098b Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 1 Jul 2014 12:05:57 +0000 Subject: [PATCH 055/128] isert: Allow working with initiators that have more RDMA resources than us Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5656 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index ccf0fe285..8140bf6a9 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1171,15 +1171,15 @@ static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id, ini_conn_param = &event->param.conn; memset(&tgt_conn_param, 0, sizeof(tgt_conn_param)); - tgt_conn_param.responder_resources = - ini_conn_param->responder_resources; - tgt_conn_param.initiator_depth = - ini_conn_param->initiator_depth; tgt_conn_param.flow_control = ini_conn_param->flow_control; tgt_conn_param.rnr_retry_count = ini_conn_param->rnr_retry_count; + tgt_conn_param.initiator_depth = isert_dev->device_attr.max_qp_init_rd_atom; + if (tgt_conn_param.initiator_depth > ini_conn_param->initiator_depth) + tgt_conn_param.initiator_depth = ini_conn_param->initiator_depth; + err = rdma_accept(cm_id, &tgt_conn_param); if (unlikely(err)) { pr_err("Failed to accept conn request, err:%d\n", err); From 6432ad6263842ed55de3d1de4b3ff561109f34f9 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 3 Jul 2014 12:43:16 +0000 Subject: [PATCH 056/128] isert: Fix discovery over RDMA Commit r5655: isert: Handle login pdu reception in more robust way Broke discovery over RDMA. Fix it, as well as tidy up a few error prints in that area Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5660 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 774dc72e6..fae0963e3 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -753,7 +753,7 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req) TRACE_ENTRY(); if (!dev) { - PRINT_ERROR("Received PDU %p on invalid connection\n", + PRINT_ERROR("Received PDU %p on invalid connection", login_req); res = -EINVAL; goto out; @@ -761,6 +761,7 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req) switch (dev->state) { case CS_INIT: + case CS_RSP_FINISHED: if (dev->login_req != NULL) { sBUG(); res = -EINVAL; @@ -775,9 +776,8 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req) case CS_REQ_FINISHED: case CS_RSP_BHS: case CS_RSP_DATA: - case CS_RSP_FINISHED: - PRINT_WARNING("%s", - "Received login PDU while handling previous one\n"); + PRINT_WARNING("Received login PDU while handling previous one. State:%d", + dev->state); res = -EINVAL; goto out; From e4c7d430c1b88ac6d61c73183df5f3ade36cc735 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 7 Jul 2014 10:36:05 +0000 Subject: [PATCH 057/128] Merged revisions 5536-5539,5543,5545,5547,5555,5557-5558,5560-5563,5566-5575,5577-5579,5581-5590,5592-5598,5600-5603,5605-5622,5624-5631,5647-5651,5654,5657-5659,5661-5662 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5536 | vlnb | 2014-05-22 06:06:46 +0300 (Thu, 22 May 2014) | 3 lines Version changed to 3.1.0-pre1 ........ r5537 | vlnb | 2014-05-22 06:18:27 +0300 (Thu, 22 May 2014) | 3 lines Web updates ........ r5538 | bvassche | 2014-05-22 10:16:04 +0300 (Thu, 22 May 2014) | 1 line nightly build: Update kernel versions ........ r5539 | vlnb | 2014-05-23 05:20:35 +0300 (Fri, 23 May 2014) | 9 lines vdisk_nullio: Add "read_zero" attribute Add an attribute called "read_zero" to vdisk_nullio devices that controls whether or not READs from a vdisk_nullio device return zeroed data buffers. Signed-off-by: Bart Van Assche ........ r5543 | bvassche | 2014-05-23 10:33:53 +0300 (Fri, 23 May 2014) | 1 line RHEL 7 build fixes ........ r5545 | bvassche | 2014-05-23 11:36:36 +0300 (Fri, 23 May 2014) | 1 line scripts/rebuild-rhel-kernel-rpm: Add RHEL 7 RC support ........ r5547 | vlnb | 2014-05-24 06:10:34 +0300 (Sat, 24 May 2014) | 3 lines Optimize read_zero functionality ........ r5555 | bvassche | 2014-05-27 14:59:11 +0300 (Tue, 27 May 2014) | 5 lines qla2x00t: Documentation / source code comment / log messages spelling fix Change a few occurrences of "conformation" into "confirmation". See also the QLogic 2500 Series Firmware Interface Specification. ........ r5557 | vlnb | 2014-05-30 03:42:34 +0300 (Fri, 30 May 2014) | 5 lines Small code reorganization. No functionality changed ........ r5558 | vlnb | 2014-05-30 06:00:07 +0300 (Fri, 30 May 2014) | 3 lines Logging fixes ........ r5560 | bvassche | 2014-06-02 18:31:50 +0300 (Mon, 02 Jun 2014) | 1 line Makefile: Only report which RPMs have been built if "make rpm" is run as a non-privileged user ........ r5561 | bvassche | 2014-06-03 09:04:47 +0300 (Tue, 03 Jun 2014) | 1 line nightly build: Update kernel versions ........ r5562 | vlnb | 2014-06-04 04:54:21 +0300 (Wed, 04 Jun 2014) | 3 lines Decrease max WRITE SAME length for better latencies ........ r5563 | vlnb | 2014-06-04 05:16:51 +0300 (Wed, 04 Jun 2014) | 3 lines Enforce limit on max unmap LBAs ........ r5566 | bvassche | 2014-06-04 18:14:22 +0300 (Wed, 04 Jun 2014) | 1 line ib_srpt: Fix an error message ........ r5567 | bvassche | 2014-06-04 18:17:59 +0300 (Wed, 04 Jun 2014) | 1 line ib_srpt: Avoid triggering a SCSI command timeout after login ........ r5568 | bvassche | 2014-06-05 09:34:19 +0300 (Thu, 05 Jun 2014) | 1 line scst_vdisk: Build fix for kernel versions <= 2.6.32 ........ r5569 | bvassche | 2014-06-05 09:46:57 +0300 (Thu, 05 Jun 2014) | 1 line scst_vdisk: Fix a kernel version < 2.6.38 compiler warning ........ r5570 | vlnb | 2014-06-06 06:20:26 +0300 (Fri, 06 Jun 2014) | 8 lines scst_lib: Fix a compiler warning triggered by the WRITE SAME implementation Avoid for release builds that the compiler reports that the variable 'ws_sg_cnt' is not used. Signed-off-by: Bart Van Assche ........ r5571 | vlnb | 2014-06-06 06:22:14 +0300 (Fri, 06 Jun 2014) | 7 lines nullio_exec_read(): Fix kunmap() argument The argument of kunmap() is of type struct page *. Detected by smatch. Signed-off-by: Bart Van Assche ........ r5572 | vlnb | 2014-06-06 06:24:03 +0300 (Fri, 06 Jun 2014) | 11 lines scst: Leave out FSF mail address This avoids that the following checkpatch complaint is triggered: Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL. Signed-off-by: Bart Van Assche ........ r5573 | vlnb | 2014-06-06 06:26:55 +0300 (Fri, 06 Jun 2014) | 10 lines scst: Make lockdep_assert_held() easier to use The lockdep_assert_held() macro is a convenient debugging tool. However, it is inconvenient to surround each invocation of that macro by an #ifdef/#endif pair. Hence make it easier to use this macro with older kernel versions. Signed-off-by: Bart Van Assche ........ r5574 | vlnb | 2014-06-07 00:59:24 +0300 (Sat, 07 Jun 2014) | 3 lines Use limits.discard_zeroes_data to set LBPRZ ........ r5575 | bvassche | 2014-06-07 13:46:49 +0300 (Sat, 07 Jun 2014) | 1 line nightly build: Update kernel versions ........ r5577 | bvassche | 2014-06-10 17:16:14 +0300 (Tue, 10 Jun 2014) | 1 line ib_srpt: Make the test for IB_EVENT_GID_CHANGE support more robust ........ r5578 | bvassche | 2014-06-10 17:49:59 +0300 (Tue, 10 Jun 2014) | 1 line ib_srpt: Make IB_EVENT_GID_CHANGE test independent of the OFED detection code ........ r5579 | bvassche | 2014-06-11 13:02:15 +0300 (Wed, 11 Jun 2014) | 1 line ib_srpt: RHEL 5 build fix ........ r5581 | bvassche | 2014-06-11 18:27:06 +0300 (Wed, 11 Jun 2014) | 1 line regression tests: Sync with a recent sysfs change ........ r5582 | bvassche | 2014-06-11 18:27:48 +0300 (Wed, 11 Jun 2014) | 1 line regression tests: Sort hash keys before comparing ........ r5583 | bvassche | 2014-06-11 18:41:01 +0300 (Wed, 11 Jun 2014) | 1 line nightly build: Update kernel versions ........ r5584 | vlnb | 2014-06-11 22:33:18 +0300 (Wed, 11 Jun 2014) | 8 lines scst: RHEL 5 build fix Avoid that building the scst kernel module fails on RHEL 5 due to a missing kvasprintf() implementation. Signed-off-by: Bart Van Assche ........ r5585 | vlnb | 2014-06-11 22:38:10 +0300 (Wed, 11 Jun 2014) | 11 lines scst: Remove unused variables Avoid that building scst with W=1 triggers compiler warnings about variables that are set but not used. See also the documentation of the gcc compiler flag -Wunused-but-set-variable. This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5586 | vlnb | 2014-06-11 22:39:51 +0300 (Wed, 11 Jun 2014) | 9 lines scst_lib: Introduce additional temporary variables Make the code slightly easier to read by introducing temporary variables for the expressions 'tgt_dev->sess' and 'sess->tgt->tgtt'. This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5587 | vlnb | 2014-06-11 23:57:03 +0300 (Wed, 11 Jun 2014) | 10 lines scst: Add support for 64-bit LUNs The datatype of scsi_device.lun will be changed from u32 into u64 in the near future. Update SCST accordingly. These changes have been implemented such that these are compatible with 32-bit and 64-bit LUNs. Signed-off-by: Bart Van Assche ........ r5588 | vlnb | 2014-06-12 00:00:16 +0300 (Thu, 12 Jun 2014) | 9 lines scst_local: Support LUN numbers >= 16384 Add support for 32-bit LUN numbers. As soon as the patches that add 64-bit LUN support are upstream this patch will also make 64-bit LUN support available in scst_local. Signed-off-by: Bart Van Assche ........ r5589 | vlnb | 2014-06-12 00:42:08 +0300 (Thu, 12 Jun 2014) | 8 lines scst: Clean up __scst_resume_activity() Move all management commands from scst_delayed_mgmt_cmd_list to the active command list during resume instead of only the first one. Signed-off-by: Bart Van Assche ........ r5590 | vlnb | 2014-06-12 01:07:00 +0300 (Thu, 12 Jun 2014) | 9 lines scst: Introduce scst_lookup_tgt_dev() This patch does not change any functionality. Signed-off-by: Bart Van Assche with some improvements ........ r5592 | bvassche | 2014-06-12 11:38:45 +0300 (Thu, 12 Jun 2014) | 7 lines scst.h: Move definition of swap() Make sure that the definition of swap() is guarded by "#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)" only instead of "#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)" and "#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)". ........ r5593 | bvassche | 2014-06-12 12:15:50 +0300 (Thu, 12 Jun 2014) | 1 line nightly build: Update kernel versions ........ r5594 | bvassche | 2014-06-12 14:33:00 +0300 (Thu, 12 Jun 2014) | 1 line ib_srpt: Set MOFED include path correctly if MOFED has been installed with --add-kernel-support ........ r5595 | bvassche | 2014-06-12 16:38:38 +0300 (Thu, 12 Jun 2014) | 1 line ib_srpt: Make non-OFED build work again ........ r5596 | vlnb | 2014-06-13 07:52:18 +0300 (Fri, 13 Jun 2014) | 16 lines scst: Switch from the cpu_*() to the cpumask_*() API The cpus_*() functions were deprecated via patch "cpumask: introduce new API, without changing anything" (November 2008, commit ID 2d3854a37e8b). Hence switch from the cpus_*() API to the cpumask_*() API. This patch has the intended side effect of not adding the "[key]" property to cpumask sysfs attributes that contain the default cpumask. The current code namely reads uninitialized bits on systems where nr_cpu_ids < NR_CPUS because cpus_equal() compares more bits than those that were set by cpumask_copy(). Signed-off-by: Bart Van Assche ........ r5597 | vlnb | 2014-06-13 08:03:17 +0300 (Fri, 13 Jun 2014) | 3 lines Forgotten versions updated ........ r5598 | bvassche | 2014-06-13 09:55:23 +0300 (Fri, 13 Jun 2014) | 1 line ib_srpt: Make one_target_per_port the default mode ........ r5600 | vlnb | 2014-06-14 01:24:06 +0300 (Sat, 14 Jun 2014) | 9 lines scst: Avoid that W=1 triggers complaints about unused variables Avoid that building scst with W=1 triggers compiler warnings about variables that are set but not used. See also the documentation of the gcc compiler flag -Wunused-but-set-variable. Signed-off-by: Bart Van Assche ........ r5601 | vlnb | 2014-06-14 01:31:42 +0300 (Sat, 14 Jun 2014) | 8 lines scst_local: Add close_session() callback function This is useful for triggering the session reassignment code via the scst_local driver. Signed-off-by: Bart Van Assche ........ r5602 | vlnb | 2014-06-14 02:57:26 +0300 (Sat, 14 Jun 2014) | 8 lines scst_pr_read_reservation(): Initialize returned buffer Avoid that this function returns an uninitialized buffer to the initiator if buffer_size < 8. Detected by Coverity. Signed-off-by: Bart Van Assche ........ r5603 | vlnb | 2014-06-14 02:58:28 +0300 (Sat, 14 Jun 2014) | 5 lines scst: Help Coverity recognize that vmalloc(0) returns NULL Signed-off-by: Bart Van Assche ........ r5605 | bvassche | 2014-06-14 20:10:58 +0300 (Sat, 14 Jun 2014) | 1 line fcst: Remove an unused variable ........ r5606 | bvassche | 2014-06-14 20:17:56 +0300 (Sat, 14 Jun 2014) | 5 lines fcst: Move exch_done() calls into ft_cmd_done() This patch ensures that exch_done() gets called if an fcst callback returns SCST_TGT_RES_FATAL_ERROR. ........ r5607 | bvassche | 2014-06-14 20:18:34 +0300 (Sat, 14 Jun 2014) | 10 lines fcst: Handle frame send failures properly Retry sending XFER_RDY, data and response frames if the network driver reports that sending failed (-ENOMEM) instead of reporting a kernel warning (WARN_ON(1)). If sending XFER_RDY or data frames failed for another reason, report this to the initiator as a write error (ASC = 03; ASCQ = 00 which stands for PERIPHERAL DEVICE WRITE FAULT). If sending a response frame failed with another error code than -ENOMEM, do not send a response. ........ r5608 | vlnb | 2014-06-17 03:50:46 +0300 (Tue, 17 Jun 2014) | 10 lines scst: Make access control group removal behavior configurable SCST rejects removal of an access control group with one or more sessions with error code -EBUSY. Make it easy to change this behavior into forcibly closing sessions when an access control group is removed. Signed-off-by: Bart Van Assche ........ r5609 | bvassche | 2014-06-17 09:37:08 +0300 (Tue, 17 Jun 2014) | 1 line nightly build: Update kernel versions ........ r5610 | vlnb | 2014-06-19 06:51:48 +0300 (Thu, 19 Jun 2014) | 3 lines Update for 3.15 kernels ........ r5611 | bvassche | 2014-06-19 10:09:53 +0300 (Thu, 19 Jun 2014) | 1 line nightly build: Add kernel 3.15 build infrastructure ........ r5612 | bvassche | 2014-06-19 15:48:25 +0300 (Thu, 19 Jun 2014) | 1 line kernel module installation: Skip "depmod" when building an RPM ........ r5613 | vlnb | 2014-06-20 07:00:41 +0300 (Fri, 20 Jun 2014) | 8 lines scst: Convert a loop to keep smatch happy Avoid that smatch reports the following warning: scst_init_session() info: loop could be replaced with if statement. Signed-off-by: Bart Van Assche ........ r5614 | vlnb | 2014-06-20 07:02:00 +0300 (Fri, 20 Jun 2014) | 13 lines iscsi-scst: Suppress a compiler warning Avoid that the following compiler warning is reported when compiling iscsi-scst: chap.c: In function 'chap_rand': chap.c:348:5: warning: ignoring return value of 'read', declared with attribute warn_unused_result [-Wunused-result] (void)read(fd, &r, sizeof(r)); ^ Signed-off-by: Bart Van Assche ........ r5615 | vlnb | 2014-06-20 07:03:40 +0300 (Fri, 20 Jun 2014) | 5 lines scst, iscsi-scst: Fix RHEL 5 compilation warnings Signed-off-by: Bart Van Assche ........ r5616 | vlnb | 2014-06-20 07:05:11 +0300 (Fri, 20 Jun 2014) | 10 lines scst: Exclude certain locking code from static analysis Loops with locking statements and also lock and unlock statements guarded by an if-statement trigger false positive warnings when analyzing the SCST code with smatch and/or sparse. Hence exclude such code from static analysis. Signed-off-by: Bart Van Assche ........ r5617 | vlnb | 2014-06-20 07:09:11 +0300 (Fri, 20 Jun 2014) | 10 lines scst: Avoid that sparse complains about unreachable code Remove the code after BUG() statements to avoid that smatch complains about unreachable code. Hide the spin_unlock() statements before BUG() statements for static analysis tools to avoid that sparse complains about locking imbalances. Signed-off-by: Bart Van Assche ........ r5618 | vlnb | 2014-06-20 07:10:40 +0300 (Fri, 20 Jun 2014) | 12 lines Change BUG_ON(1) into BUG() With CONFIG_BUG=y both BUG() and BUG_ON(1) halt the system. However, with CONFIG_BUG=n BUG() halts the system but BUG_ON(1) not. To avoid such subtleties, change BUG_ON(1) into BUG(). See also patch Josh Triplett, "bug: Make BUG() always stop the machine", 7 April 2014 (commit ID a4b5d580e07875f9be29f62a57c67fbbdbb40ba2). Signed-off-by: Bart Van Assche ........ r5619 | bvassche | 2014-06-20 08:56:36 +0300 (Fri, 20 Jun 2014) | 1 line nightly build: Add kernel version 3.15.1 ........ r5620 | vlnb | 2014-06-24 07:45:08 +0300 (Tue, 24 Jun 2014) | 9 lines scst_vdisk: Split vdisk_exec_inquiry() Make vdisk_exec_inquiry() easier to read by moving the code for the implementation of each VPD page into a separate function. This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5621 | bvassche | 2014-06-24 16:32:18 +0300 (Tue, 24 Jun 2014) | 1 line ib_srpt: Complain if another ib_srpt.ko kernel module already exists ........ r5622 | bvassche | 2014-06-24 16:33:23 +0300 (Tue, 24 Jun 2014) | 2 lines ib_srpt: Set SCSI residual fields in SRP_CMD reply ........ r5624 | bvassche | 2014-06-25 14:50:40 +0300 (Wed, 25 Jun 2014) | 1 line nightly build: Use http instead of ftp for downloading kernel source code ........ r5625 | vlnb | 2014-06-26 00:38:19 +0300 (Thu, 26 Jun 2014) | 5 lines scst_debug.h: Make EXTRACHECKS_*_ON() statements visible to Coverity Signed-off-by: Bart Van Assche ........ r5626 | vlnb | 2014-06-27 02:26:25 +0300 (Fri, 27 Jun 2014) | 10 lines scst_vdisk: Three more put_unaligned_*() conversions Convert three more *(__be16 *)p = cpu_to_be16(v) statements into put_unaligned_be16(v, p) since the latter is easier to read. Also convert one "cmd->dev" into "dev" expression. This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5627 | bvassche | 2014-06-27 13:32:02 +0300 (Fri, 27 Jun 2014) | 1 line nightly build: Update kernel versions ........ r5628 | bvassche | 2014-06-28 22:56:36 +0300 (Sat, 28 Jun 2014) | 1 line ib_srpt: Remove existing ib_srpt.ko kernel modules before installation ........ r5629 | bvassche | 2014-06-28 22:58:44 +0300 (Sat, 28 Jun 2014) | 6 lines scst_vdisk: Fix 32-bit build Avoid 64-bit modulo computations since these result in undefined symbol errors on 32-bit systems (__moddi3 / __umoddi3). Support sizes >= 2**32 bytes on 32-bit systems. ........ r5630 | bvassche | 2014-06-28 23:00:22 +0300 (Sat, 28 Jun 2014) | 1 line scst.spec.in: Follow-up for r5628 ........ r5631 | bvassche | 2014-06-28 23:15:45 +0300 (Sat, 28 Jun 2014) | 1 line scst_local: Avoid that session deletion triggers a kernel warning ........ r5647 | bvassche | 2014-06-30 10:18:53 +0300 (Mon, 30 Jun 2014) | 1 line scst: Build fix for Linux kernel versions 2.6.33 and 2.6.34 ........ r5648 | bvassche | 2014-06-30 10:28:18 +0300 (Mon, 30 Jun 2014) | 1 line scst: Build fix for kernel versions <= 2.6.31 ........ r5649 | bvassche | 2014-06-30 11:40:23 +0300 (Mon, 30 Jun 2014) | 6 lines scst_vdisk: Fix a checkpatch warning Address the following checkpatch warning: char * array declaration might be better as static const ........ r5650 | bvassche | 2014-06-30 11:52:06 +0300 (Mon, 30 Jun 2014) | 1 line nightly build: Correct a kernel version ........ r5651 | bvassche | 2014-06-30 12:18:41 +0300 (Mon, 30 Jun 2014) | 1 line nightly build: Correct a kernel version ........ r5654 | bvassche | 2014-07-01 09:38:13 +0300 (Tue, 01 Jul 2014) | 6 lines scst_vdisk: Fix a checkpatch warning Avoid that checkpatch reports the following warning: WARNING: static const char * array should probably be static const char * const ........ r5657 | bvassche | 2014-07-01 19:46:12 +0300 (Tue, 01 Jul 2014) | 1 line nightly build: Update kernel versions ........ r5658 | bvassche | 2014-07-03 11:36:48 +0300 (Thu, 03 Jul 2014) | 1 line scripts/kernel-functions: Handle 3.x.0 kernel versions correctly ........ r5659 | bvassche | 2014-07-03 11:42:08 +0300 (Thu, 03 Jul 2014) | 1 line scripts/generate-patched-kernel: Clean up ........ r5661 | bvassche | 2014-07-04 08:39:28 +0300 (Fri, 04 Jul 2014) | 1 line Make scripts/kernel-functions again compatible with 2.6.x kernels ........ r5662 | bvassche | 2014-07-06 11:02:28 +0300 (Sun, 06 Jul 2014) | 1 line scripts/run-regression-tests: Add command-line option -4 (disable IPv6) ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5666 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 8 +- fcst/Makefile | 1 + fcst/fcst.h | 22 + fcst/ft_cmd.c | 86 +- fcst/ft_io.c | 16 +- fcst/ft_sess.c | 2 - ibmvstgt/Makefile | 4 +- iscsi-scst/COPYING | 4 +- iscsi-scst/Makefile | 2 + iscsi-scst/README | 2 +- iscsi-scst/include/iscsi_scst_ver.h | 2 +- iscsi-scst/kernel/config.c | 2 - iscsi-scst/kernel/conn.c | 20 +- iscsi-scst/kernel/iscsi.c | 19 +- iscsi-scst/kernel/iscsi.h | 4 + iscsi-scst/kernel/isert-scst/iser_rdma.c | 6 +- iscsi-scst/kernel/isert-scst/isert_login.c | 2 - iscsi-scst/kernel/nthread.c | 2 +- iscsi-scst/kernel/param.c | 4 - .../patches/put_page_callback-3.15.patch | 364 +++++++ iscsi-scst/kernel/session.c | 21 +- iscsi-scst/kernel/target.c | 14 - iscsi-scst/resource_agents/SCSTLun | 3 +- iscsi-scst/resource_agents/SCSTTarget | 3 +- iscsi-scst/usr/chap.c | 3 +- iscsi-scst/usr/isns.c | 4 +- iscsi-scst/usr/isns_proto.h | 4 +- mpt/Makefile | 1 + mvsas_tgt/Makefile | 1 + mvsas_tgt/mv_64xx.c | 4 +- mvsas_tgt/mv_64xx.h | 4 +- mvsas_tgt/mv_94xx.c | 4 +- mvsas_tgt/mv_94xx.h | 4 +- mvsas_tgt/mv_chips.h | 4 +- mvsas_tgt/mv_defs.h | 4 +- mvsas_tgt/mv_init.c | 6 +- mvsas_tgt/mv_sas.c | 4 +- mvsas_tgt/mv_sas.h | 4 +- mvsas_tgt/mv_spi.c | 4 +- mvsas_tgt/mv_spi.h | 4 +- mvsas_tgt/mv_tgt.c | 6 +- mvsas_tgt/mv_tgt.h | 4 +- nightly/conf/nightly.conf | 17 +- qla2x00t/Makefile | 1 + qla2x00t/qla2x00-target/Makefile | 1 + qla2x00t/qla2x00-target/Makefile_in-tree-3.15 | 5 + qla2x00t/qla2x00-target/README | 6 +- qla2x00t/qla2x00-target/qla2x00t.c | 14 +- qla2x00t/qla2x00-target/qla2x00t.h | 4 +- qla2x00t/qla_attr.c | 4 +- qla_isp/LICENSE | 3 +- qla_isp/common/isp.c | 3 +- qla_isp/common/isp_library.c | 3 +- qla_isp/common/isp_library.h | 3 +- qla_isp/common/isp_stds.h | 3 +- qla_isp/common/isp_target.c | 3 +- qla_isp/common/isp_target.h | 3 +- qla_isp/common/isp_tpublic.h | 3 +- qla_isp/common/ispmbox.h | 3 +- qla_isp/common/ispreg.h | 3 +- qla_isp/common/ispvar.h | 3 +- qla_isp/firmware/fwbin | 3 +- qla_isp/linux-2.6/Makefile | 7 +- qla_isp/linux-2.6/build/Makefile | 3 +- qla_isp/linux/isp_cb_ops.c | 3 +- qla_isp/linux/isp_ioctl.h | 3 +- qla_isp/linux/isp_linux.c | 3 +- qla_isp/linux/isp_linux.h | 3 +- qla_isp/linux/isp_pci.c | 3 +- qla_isp/linux/isp_scst.c | 3 +- scripts/generate-patched-kernel | 39 +- scripts/kernel-functions | 15 +- scripts/rebuild-rhel-kernel-rpm | 53 +- scripts/run-regression-tests | 48 +- scst.spec.in | 2 + scst/COPYING | 4 +- scst/README | 11 +- scst/README_in-tree | 9 +- scst/include/scst.h | 107 ++- scst/include/scst_const.h | 4 +- scst/include/scst_debug.h | 4 +- .../in-tree/Kconfig.drivers.Linux-3.15.patch | 13 + .../kernel/in-tree/Makefile.dev_handlers-3.15 | 14 + .../in-tree/Makefile.drivers.Linux-3.15.patch | 12 + scst/kernel/in-tree/Makefile.scst-3.15 | 13 + .../scst_exec_req_fifo-3.10.0-121.el7.patch | 1 + scst/kernel/scst_exec_req_fifo-3.15.patch | 528 +++++++++++ scst/src/dev_handlers/Makefile | 1 + scst/src/dev_handlers/scst_tape.c | 6 +- scst/src/dev_handlers/scst_user.c | 3 +- scst/src/dev_handlers/scst_vdisk.c | 894 ++++++++++-------- scst/src/scst_lib.c | 384 +++++--- scst/src/scst_main.c | 56 +- scst/src/scst_pres.c | 15 +- scst/src/scst_priv.h | 9 +- scst/src/scst_proc.c | 2 +- scst/src/scst_sysfs.c | 27 +- scst/src/scst_targ.c | 136 ++- scst/src/scst_tg.c | 46 - scst_local/Makefile | 1 + scst_local/in-tree/Makefile-3.15 | 2 + scst_local/scst_local.c | 101 +- scstadmin/LICENSE | 4 +- .../scst-0.9.10/t/03-targets.t | 1 + .../scstadmin.sysfs/scst-0.9.10/t/04-alua.t | 1 + .../scst-0.9.10/t/05-dynattr.t | 1 + .../scst-0.9.10/t/after-restore.conf | 1 - scstadmin/scstadmin.sysfs/scstadmin | 2 +- srpt/LICENSE | 3 +- srpt/Makefile | 63 +- srpt/README | 3 +- srpt/conftest/gid_change/Makefile | 1 + srpt/conftest/gid_change/gid_change.c | 9 + srpt/conftest/kcflags/Makefile | 1 + srpt/conftest/kcflags/kcflags.c | 8 + srpt/conftest/pre_cflags/Makefile | 1 + srpt/conftest/pre_cflags/pre_cflags.c | 8 + srpt/patches/kernel-3.15-pre-cflags.patch | 12 + srpt/src/ib_srpt.c | 40 +- srpt/src/ib_srpt.h | 9 +- usr/fileio/README | 2 +- usr/fileio/common.h | 2 +- usr/fileio/fileio.c | 2 +- www/downloads.html | 11 +- www/target_qla2x00t.html | 2 + 125 files changed, 2482 insertions(+), 1063 deletions(-) create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.15.patch create mode 100644 qla2x00t/qla2x00-target/Makefile_in-tree-3.15 create mode 100644 scst/kernel/in-tree/Kconfig.drivers.Linux-3.15.patch create mode 100644 scst/kernel/in-tree/Makefile.dev_handlers-3.15 create mode 100644 scst/kernel/in-tree/Makefile.drivers.Linux-3.15.patch create mode 100644 scst/kernel/in-tree/Makefile.scst-3.15 create mode 120000 scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.el7.patch create mode 100644 scst/kernel/scst_exec_req_fifo-3.15.patch create mode 100644 scst_local/in-tree/Makefile-3.15 create mode 100644 srpt/conftest/gid_change/Makefile create mode 100644 srpt/conftest/gid_change/gid_change.c create mode 100644 srpt/conftest/kcflags/Makefile create mode 100644 srpt/conftest/kcflags/kcflags.c create mode 100644 srpt/conftest/pre_cflags/Makefile create mode 100644 srpt/conftest/pre_cflags/pre_cflags.c create mode 100644 srpt/patches/kernel-3.15-pre-cflags.patch diff --git a/Makefile b/Makefile index 3e3637749..c85d9f619 100644 --- a/Makefile +++ b/Makefile @@ -402,9 +402,11 @@ scst-rpm: rpm: $(MAKE) scst-rpm $(MAKE) -C scstadmin rpm - @echo - @echo "The following RPMs have been built:" - @find -name '*.rpm' + @if [ "$$(id -u)" != 0 ]; then \ + echo; \ + echo "The following RPMs have been built:"; \ + find -name '*.rpm'; \ + fi 2perf: extraclean cd $(SCST_DIR) && $(MAKE) $@ diff --git a/fcst/Makefile b/fcst/Makefile index 5939c958a..1200bcb77 100644 --- a/fcst/Makefile +++ b/fcst/Makefile @@ -86,6 +86,7 @@ tgt: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ SCST_INC_DIR=$(SCST_INC_DIR) modules_install ins: diff --git a/fcst/fcst.h b/fcst/fcst.h index 5d3027514..50f25b4a5 100644 --- a/fcst/fcst.h +++ b/fcst/fcst.h @@ -174,4 +174,26 @@ struct ft_tpg *ft_lport_find_tpg(struct fc_lport *); struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *); void ft_cmd_dump(struct scst_cmd *, const char *); +/* #define FCST_INJECT_SEND_ERRORS 2 */ + +#ifdef FCST_INJECT_SEND_ERRORS +#define FCST_INJ_SEND_ERR(e) \ +({ \ + int _error = 0; \ + \ + if (scst_random() % 62929 == 0) \ + _error = -ENOMEM; \ + if (FCST_INJECT_SEND_ERRORS >= 2 && scst_random() % 69491 == 0) \ + _error = -ENXIO; \ + if (_error) \ + pr_warn("%s: injected seq_send() error %d\n", __func__, \ + _error); \ + else \ + _error = (e); \ + _error; \ +}) +#else +#define FCST_INJ_SEND_ERR(e) (e) +#endif + #endif /* __SCSI_FCST_H__ */ diff --git a/fcst/ft_cmd.c b/fcst/ft_cmd.c index aeb9241d8..8c3509a78 100644 --- a/fcst/ft_cmd.c +++ b/fcst/ft_cmd.c @@ -212,13 +212,10 @@ 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: @@ -257,10 +254,13 @@ static void ft_abort_cmd(struct scst_cmd *cmd) static void ft_cmd_done(struct ft_cmd *fcmd) { struct fc_frame *fp = fcmd->req_frame; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) - struct fc_lport *lport; + struct fc_seq *sp = fcmd->seq; + struct fc_lport *lport = fr_dev(fp); - lport = fr_dev(fp); + if (sp) + lport->tt.exch_done(sp); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) if (fr_seq(fp)) lport->tt.seq_release(fr_seq(fp)); #endif @@ -291,6 +291,7 @@ int ft_send_response(struct scst_cmd *cmd) struct fc_exch *ep; unsigned int slen; size_t len; + enum ft_cmd_state prev_state; int resid = 0; int bi_resid = 0; int error; @@ -303,7 +304,7 @@ int ft_send_response(struct scst_cmd *cmd) 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); + prev_state = ft_set_cmd_state(fcmd, FT_STATE_CMD_RSP_SENT); if (scst_cmd_aborted_on_xmit(cmd)) { FT_IO_DBG("cmd aborted did %x oxid %x\n", ep->did, ep->oxid); @@ -313,7 +314,8 @@ int ft_send_response(struct scst_cmd *cmd) if (!scst_cmd_get_is_send_status(cmd)) { FT_IO_DBG("send status not set. feature not implemented\n"); - return SCST_TGT_RES_FATAL_ERROR; + error = SCST_TGT_RES_FATAL_ERROR; + goto err; } status = scst_cmd_get_status(cmd); @@ -333,7 +335,7 @@ int ft_send_response(struct scst_cmd *cmd) error = ft_send_read_data(cmd); if (error) { FT_ERR("ft_send_read_data returned %d\n", error); - return error; + goto err; } if (dir == SCST_DATA_BIDI) { @@ -347,8 +349,10 @@ int ft_send_response(struct scst_cmd *cmd) } fp = fc_frame_alloc(lport, len); - if (!fp) - return SCST_TGT_RES_QUEUE_FULL; + if (!fp) { + error = SCST_TGT_RES_QUEUE_FULL; + goto err; + } fcp = fc_frame_payload_get(fp, len); memset(fcp, 0, sizeof(*fcp)); @@ -384,14 +388,26 @@ 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); - error = lport->tt.seq_send(lport, fcmd->seq, fp); - if (error < 0) + error = FCST_INJ_SEND_ERR(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); + error = error == -ENOMEM ? SCST_TGT_RES_QUEUE_FULL : + SCST_TGT_RES_FATAL_ERROR; + goto err; + } done: - lport->tt.exch_done(fcmd->seq); scst_tgt_cmd_done(cmd, SCST_CONTEXT_SAME); return SCST_TGT_RES_SUCCESS; + +err: + ft_set_cmd_state(fcmd, prev_state); + WARN_ONCE(error != SCST_TGT_RES_QUEUE_FULL && + error != SCST_TGT_RES_FATAL_ERROR, + "%s: invalid error code %d\n", + __func__, error); + return error; + } /* @@ -452,6 +468,7 @@ int ft_send_xfer_rdy(struct scst_cmd *cmd) struct fcp_txrdy *txrdy; struct fc_lport *lport; struct fc_exch *ep; + int error; fcmd = scst_cmd_get_tgt_priv(cmd); @@ -472,8 +489,17 @@ int ft_send_xfer_rdy(struct scst_cmd *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, FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - lport->tt.seq_send(lport, fcmd->seq, fp); - return SCST_TGT_RES_SUCCESS; + error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, fp)); + switch (error) { + case 0: + return SCST_TGT_RES_SUCCESS; + case -ENOMEM: + ft_set_cmd_state(fcmd, FT_STATE_NEW); + return SCST_TGT_RES_QUEUE_FULL; + default: + ft_set_cmd_state(fcmd, FT_STATE_NEW); + return SCST_TGT_RES_FATAL_ERROR; + } } /* @@ -528,16 +554,14 @@ 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(fr_seq(rx_fp)); + ; #else fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0); sp = fr_seq(fp); - if (sp) { + if (sp) lport->tt.seq_send(lport, sp, fp); - lport->tt.exch_done(sp); - } else { + else lport->tt.frame_send(lport, fp); - } #endif } @@ -651,7 +675,7 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) { struct fc_seq *sp; struct scst_cmd *cmd; - struct ft_cmd *fcmd; + struct ft_cmd *fcmd = NULL; struct fcp_cmnd *fcp; struct fc_lport *lport; int data_dir; @@ -659,6 +683,15 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) int cdb_len; lport = sess->tport->lport; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + sp = fr_seq(fp); +#else + sp = lport->tt.seq_assign(lport, fp); + if (!sp) + goto busy; +#endif + fcmd = kzalloc(sizeof(*fcmd), GFP_ATOMIC); if (!fcmd) goto busy; @@ -702,13 +735,6 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp) 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); -#else - sp = lport->tt.seq_assign(lport, fp); - if (!sp) - goto busy; -#endif fcmd->seq = sp; lport->tt.seq_set_resp(sp, ft_recv_seq, cmd); @@ -757,6 +783,8 @@ busy: ft_send_resp_status(fp, SAM_STAT_BUSY, 0); if (fcmd) ft_cmd_done(fcmd); + else if (sp) + lport->tt.exch_done(sp); } /* diff --git a/fcst/ft_io.c b/fcst/ft_io.c index 26305eecb..19be72e8b 100644 --- a/fcst/ft_io.c +++ b/fcst/ft_io.c @@ -17,8 +17,7 @@ * more details. * * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * this program. */ #include #include @@ -176,10 +175,17 @@ int ft_send_read_data(struct scst_cmd *cmd) remaining ? (FC_FC_EX_CTX | FC_FC_REL_OFF) : (FC_FC_EX_CTX | FC_FC_REL_OFF | FC_FC_END_SEQ), fh_off); - error = lport->tt.seq_send(lport, fcmd->seq, fp); + error = FCST_INJ_SEND_ERR(lport->tt.seq_send(lport, fcmd->seq, + fp)); if (error) { - WARN_ON(1); - /* XXX For now, initiator will retry */ + pr_warn("Sending frame with oid %#x oxid %#x resp_len" + " %d failed at frame_off %u / remaining %zu" + " with error code %d - %s", ep->oid, ep->oxid, + scst_cmd_get_resp_data_len(cmd), frame_off, + remaining, error, error == -ENOMEM ? + "retrying" : "giving up"); + return error == -ENOMEM ? SCST_TGT_RES_QUEUE_FULL : + SCST_TGT_RES_FATAL_ERROR; } else fcmd->read_data_len = frame_off; } diff --git a/fcst/ft_sess.c b/fcst/ft_sess.c index 3ce88bca4..1690a87d1 100644 --- a/fcst/ft_sess.c +++ b/fcst/ft_sess.c @@ -350,10 +350,8 @@ static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id) */ static void ft_sess_close(struct ft_sess *sess) { - struct fc_lport *lport; u32 port_id; - lport = sess->tport->lport; port_id = sess->port_id; if (port_id == -1) return; diff --git a/ibmvstgt/Makefile b/ibmvstgt/Makefile index daaf57f4e..2bc8ce127 100644 --- a/ibmvstgt/Makefile +++ b/ibmvstgt/Makefile @@ -38,7 +38,9 @@ all: src/$(MODULE_SYMVERS) $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src modules install: all src/ibmvstgt.ko - $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src modules_install + $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ + modules_install uninstall: rm -f $(INSTALL_DIR)/libsrp.ko $(INSTALL_DIR)/ibmvstgt.ko diff --git a/iscsi-scst/COPYING b/iscsi-scst/COPYING index afd5a9471..31b2c9e30 100644 --- a/iscsi-scst/COPYING +++ b/iscsi-scst/COPYING @@ -3,7 +3,6 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -305,8 +304,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + along with this program. Also add information on how to contact you by electronic and paper mail. diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index cb4f0a4f9..50c10a0a4 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -120,8 +120,10 @@ install: all @install -vD -m 755 usr/iscsi-scst-adm $(DESTDIR)$(SBINDIR)/iscsi-scst-adm @install -vD -m 644 doc/manpages/iscsi-scst-adm.8 $(DESTDIR)$(MANDIR)/man8/iscsi-scst-adm.8 $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(KMOD) \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install uninstall: diff --git a/iscsi-scst/README b/iscsi-scst/README index f265be6c3..d80e33f08 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -1,7 +1,7 @@ iSCSI SCST target driver ======================== -Version 3.0.0, XX XXXXX 2014 +Version 3.1.0, XX XXXXX 2014 ---------------------------- ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET) diff --git a/iscsi-scst/include/iscsi_scst_ver.h b/iscsi-scst/include/iscsi_scst_ver.h index 1f5388862..8e3790195 100644 --- a/iscsi-scst/include/iscsi_scst_ver.h +++ b/iscsi-scst/include/iscsi_scst_ver.h @@ -21,4 +21,4 @@ #define ISCSI_VERSION_STRING_SUFFIX #endif -#define ISCSI_VERSION_STRING "3.0.0-pre2" ISCSI_VERSION_STRING_SUFFIX +#define ISCSI_VERSION_STRING "3.1.0-pre1" ISCSI_VERSION_STRING_SUFFIX diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index 49d427ad0..b51a43293 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -407,9 +407,7 @@ static int add_session(void __user *ptr) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index cceb7b698..a1f85843e 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -92,9 +92,7 @@ void conn_info_show(struct seq_file *seq, struct iscsi_session *session) struct sock *sk; char buf[64]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); -#endif list_for_each_entry(conn, &session->conn_list, conn_list_entry) { sk = conn->sock->sk; @@ -246,9 +244,7 @@ int conn_sysfs_add(struct iscsi_conn *conn) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&conn->target->target_mutex); -#endif iscsi_get_initiator_ip(conn, addr, sizeof(addr)); @@ -319,9 +315,7 @@ struct iscsi_conn *conn_lookup(struct iscsi_session *session, u16 cid) { struct iscsi_conn *conn; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); -#endif /* * We need to find the latest conn to correctly handle @@ -453,7 +447,11 @@ static void iscsi_state_change(struct sock *sk) return; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) +static void iscsi_data_ready(struct sock *sk) +#else static void iscsi_data_ready(struct sock *sk, int len) +#endif { struct iscsi_conn *conn = sk->sk_user_data; @@ -461,7 +459,11 @@ static void iscsi_data_ready(struct sock *sk, int len) iscsi_make_conn_rd_active(conn); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) + conn->old_data_ready(sk); +#else conn->old_data_ready(sk, len); +#endif TRACE_EXIT(); return; @@ -807,9 +809,7 @@ void conn_free(struct iscsi_conn *conn) TRACE_MGMT_DBG("Freeing conn %p (sess=%p, %#Lx %u)", conn, session, (long long unsigned int)session->sid, conn->cid); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&conn->target->target_mutex); -#endif del_timer_sync(&conn->rsp_timer); @@ -914,9 +914,7 @@ int iscsi_conn_alloc(struct iscsi_session *session, struct iscsi_conn *conn; int res = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); -#endif conn = kmem_cache_zalloc(iscsi_conn_cache, GFP_KERNEL); if (!conn) { @@ -980,9 +978,7 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info) bool reinstatement = false; struct iscsit_transport *t; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); -#endif conn = conn_lookup(session, info->cid); if ((conn != NULL) && diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index fc72e23dd..cbc39e055 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -1017,7 +1017,7 @@ static void iscsi_tcp_set_sense_data(struct iscsi_cmnd *rsp, sg_init_table(sg, 2); sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr)); - sg_set_buf(&sg[1], sense_buf, sense_len); + sg_set_buf(&sg[1], (u8 *)sense_buf, sense_len); } static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp, @@ -3271,7 +3271,8 @@ static ssize_t iscsi_tcp_get_initiator_ip(struct iscsi_conn *conn, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", NIP6(inet6_sk(sk)->daddr)); #else -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) && \ + (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7) pos = scnprintf(buf, size, "[%p6]", &inet6_sk(sk)->daddr); #else pos = scnprintf(buf, size, "[%p6]", &sk->sk_v6_daddr); @@ -3732,7 +3733,7 @@ static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) case SCST_ABORT_ALL_TASKS_SESS: case SCST_ABORT_ALL_TASKS: case SCST_NEXUS_LOSS: - sBUG_ON(1); + sBUG(); break; default: iscsi_send_task_mgmt_resp(req, status, scst_mgmt_cmd_dropped(scst_mcmd)); @@ -4149,8 +4150,7 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, list_for_each_entry(p, &iscsi_thread_pools_list, thread_pools_list_entry) { - if ((cpu_mask == NULL) || - __cpus_equal(cpu_mask, &p->cpu_mask, nr_cpumask_bits)) { + if (!cpu_mask || cpumask_equal(cpu_mask, &p->cpu_mask)) { p->thread_pool_ref++; TRACE_DBG("iSCSI thread pool %p found (new ref %d)", p, p->thread_pool_ref); @@ -4184,12 +4184,9 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, INIT_LIST_HEAD(&p->wr_list); init_waitqueue_head(&p->wr_waitQ); if (cpu_mask == NULL) - cpus_setall(p->cpu_mask); - else { - cpus_clear(p->cpu_mask); - for_each_cpu(i, cpu_mask) - cpu_set(i, p->cpu_mask); - } + cpumask_setall(&p->cpu_mask); + else + cpumask_copy(&p->cpu_mask, cpu_mask); p->thread_pool_ref = 1; INIT_LIST_HEAD(&p->threads_list); diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index d93c78d4f..6410a9cd0 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -253,7 +253,11 @@ struct iscsi_conn { struct socket *sock; void (*old_state_change)(struct sock *); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) + void (*old_data_ready)(struct sock *); +#else void (*old_data_ready)(struct sock *, int); +#endif void (*old_write_space)(struct sock *); /* Both read only. Stay here for better CPU cache locality. */ diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 8140bf6a9..6c30f28a8 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -807,9 +807,8 @@ static struct isert_device *isert_device_create(struct ib_device *ib_dev) INIT_LIST_HEAD(&isert_dev->conn_list); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&dev_list_mutex); -#endif + isert_dev_list_add(isert_dev); pr_info("iser created device:%p\n", isert_dev); @@ -843,9 +842,8 @@ static void isert_device_release(struct isert_device *isert_dev) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&dev_list_mutex); -#endif + isert_dev_list_remove(isert_dev); /* remove from global list */ for (i = 0; i < isert_dev->num_cqs; ++i) { diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index fae0963e3..501b7ba81 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -205,9 +205,7 @@ int isert_conn_alloc(struct iscsi_session *session, TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); -#endif if (unlikely(!filp)) { res = -EBADF; diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 145174009..febf8d7d8 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -369,7 +369,7 @@ void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd) case SCST_ABORT_ALL_TASKS_SESS: case SCST_ABORT_ALL_TASKS: case SCST_NEXUS_LOSS: - sBUG_ON(1); + sBUG(); break; default: /* Nothing to do */ diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c index 7b307530f..702d29ca3 100644 --- a/iscsi-scst/kernel/param.c +++ b/iscsi-scst/kernel/param.c @@ -257,9 +257,7 @@ static int iscsi_tgt_params_set(struct iscsi_session *session, struct iscsi_tgt_params *params = &session->tgt_params; int32_t *iparams = info->target_params; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); -#endif if (set) { struct iscsi_conn *conn; @@ -327,9 +325,7 @@ int iscsi_params_set(struct iscsi_target *target, int err; struct iscsi_session *session; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target->target_mutex); -#endif if (info->sid == 0) { PRINT_ERROR("sid must not be %d", 0); diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.15.patch b/iscsi-scst/kernel/patches/put_page_callback-3.15.patch new file mode 100644 index 000000000..0bf0ce5e4 --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.15.patch @@ -0,0 +1,364 @@ +=== modified file 'drivers/block/drbd/drbd_receiver.c' +--- old/drivers/block/drbd/drbd_receiver.c 2014-06-18 01:32:48 +0000 ++++ new/drivers/block/drbd/drbd_receiver.c 2014-06-18 01:44:08 +0000 +@@ -131,7 +131,7 @@ static int page_chain_free(struct page * + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; + +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2014-06-18 01:32:48 +0000 ++++ new/include/linux/mm_types.h 2014-06-18 01:44:08 +0000 +@@ -196,6 +196,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2014-06-18 01:32:48 +0000 ++++ new/include/linux/net.h 2014-06-18 01:44:08 +0000 +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -285,6 +286,45 @@ int kernel_sendpage(struct socket *sock, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2014-06-18 01:32:48 +0000 ++++ new/include/linux/skbuff.h 2014-06-18 01:44:08 +0000 +@@ -2113,7 +2113,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -2136,7 +2136,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2014-06-18 01:32:48 +0000 ++++ new/net/Kconfig 2014-06-18 01:44:08 +0000 +@@ -75,6 +75,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/ceph/pagevec.c' +--- old/net/ceph/pagevec.c 2014-06-18 01:32:48 +0000 ++++ new/net/ceph/pagevec.c 2014-06-18 01:44:08 +0000 +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page ** + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + kfree(pages); + } + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2014-06-18 01:32:48 +0000 ++++ new/net/core/skbuff.c 2014-06-18 01:44:08 +0000 +@@ -425,7 +425,7 @@ struct sk_buff *__netdev_alloc_skb(struc + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -483,7 +483,7 @@ static void skb_clone_fraglist(struct sk + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -804,7 +804,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1647,7 +1647,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1700,7 +1700,7 @@ static bool spd_fill_page(struct splice_ + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2159,7 +2159,7 @@ skb_zerocopy(struct sk_buff *to, struct + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } +@@ -2813,7 +2813,7 @@ int skb_append_datato_frags(struct sock + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); + +=== modified file 'net/core/sock.c' +--- old/net/core/sock.c 2014-06-18 01:32:48 +0000 ++++ new/net/core/sock.c 2014-06-18 01:44:08 +0000 +@@ -1888,7 +1888,7 @@ bool skb_page_frag_refill(unsigned int s + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + order = SKB_FRAG_PAGE_ORDER; +@@ -2651,7 +2651,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2014-06-18 01:32:48 +0000 ++++ new/net/ipv4/Makefile 2014-06-18 01:44:08 +0000 +@@ -53,6 +53,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah. + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o xfrm4_protocol.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2014-06-18 01:32:48 +0000 ++++ new/net/ipv4/ip_output.c 2014-06-18 01:44:08 +0000 +@@ -1047,7 +1047,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1272,7 +1272,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2014-06-18 01:32:48 +0000 ++++ new/net/ipv4/tcp.c 2014-06-18 01:44:08 +0000 +@@ -939,7 +939,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1238,7 +1238,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2014-06-18 01:44:08 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + +=== modified file 'net/ipv6/ip6_output.c' +--- old/net/ipv6/ip6_output.c 2014-06-18 01:32:48 +0000 ++++ new/net/ipv6/ip6_output.c 2014-06-18 01:44:08 +0000 +@@ -1461,7 +1461,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, + diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index ddd64adf1..953492276 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -25,9 +25,7 @@ struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid) { struct iscsi_session *session; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target->target_mutex); -#endif list_for_each_entry(session, &target->session_list, session_list_entry) { @@ -46,9 +44,7 @@ static int iscsi_session_alloc(struct iscsi_target *target, struct iscsi_session *session; char *name = NULL; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif session = kmem_cache_zalloc(iscsi_sess_cache, GFP_KERNEL); if (!session) @@ -138,9 +134,7 @@ void sess_reinst_finished(struct iscsi_session *sess) TRACE_MGMT_DBG("Enabling reinstate successor sess %p", sess); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&sess->target->target_mutex); -#endif sBUG_ON(!sess->sess_reinstating); @@ -165,9 +159,7 @@ int __add_session(struct iscsi_target *target, TRACE_MGMT_DBG("Adding session SID %llx", info->sid); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif err = iscsi_session_alloc(target, info, &new_sess); if (err != 0) @@ -318,9 +310,7 @@ int session_free(struct iscsi_session *session, bool del) TRACE_MGMT_DBG("Freeing session %p (SID %llx)", session, session->sid); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&session->target->target_mutex); -#endif sBUG_ON(!list_empty(&session->conn_list)); if (unlikely(atomic_read(&session->active_cmds) != 0)) { @@ -376,9 +366,7 @@ int __del_session(struct iscsi_target *target, u64 sid) { struct iscsi_session *session; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target->target_mutex); -#endif session = session_lookup(target, sid); if (!session) @@ -400,9 +388,7 @@ void iscsi_sess_force_close(struct iscsi_session *sess) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&sess->target->target_mutex); -#endif PRINT_INFO("Deleting session %llx with initiator %s (%p)", (long long unsigned int)sess->sid, sess->initiator_name, sess); @@ -424,9 +410,7 @@ static void iscsi_session_info_show(struct seq_file *seq, { struct iscsi_session *session; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target->target_mutex); -#endif list_for_each_entry(session, &target->session_list, session_list_entry) { @@ -442,7 +426,12 @@ static void iscsi_session_info_show(struct seq_file *seq, static int iscsi_session_seq_open(struct inode *inode, struct file *file) { int res; + +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5 + res = seq_open(file, (struct seq_operations *)&iscsi_seq_op); +#else res = seq_open(file, &iscsi_seq_op); +#endif if (!res) ((struct seq_file *)file->private_data)->private = iscsi_session_info_show; diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index ef364e541..0c3280d3f 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -34,9 +34,7 @@ struct iscsi_target *target_lookup_by_id(u32 id) { struct iscsi_target *target; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif list_for_each_entry(target, &target_list, target_list_entry) { if (target->tid == id) @@ -50,9 +48,7 @@ static struct iscsi_target *target_lookup_by_name(const char *name) { struct iscsi_target *target; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif list_for_each_entry(target, &target_list, target_list_entry) { if (!strcmp(target->name, name)) @@ -71,9 +67,7 @@ static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid, TRACE_MGMT_DBG("Creating target tid %u, name %s", tid, name); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif len = strlen(name); if (!len) { @@ -144,9 +138,7 @@ int __add_target(struct iscsi_kern_target_info *info) struct iscsi_kern_attr __user *attrs_ptr; #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif if (nr_targets > MAX_NR_TARGETS) { err = -EBUSY; @@ -262,9 +254,7 @@ int __del_target(u32 id) struct iscsi_target *target; int err; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target_mgmt_mutex); -#endif target = target_lookup_by_id(id); if (!target) { @@ -302,9 +292,7 @@ void target_del_session(struct iscsi_target *target, TRACE_MGMT_DBG("Deleting session %p", session); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target->target_mutex); -#endif if (!list_empty(&session->conn_list)) { struct iscsi_conn *conn, *tc; @@ -330,9 +318,7 @@ void target_del_all_sess(struct iscsi_target *target, int flags) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&target->target_mutex); -#endif if (!list_empty(&target->session_list)) { TRACE_MGMT_DBG("Deleting all sessions from target %p", target); diff --git a/iscsi-scst/resource_agents/SCSTLun b/iscsi-scst/resource_agents/SCSTLun index 4e20bd069..eb84596be 100644 --- a/iscsi-scst/resource_agents/SCSTLun +++ b/iscsi-scst/resource_agents/SCSTLun @@ -26,8 +26,7 @@ # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# along with this program. # ####################################################################### diff --git a/iscsi-scst/resource_agents/SCSTTarget b/iscsi-scst/resource_agents/SCSTTarget index c1f4e2ad1..008eeb745 100644 --- a/iscsi-scst/resource_agents/SCSTTarget +++ b/iscsi-scst/resource_agents/SCSTTarget @@ -22,8 +22,7 @@ # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# along with this program. # ####################################################################### diff --git a/iscsi-scst/usr/chap.c b/iscsi-scst/usr/chap.c index 287210a1f..a54309be4 100644 --- a/iscsi-scst/usr/chap.c +++ b/iscsi-scst/usr/chap.c @@ -344,7 +344,8 @@ static int chap_rand(void) fd = open("/dev/urandom", O_RDONLY); assert(fd != -1); - (void)read(fd, &r, sizeof(r)); + if (read(fd, &r, sizeof(r)) < sizeof(r)) { + } close(fd); return r; } diff --git a/iscsi-scst/usr/isns.c b/iscsi-scst/usr/isns.c index 8fa916099..cdd7c3e4e 100644 --- a/iscsi-scst/usr/isns.c +++ b/iscsi-scst/usr/isns.c @@ -16,9 +16,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA + * along with this program. */ #include diff --git a/iscsi-scst/usr/isns_proto.h b/iscsi-scst/usr/isns_proto.h index c9ab970d4..49e45556f 100644 --- a/iscsi-scst/usr/isns_proto.h +++ b/iscsi-scst/usr/isns_proto.h @@ -16,9 +16,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA + * along with this program. */ #ifndef ISNS_PROTO_H diff --git a/mpt/Makefile b/mpt/Makefile index 957c39a1e..f75c73761 100644 --- a/mpt/Makefile +++ b/mpt/Makefile @@ -74,6 +74,7 @@ tgt: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) diff --git a/mvsas_tgt/Makefile b/mvsas_tgt/Makefile index 5b4db8d9c..30af54767 100644 --- a/mvsas_tgt/Makefile +++ b/mvsas_tgt/Makefile @@ -89,6 +89,7 @@ tgt: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install ins: diff --git a/mvsas_tgt/mv_64xx.c b/mvsas_tgt/mv_64xx.c index 48a450b4f..4b5b00e91 100644 --- a/mvsas_tgt/mv_64xx.c +++ b/mvsas_tgt/mv_64xx.c @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #include "mv_sas.h" diff --git a/mvsas_tgt/mv_64xx.h b/mvsas_tgt/mv_64xx.h index 179135222..8f3d07f2e 100644 --- a/mvsas_tgt/mv_64xx.h +++ b/mvsas_tgt/mv_64xx.h @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #ifndef _MVS64XX_REG_H_ diff --git a/mvsas_tgt/mv_94xx.c b/mvsas_tgt/mv_94xx.c index b7404d5f5..ec9612608 100644 --- a/mvsas_tgt/mv_94xx.c +++ b/mvsas_tgt/mv_94xx.c @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #include "mv_sas.h" diff --git a/mvsas_tgt/mv_94xx.h b/mvsas_tgt/mv_94xx.h index cd1a1f5e7..634cfcb41 100644 --- a/mvsas_tgt/mv_94xx.h +++ b/mvsas_tgt/mv_94xx.h @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #ifndef _MVS94XX_REG_H_ diff --git a/mvsas_tgt/mv_chips.h b/mvsas_tgt/mv_chips.h index fdad49204..99aa58ed8 100644 --- a/mvsas_tgt/mv_chips.h +++ b/mvsas_tgt/mv_chips.h @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ diff --git a/mvsas_tgt/mv_defs.h b/mvsas_tgt/mv_defs.h index c2bf56ded..090896ae3 100644 --- a/mvsas_tgt/mv_defs.h +++ b/mvsas_tgt/mv_defs.h @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #ifndef _MV_DEFS_H_ diff --git a/mvsas_tgt/mv_init.c b/mvsas_tgt/mv_init.c index 1994912e4..4e381ba9d 100644 --- a/mvsas_tgt/mv_init.c +++ b/mvsas_tgt/mv_init.c @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ @@ -195,7 +193,7 @@ static void mvs_tasklet(unsigned long opaque) mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[0]; if (unlikely(!mvi)) - BUG_ON(1); + BUG(); for (i = 0; i < core_nr; i++) { mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[i]; diff --git a/mvsas_tgt/mv_sas.c b/mvsas_tgt/mv_sas.c index c3736c705..fe778dc93 100644 --- a/mvsas_tgt/mv_sas.c +++ b/mvsas_tgt/mv_sas.c @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. * * Changelog: * - Praveen Murali May 15, 2012 diff --git a/mvsas_tgt/mv_sas.h b/mvsas_tgt/mv_sas.h index b698fd97f..731343877 100644 --- a/mvsas_tgt/mv_sas.h +++ b/mvsas_tgt/mv_sas.h @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #ifndef _MV_SAS_H_ diff --git a/mvsas_tgt/mv_spi.c b/mvsas_tgt/mv_spi.c index 2e51e3f2b..35bf89a5c 100644 --- a/mvsas_tgt/mv_spi.c +++ b/mvsas_tgt/mv_spi.c @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ diff --git a/mvsas_tgt/mv_spi.h b/mvsas_tgt/mv_spi.h index f60892b38..04648c62e 100644 --- a/mvsas_tgt/mv_spi.h +++ b/mvsas_tgt/mv_spi.h @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #ifdef SUPPORT_TARGET diff --git a/mvsas_tgt/mv_tgt.c b/mvsas_tgt/mv_tgt.c index b2c6ff8dd..86c5a49fa 100644 --- a/mvsas_tgt/mv_tgt.c +++ b/mvsas_tgt/mv_tgt.c @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #ifdef SUPPORT_TARGET @@ -1226,7 +1224,7 @@ static void mvst_do_cmd_completion(struct mvs_info *mvi, TRACE_DBG("Read data command %p finished", cmd); if (err) { cmd->cmd_state = MVST_STATE_SEND_DATA_RETRY; - sBUG_ON(1); + sBUG(); } goto out; } else if (cmd->cmd_state == MVST_STATE_ABORTED) { diff --git a/mvsas_tgt/mv_tgt.h b/mvsas_tgt/mv_tgt.h index 97c422135..0058854f7 100644 --- a/mvsas_tgt/mv_tgt.h +++ b/mvsas_tgt/mv_tgt.h @@ -17,9 +17,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. */ #ifdef SUPPORT_TARGET diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index 268ce0bb2..8248a5a78 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,17 +3,18 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.14.4 \ -3.13.11 \ -3.12.20-nc \ +3.15.3 \ +3.14.10-nc \ +3.13.11-nc \ +3.12.21-nc \ 3.11.10-nc \ -3.10.40-nc \ +3.10.46-nc \ 3.9.11-nc \ -3.8.13.14-nc \ +3.8.13-nc \ 3.7.10-nc \ -3.6.11.9-nc \ +3.6.11-nc \ 3.5.7-nc \ -3.4.91-nc \ +3.4.96-nc \ 3.3.8-nc \ 3.2.59-nc \ 3.1.10-nc \ @@ -26,7 +27,7 @@ ABT_KERNELS=" \ 2.6.35.14-u-nc \ 2.6.34.14-nc \ 2.6.33.20-nc \ -2.6.32.61-nc \ +2.6.32.62-nc \ 2.6.31.14-nc \ 2.6.30.10-nc \ 2.6.29.6-nc \ diff --git a/qla2x00t/Makefile b/qla2x00t/Makefile index ac628ce9b..b1651b067 100644 --- a/qla2x00t/Makefile +++ b/qla2x00t/Makefile @@ -61,6 +61,7 @@ all: install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install uninstall: diff --git a/qla2x00t/qla2x00-target/Makefile b/qla2x00t/qla2x00-target/Makefile index 7dbbf0117..67aacf8a1 100644 --- a/qla2x00t/qla2x00-target/Makefile +++ b/qla2x00t/qla2x00-target/Makefile @@ -105,6 +105,7 @@ ifneq ($(BUILD_2X_MODULE),) SCST_INC_DIR=$(SCST_INC_DIR) endif $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ SCST_INC_DIR=$(SCST_INC_DIR) modules_install uninstall: diff --git a/qla2x00t/qla2x00-target/Makefile_in-tree-3.15 b/qla2x00t/qla2x00-target/Makefile_in-tree-3.15 new file mode 100644 index 000000000..9657aee84 --- /dev/null +++ b/qla2x00t/qla2x00-target/Makefile_in-tree-3.15 @@ -0,0 +1,5 @@ +ccflags-y += -Idrivers/scsi/qla2xxx + +qla2x00tgt-y := qla2x00t.o + +obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index 386eb4801..d06a7757a 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -1,7 +1,7 @@ Target driver for QLogic 22xx/23xx/24xx/25xx Fibre Channel cards ================================================================ -Version 3.0.0, XX XXXXX 2014 +Version 3.1.0, XX XXXXX 2014 ---------------------------- This driver consists from two parts: the target mode driver itself and @@ -157,7 +157,7 @@ particular port. Setting this attribute to 1 will reverse current status of the initiator mode from enabled to disabled and vice versa. -Explicit conformation +Explicit confirmation --------------------- This option should (actually, almost always must) be enabled by echoing @@ -281,7 +281,7 @@ Each target subdirectory contains the following entries: of this FC port. It allows to finish configuring it before it starts accepting new connections. 0 by default. - - explicit_confirmation - allows to enable explicit conformations, see + - explicit_confirmation - allows to enable explicit confirmations, see above. - rel_tgt_id - allows to read or write SCSI Relative Target Port diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 346d6df25..3741f88fa 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -4956,7 +4956,7 @@ static void q2x_send_busy(scsi_qla_host_t *ha, atio_entry_t *atio) ctio->flags |= cpu_to_le16(OF_INC_RC); /* * CTIO from fw w/o scst_cmd doesn't provide enough info to retry it, - * if the explicit conformation is used. + * if the explicit confirmation is used. */ TRACE_BUFFER("CTIO BUSY packet data", ctio, REQUEST_ENTRY_SIZE); @@ -5017,7 +5017,7 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, CTIO7_FLAGS_DONT_RET_CTIO); /* * CTIO from fw w/o scst_cmd doesn't provide enough info to retry it, - * if the explicit conformation is used. + * if the explicit confirmation is used. */ ctio->ox_id = swab16(atio->fcp_hdr.ox_id); ctio->scsi_status = cpu_to_le16(status); @@ -5606,7 +5606,7 @@ static void q2t_exec_sess_work(struct q2t_tgt *tgt, loop_id = GET_TARGET_ID(ha, &prm->tm_iocb); break; default: - sBUG_ON(1); + sBUG(); break; } @@ -5683,7 +5683,7 @@ send: break; } default: - sBUG_ON(1); + sBUG(); break; } @@ -5733,7 +5733,7 @@ out_term: 0, 0, 0, 0, 0); break; default: - sBUG_ON(1); + sBUG(); break; } goto out_put; @@ -6355,12 +6355,12 @@ static ssize_t q2t_store_expl_conf_enabled(struct kobject *kobj, switch (buffer[0]) { case '0': ha->enable_explicit_conf = 0; - PRINT_INFO("qla2x00t(%ld): explicit conformations disabled", + PRINT_INFO("qla2x00t(%ld): explicit confirmations disabled", ha->instance); break; case '1': ha->enable_explicit_conf = 1; - PRINT_INFO("qla2x00t(%ld): explicit conformations enabled", + PRINT_INFO("qla2x00t(%ld): explicit confirmations enabled", ha->instance); break; default: diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index e8bae79c9..21c4632d1 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -30,8 +30,8 @@ /* Version numbers, the same as for the kernel */ #define Q2T_VERSION(a, b, c, d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d)) -#define Q2T_VERSION_CODE Q2T_VERSION(3, 0, 0, 0) -#define Q2T_VERSION_STRING "3.0.0-pre2" +#define Q2T_VERSION_CODE Q2T_VERSION(3, 1, 0, 0) +#define Q2T_VERSION_STRING "3.1.0-pre1" #define Q2T_PROC_VERSION_NAME "version" #define Q2T_MAX_CDB_LEN 16 diff --git a/qla2x00t/qla_attr.c b/qla2x00t/qla_attr.c index 993c404e3..1df02d8ee 100644 --- a/qla2x00t/qla_attr.c +++ b/qla2x00t/qla_attr.c @@ -192,12 +192,12 @@ qla2x00_store_expl_conf_enabled(struct device *dev, switch (buffer[0]) { case '0': ha->enable_explicit_conf = 0; - qla_printk(KERN_INFO, ha, "qla2xxx(%ld): explicit conformation " + qla_printk(KERN_INFO, ha, "qla2xxx(%ld): explicit confirmation " "disabled\n", ha->instance); break; case '1': ha->enable_explicit_conf = 1; - qla_printk(KERN_INFO, ha, "qla2xxx(%ld): explicit conformation " + qla_printk(KERN_INFO, ha, "qla2xxx(%ld): explicit confirmation " "enabled\n", ha->instance); break; default: diff --git a/qla_isp/LICENSE b/qla_isp/LICENSE index 5a91588b4..93e55eea9 100644 --- a/qla_isp/LICENSE +++ b/qla_isp/LICENSE @@ -38,8 +38,7 @@ is the GNU Public License: GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. Matthew Jacob diff --git a/qla_isp/common/isp.c b/qla_isp/common/isp.c index a66e95772..6ef236ad8 100644 --- a/qla_isp/common/isp.c +++ b/qla_isp/common/isp.c @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/isp_library.c b/qla_isp/common/isp_library.c index 256cdda41..fc7247e7e 100644 --- a/qla_isp/common/isp_library.c +++ b/qla_isp/common/isp_library.c @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/isp_library.h b/qla_isp/common/isp_library.h index 95fb49dad..9a2f65610 100644 --- a/qla_isp/common/isp_library.h +++ b/qla_isp/common/isp_library.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/isp_stds.h b/qla_isp/common/isp_stds.h index 5c4ee3fcb..432551def 100644 --- a/qla_isp/common/isp_stds.h +++ b/qla_isp/common/isp_stds.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/isp_target.c b/qla_isp/common/isp_target.c index fe8966d0a..b25da91fb 100644 --- a/qla_isp/common/isp_target.c +++ b/qla_isp/common/isp_target.c @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/isp_target.h b/qla_isp/common/isp_target.h index c957d1290..d3d380bd5 100644 --- a/qla_isp/common/isp_target.h +++ b/qla_isp/common/isp_target.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/isp_tpublic.h b/qla_isp/common/isp_tpublic.h index 364d76f1b..f55cdaf13 100644 --- a/qla_isp/common/isp_tpublic.h +++ b/qla_isp/common/isp_tpublic.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/ispmbox.h b/qla_isp/common/ispmbox.h index 9e1a829dd..a4ad35f1d 100644 --- a/qla_isp/common/ispmbox.h +++ b/qla_isp/common/ispmbox.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/ispreg.h b/qla_isp/common/ispreg.h index 1d2894581..a3eb744bb 100644 --- a/qla_isp/common/ispreg.h +++ b/qla_isp/common/ispreg.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/common/ispvar.h b/qla_isp/common/ispvar.h index 008e6ded9..4e627fdf5 100644 --- a/qla_isp/common/ispvar.h +++ b/qla_isp/common/ispvar.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/firmware/fwbin b/qla_isp/firmware/fwbin index fe00b5980..71d282c99 100755 --- a/qla_isp/firmware/fwbin +++ b/qla_isp/firmware/fwbin @@ -42,8 +42,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# along with this program. # # # Matthew Jacob diff --git a/qla_isp/linux-2.6/Makefile b/qla_isp/linux-2.6/Makefile index 5c669c9e1..8a3fdd83c 100644 --- a/qla_isp/linux-2.6/Makefile +++ b/qla_isp/linux-2.6/Makefile @@ -14,8 +14,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# along with this program. # # # Matthew Jacob @@ -53,7 +52,9 @@ extraclean: clean rm -f *.orig *.rej install: - @$(MAKE) -C ${LINUX} M=${CURDIR}/build modules_install + @$(MAKE) -C ${LINUX} M=${CURDIR}/build \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ + modules_install install_host_progs: @$(MAKE) -C build $@ diff --git a/qla_isp/linux-2.6/build/Makefile b/qla_isp/linux-2.6/build/Makefile index 96ba94801..db836c075 100644 --- a/qla_isp/linux-2.6/build/Makefile +++ b/qla_isp/linux-2.6/build/Makefile @@ -12,8 +12,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# along with this program. # # # Matthew Jacob diff --git a/qla_isp/linux/isp_cb_ops.c b/qla_isp/linux/isp_cb_ops.c index b906047d0..fc8ec4b5b 100644 --- a/qla_isp/linux/isp_cb_ops.c +++ b/qla_isp/linux/isp_cb_ops.c @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/linux/isp_ioctl.h b/qla_isp/linux/isp_ioctl.h index 9d64ca277..8184ca9ff 100644 --- a/qla_isp/linux/isp_ioctl.h +++ b/qla_isp/linux/isp_ioctl.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/linux/isp_linux.c b/qla_isp/linux/isp_linux.c index 60049a804..f907c3bec 100644 --- a/qla_isp/linux/isp_linux.c +++ b/qla_isp/linux/isp_linux.c @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/linux/isp_linux.h b/qla_isp/linux/isp_linux.h index 3a10e2588..331d4f896 100644 --- a/qla_isp/linux/isp_linux.h +++ b/qla_isp/linux/isp_linux.h @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/linux/isp_pci.c b/qla_isp/linux/isp_pci.c index c5d2b5bb2..b6d9ffa0e 100644 --- a/qla_isp/linux/isp_pci.c +++ b/qla_isp/linux/isp_pci.c @@ -40,8 +40,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/qla_isp/linux/isp_scst.c b/qla_isp/linux/isp_scst.c index 1d683a0b3..f9e0c7102 100644 --- a/qla_isp/linux/isp_scst.c +++ b/qla_isp/linux/isp_scst.c @@ -39,8 +39,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. * * * Matthew Jacob diff --git a/scripts/generate-patched-kernel b/scripts/generate-patched-kernel index 94d139b77..a0cbdbb96 100755 --- a/scripts/generate-patched-kernel +++ b/scripts/generate-patched-kernel @@ -19,37 +19,24 @@ ############################################################################ -######################## -# Function definitions # -######################## - -source $(dirname $0)/kernel-functions - function usage { echo "Usage: $0 " } -######################### -# Argument verification # -######################### +script_dir="$(dirname $0)" +if [ "${script_dir#/}" = "${script_dir}" ]; then + script_dir="$PWD/$script_dir" +fi +scst_dir="$(dirname "${script_dir}")" -set -e +source "${script_dir}/kernel-functions" if [ "$1" = "" ]; then echo "Error: missing kernel version argument." exit 1 fi - -########################## -# Kernel tree generation # -########################## - -scriptname="$0" -if [ "${scriptname#/}" = "${scriptname}" ]; then - scriptname="$PWD/$scriptname" -fi target="linux-$1" kernel_version="$(kernel_version "$1")" patchlevel="$(patchlevel "$1")" @@ -60,15 +47,13 @@ extract_kernel_tree "$1" || exit $? cd "${target}" || exit $? -list-source-files "$(dirname "$(dirname "$scriptname")")" \ -| grep -- "-${kernel_version}.*.patch$" \ -| grep -v /in-tree/ \ -| while read p - do +list-source-files "${scst_dir}" | + grep -- "-${kernel_version}.*.patch$" | + grep -v /in-tree/ | + while read p; do if [ "${p/readahead-2.6.32.below11.patch//}" = "$p" \ - -o "${patchlevel:-0}" -lt 11 ] - then + -o "${patchlevel:-0}" -lt 11 ]; then echo "==== $p" - patch -p1 <$p + patch -p1 <"${scst_dir}/$p" fi done diff --git a/scripts/kernel-functions b/scripts/kernel-functions index 58751563f..ce527498e 100644 --- a/scripts/kernel-functions +++ b/scripts/kernel-functions @@ -2,7 +2,7 @@ # Shell functions for parsing the Linux kernel version and for downloading # from kernel.org. -kernel_mirror="ftp://ftp.kernel.org/pub/linux/kernel" +kernel_mirror="http://ftp.kernel.org/pub/linux/kernel" kernel_longterm="http://www.kernel.org/pub/linux/kernel" kernel_sources="$HOME/software/downloads" @@ -53,9 +53,9 @@ function download_kernel { test -w "${kernel_sources}" || return $? ( cd "${kernel_sources}" || return $? - if [ "$plevel" = "" ] \ - || download_file "${kernel_mirror}/v$series/patch-$1.xz" \ - || download_file "${kernel_mirror}/v$series/longterm/v${kver}/patch-$1.xz" + if [ "$plevel" = "" -o "$plevel" = "0" ] || + download_file "${kernel_mirror}/v$series/patch-$1.xz" || + download_file "${kernel_mirror}/v$series/longterm/v${kver}/patch-$1.xz" then download_file "${kernel_mirror}/v$series/linux-${kver}.tar.xz" \ || download_file "${kernel_mirror}/v$series/longterm/v${kver}/linux-${kver}.tar.xz" \ @@ -87,14 +87,17 @@ function extract_kernel_tree { mkdir "${tmpdir}" || return $? ( cd "${tmpdir}" || return $? - if [ "$plevel" != "" -a -e "${kernel_sources}/patch-$1.xz" ]; then + if [ "$plevel" != "" -a "$plevel" != "0" -a \ + -e "${kernel_sources}/patch-$1.xz" ]; then extract_kernel_archive $kver || return $? mv linux-$kver linux-$1 ( cd linux-$1 && xz -cd "${kernel_sources}/patch-$1.xz" \ | patch -p1 -f -s; ) \ || return $? else - extract_kernel_archive $1 || return $? + extract_kernel_archive $1 || + { extract_kernel_archive $kver && mv linux-$kver linux-$1; } || + return $? fi mv "linux-$1" ".." || return $? cd "../linux-$1" || return $? diff --git a/scripts/rebuild-rhel-kernel-rpm b/scripts/rebuild-rhel-kernel-rpm index b881fe4db..88c93d16d 100755 --- a/scripts/rebuild-rhel-kernel-rpm +++ b/scripts/rebuild-rhel-kernel-rpm @@ -85,6 +85,9 @@ case "$distro" in srpm_url=("http://ftp.scientificlinux.org/linux/scientific/$releasevermajor$releaseverminor/SRPMS/vendor") fi ;; + "Fedora") + srpm_url="http://ftp.redhat.com/redhat/rhel/rc/7/Server/source/tree/Packages" + ;; *) echo "Unknown type of distribution: $distro" exit 1 @@ -183,7 +186,10 @@ log "Copying SCST patches to the SOURCES directory" cd ${rpmbuild_dir}/SOURCES copy_patch $scst_dir/scst/kernel/rhel/scst_exec_req_fifo-${kver}.patch scst_exec_req_fifo.patch -copy_patch $scst_dir/iscsi-scst/kernel/patches/rhel/put_page_callback-${kver}.patch put_page_callback.patch +f="$scst_dir/iscsi-scst/kernel/patches/rhel/put_page_callback-${kver}.patch" +if [ -e "$f" ]; then + copy_patch "$f" put_page_callback.patch +fi log "Adding SCST patches in kernel.spec" @@ -319,6 +325,49 @@ diff -u SPECS/kernel.spec{.orig,} make ARCH=$Arch %{oldconfig_target} > /dev/null echo "# $Arch" > configs/$i EOF +elif [ ${kver#3.10.0-121} != $kver ]; then +# RHEL/CentOS/SL 7.0 +patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $? +--- kernel.spec.orig 2014-05-23 10:09:17.707202148 +0200 ++++ kernel.spec 2014-05-23 10:15:50.883937952 +0200 +@@ -4,6 +4,7 @@ + Summary: The Linux kernel + + # % define buildid .local ++%define buildid .scst + + # For a stable, released kernel, released_kernel should be 1. For rawhide + # and/or a kernel built from an rc or git snapshot, released_kernel should +@@ -367,6 +368,9 @@ + Source2000: cpupower.service + Source2001: cpupower.config + ++Patch200: scst_exec_req_fifo.patch ++#Patch201: put_page_callback.patch ++ + # empty final patch to facilitate testing of kernel patches + Patch999999: linux-kernel-test.patch + +@@ -668,6 +672,9 @@ + # Drop some necessary files from the source dir into the buildroot + cp $RPM_SOURCE_DIR/kernel-%{version}-*.config . + ++ApplyPatch scst_exec_req_fifo.patch ++#ApplyPatch put_page_callback.patch ++ + ApplyOptionalPatch linux-kernel-test.patch + + # Any further pre-build tree manipulations happen here. +@@ -700,6 +707,8 @@ + for i in *.config + do + mv $i .config ++ echo "CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION=y" >> .config ++ sed -i.tmp -e 's/^CONFIG_SCSI_QLA_FC=.*/CONFIG_SCSI_QLA_FC=n/' .config + Arch=`head -1 .config | cut -b 3-` + make %{?cross_opts} ARCH=$Arch listnewconfig | grep -E '^CONFIG_' >.newoptions || true + %if %{listnewconfig_fail} +EOF else log "Unrecognized kernel version ${kver}" fi @@ -327,7 +376,7 @@ log "Rebuilding kernel" cd ${rpmbuild_dir}/SPECS { - rpmbuild -bb --target=${arch} --with baseonly --with firmware --without kabichk kernel*.spec + rpmbuild -bb --target=${arch} --nodeps --with baseonly --with firmware --without kabichk kernel*.spec rc=$? if [ $rc != 0 ]; then exit $rc diff --git a/scripts/run-regression-tests b/scripts/run-regression-tests index bae2ad0a5..f792254c4 100755 --- a/scripts/run-regression-tests +++ b/scripts/run-regression-tests @@ -263,30 +263,32 @@ CONFIG_TRACING CONFIG_X86_32 \ " echo "Patching and configuring kernel ..." + if [ "$ipv6" = "false" ]; then + disable="$disable CONFIG_IPV6" + fi ( local srcdir="$PWD" - cd "${outputdir}/linux-$1" \ - && if [ "${multiple_patches}" = "false" ]; then - patch -p1 -f -s <"${patchfile}" >"${patchoutput}" - else - rm -f "${patchoutput}" - for p in "${outputdir}/${patchdir}"/* - do - echo "==== $p" >>"${patchoutput}" - patch -p1 -f -s <"${p}" >>"${patchoutput}" 2>&1 - done - fi \ - && if [ -e $srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ]; then - echo "$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ..." \ + cd "${outputdir}/linux-$1" && + if [ "${multiple_patches}" = "false" ]; then + patch -p1 -f -s <"${patchfile}" >"${patchoutput}" + else + rm -f "${patchoutput}" + for p in "${outputdir}/${patchdir}"/*; do + echo "==== $p" >>"${patchoutput}" + patch -p1 -f -s <"${p}" >>"${patchoutput}" 2>&1 + done + fi && + if [ -e $srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ]; then + echo "$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ..." \ >>"${patchoutput}" - patch -p1 -f -s <$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch \ - >>"${patchoutput}"; - else - echo "srpt/patches/kernel-${kver}-pre-cflags.patch not found."; \ - fi \ - && make -s allmodconfig &>"${outputdir}/make-config-output.txt" \ - && for c in $disable; do sed -i.tmp "s/^$c=y\$/$c=n/" .config; done \ - && make -s oldconfig &>/dev/null + patch -p1 -f -s <$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch \ + >>"${patchoutput}" + else + echo "srpt/patches/kernel-${kver}-pre-cflags.patch not found." + fi && + make -s allmodconfig &>"${outputdir}/make-config-output.txt" && + for c in $disable; do sed -i.tmp "s/^$c=[ym]\$/$c=n/" .config; done && + make -s oldconfig &>/dev/null ) } @@ -492,7 +494,7 @@ fi # Where to store persistenly downloaded kernel tarballs and kernel patches. kernel_sources="$HOME/software/downloads" # URL for downloading kernel tarballs and kernel patches. -kernel_mirror="ftp://ftp.kernel.org/pub/linux/kernel" +kernel_mirror="http://ftp.kernel.org/pub/linux/kernel" kernel_longterm="http://www.kernel.org/pub/linux/kernel" kernel_versions="" # Directory in which the regression test output files will be stored. Must be @@ -585,6 +587,7 @@ do run_checkpatch="true" run_sparse="true" run_smatch="true" + ipv6="true" global_multiple_patches="${multiple_patches}" while [ "${kv%-?}" != "${kv}" -o "${kv%-??}" != "${kv}" ]; do kv_without_opt="${kv%-?}" @@ -593,6 +596,7 @@ do fi kopt="${kv#${kv_without_opt}}" case "${kopt}" in + '-4') ipv6="false";; '-f') full_check="true";; '-i') ibmvio="true";; '-nc') run_checkpatch="false";; diff --git a/scst.spec.in b/scst.spec.in index 579888487..535875b0d 100644 --- a/scst.spec.in +++ b/scst.spec.in @@ -90,6 +90,8 @@ rm -f /usr/local/man/man8/iscsi-scstd.8 rm -f /usr/local/sbin/iscsi-scst-adm rm -f /usr/local/sbin/iscsi-scstd rm -rf /usr/local/include/scst +# Remove existing ib_srpt.ko kernel modules +find /lib/modules/%{kver} -name ib_srpt.ko -exec rm {} \; %post /sbin/depmod -a %{kver} diff --git a/scst/COPYING b/scst/COPYING index 6fa77f597..d2469d5dd 100644 --- a/scst/COPYING +++ b/scst/COPYING @@ -2,7 +2,6 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -304,8 +303,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + along with this program. Also add information on how to contact you by electronic and paper mail. diff --git a/scst/README b/scst/README index 1f42e569e..110630d72 100644 --- a/scst/README +++ b/scst/README @@ -1,7 +1,7 @@ Generic SCSI target mid-level for Linux (SCST) ============================================== -Version 3.0.0, XX XXXXX 2014 +Version 3.1.0, XX XXXXX 2014 ---------------------------- SCST is designed to provide unified, consistent interface between SCSI @@ -982,7 +982,7 @@ Intended to be used for performance measurements at the same way as blocksize, read_only, removable, tst. See vdisk_fileio above for description of those parameters. -vdisk_nullio also has extra attribute: +vdisk_nullio devices have the following two additional attributes: - dummy - if this flag is set, LUNs corresponding to this device will not appear at the initiator side. This is because SCST will set the @@ -991,6 +991,13 @@ vdisk_nullio also has extra attribute: See also SPC-4 for more information. It is designed to be used as a "dummy" placeholder on LUN 0, if LUN 0 is not desired. + - read_zero - if this flag is set, reading from a vdisk_nullio device + returns a buffer filled with byte 0x00. If this flag is cleared + (which is the default behavior), the buffer returned to the + initiator is not cleared. Although this results in slightly faster + operation this is a security hole since any data that is present in + kernel memory can be returned to the initiator. + Handler vcdrom allows emulation of a virtual CDROM device using an ISO file as backend. It has only single parameter: tst. diff --git a/scst/README_in-tree b/scst/README_in-tree index 739647867..18b10cded 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -840,7 +840,7 @@ Intended to be used for performance measurements at the same way as blocksize, read_only, removable, tst. See vdisk_fileio above for description of those parameters. -vdisk_nullio also has extra attribute: +vdisk_nullio devices have the following two additional attributes: - dummy - if this flag is set, LUNs corresponding to this device will not appear at the initiator side. This is because SCST will set the @@ -849,6 +849,13 @@ vdisk_nullio also has extra attribute: See also SPC-4 for more information. It is designed to be used as a "dummy" placeholder on LUN 0, if LUN 0 is not desired. + - read_zero - if this flag is set, reading from a vdisk_nullio device + returns a buffer filled with byte 0x00. If this flag is cleared + (which is the default behavior), the buffer returned to the + initiator is not cleared. Although this results in slightly faster + operation this is a security hole since any data that is present in + kernel memory can be returned to the initiator. + Handler vcdrom allows emulation of a virtual CDROM device using an ISO file as backend. It has only single parameter: tst. diff --git a/scst/include/scst.h b/scst/include/scst.h index 66cb6d685..28e79fc02 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -79,6 +79,14 @@ typedef _Bool bool; #define __aligned __attribute__((aligned)) #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +#define lockdep_assert_held(l) do { (void)(l); } while (0) +#endif + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) #ifndef O_DSYNC #define O_DSYNC O_SYNC @@ -119,13 +127,6 @@ typedef _Bool bool; #define nr_cpumask_bits NR_CPUS #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -#ifndef swap -#define swap(a, b) \ - do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) -#endif -#endif - /* verify cpu argument to cpumask_* operators */ static inline unsigned int cpumask_check(unsigned int cpu) { @@ -172,6 +173,27 @@ static inline void cpumask_copy(cpumask_t *dstp, { bitmap_copy(cpumask_bits(dstp), cpumask_bits(srcp), nr_cpumask_bits); } + +/** + * cpumask_setall - set all cpus (< nr_cpu_ids) in a cpumask + * @dstp: the cpumask pointer + */ +static inline void cpumask_setall(cpumask_t *dstp) +{ + bitmap_fill(cpumask_bits(dstp), nr_cpumask_bits); +} + +/** + * cpumask_equal - *src1p == *src2p + * @src1p: the first input + * @src2p: the second input + */ +static inline bool cpumask_equal(const cpumask_t *src1p, + const cpumask_t *src2p) +{ + return bitmap_equal(cpumask_bits(src1p), cpumask_bits(src2p), + nr_cpumask_bits); +} #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) && \ @@ -179,6 +201,13 @@ static inline void cpumask_copy(cpumask_t *dstp, #define set_cpus_allowed_ptr(p, new_mask) set_cpus_allowed((p), *(new_mask)) #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) +#ifndef swap +#define swap(a, b) \ + do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) +#endif +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) static inline unsigned int queue_max_hw_sectors(struct request_queue *q) { @@ -186,6 +215,16 @@ static inline unsigned int queue_max_hw_sectors(struct request_queue *q) } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +/* + * See also patch "kernel.h: add pr_warn for symmetry to dev_warn, + * netdev_warn" (commit fc62f2f19edf46c9bdbd1a54725b56b18c43e94f). + */ +#ifndef pr_warn +#define pr_warn pr_warning +#endif +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) /* * See also patch "sched: Fix softirq time accounting" (commit ID @@ -1909,6 +1948,13 @@ struct scst_order_data { spinlock_t init_done_lock; }; +struct scst_orig_sg_data { + int *p_orig_sg_cnt; + int orig_sg_cnt; + struct scatterlist *orig_sg_entry; + int orig_entry_offs, orig_entry_len; +}; + /* * SCST command, analog of I_T_L_Q nexus or task */ @@ -1998,9 +2044,7 @@ struct scst_cmd { /* Set if the target driver called scst_set_expected() */ unsigned int expected_values_set:1; - /* - * Set if the SG buffer was modified by scst_adjust_sg() - */ + /* Set if the SG buffer was modified by scst_adjust_sg() */ unsigned int sg_buff_modified:1; /* @@ -2229,11 +2273,8 @@ struct scst_cmd { /* Used for storage of dev handler private stuff */ void *dh_priv; - /* Used to restore sg if it was modified by scst_adjust_sg() */ - int *p_orig_sg_cnt; - int orig_sg_cnt; - struct scatterlist *orig_sg_entry; - int orig_entry_offs, orig_entry_len; + /* List entry for dev's blocked_cmd_list */ + struct list_head blocked_cmd_list_entry; /* Used to retry commands in case of double UA */ int dbl_ua_orig_resp_data_len, dbl_ua_orig_data_direction; @@ -2244,18 +2285,24 @@ struct scst_cmd { */ struct list_head mgmt_cmd_list; - /* List entry for dev's blocked_cmd_list */ - struct list_head blocked_cmd_list_entry; + /* Used to restore sg if it was modified by scst_adjust_sg() */ + struct scst_orig_sg_data orig_sg; - /* Counter of the corresponding SCST_PR_ABORT_ALL TM commands */ - struct scst_pr_abort_all_pending_mgmt_cmds_counter *pr_abort_counter; + /* Per opcode stuff */ + union { + /* Counter of the corresponding SCST_PR_ABORT_ALL TM commands */ + struct scst_pr_abort_all_pending_mgmt_cmds_counter *pr_abort_counter; - /* - * List of parsed data descriptors for commands operating with - * several lba and data_len pairs, like UNMAP, and its size in elements. - */ - void *cmd_data_descriptors; - int cmd_data_descriptors_cnt; + /* + * List of parsed data descriptors for commands operating with + * several lba and data_len pairs, like UNMAP, and its size + * in elements. + */ + struct { + void *cmd_data_descriptors; + int cmd_data_descriptors_cnt; + }; + }; #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) char not_parsed_op_name[8]; @@ -2780,6 +2827,9 @@ struct scst_acg_dev { * control information. */ struct scst_acg { + /* One more than the number of sessions in acg_sess_list */ + struct kref acg_kref; + /* Owner target */ struct scst_tgt *tgt; @@ -4277,11 +4327,16 @@ static inline int cancel_delayed_work_sync(struct delayed_work *work) #endif #endif -#ifdef CONFIG_DEBUG_LOCK_ALLOC +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && \ + defined(CONFIG_DEBUG_LOCK_ALLOC) extern struct lockdep_map scst_suspend_dep_map; #define scst_assert_activity_suspended() \ WARN_ON(debug_locks && !lock_is_held(&scst_suspend_dep_map)); #else +/* + * See also patch "lockdep: Introduce lockdep_assert_held()" (commit ID + * f607c6685774811b8112e124f10a053d77015485) + */ #define scst_assert_activity_suspended() do { } while (0) #endif diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index da40be827..3b0e57201 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -42,13 +42,13 @@ * and FIO_REV in usr/fileio/common.h as well. */ #define SCST_VERSION(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + d) -#define SCST_VERSION_CODE SCST_VERSION(3, 0, 0, 0) +#define SCST_VERSION_CODE SCST_VERSION(3, 1, 0, 0) #ifdef CONFIG_SCST_PROC #define SCST_VERSION_STRING_SUFFIX "-procfs" #else #define SCST_VERSION_STRING_SUFFIX #endif -#define SCST_VERSION_NAME "3.0.0-pre2" +#define SCST_VERSION_NAME "3.1.0-pre1" #define SCST_VERSION_STRING SCST_VERSION_NAME SCST_VERSION_STRING_SUFFIX #define SCST_CONST_VERSION "$Revision$" diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index 29b7c6c5f..11a4e5fff 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -116,11 +116,13 @@ #endif #endif -#ifdef CONFIG_SCST_EXTRACHECKS +#if defined(CONFIG_SCST_EXTRACHECKS) || defined(__COVERITY__) +#define EXTRACHECKS_BUG() sBUG() #define EXTRACHECKS_BUG_ON(a) sBUG_ON(a) #define EXTRACHECKS_WARN_ON(a) WARN_ON(a) #define EXTRACHECKS_WARN_ON_ONCE(a) WARN_ON_ONCE(a) #else +#define EXTRACHECKS_BUG() do { } while (0) #define EXTRACHECKS_BUG_ON(a) do { } while (0) #define EXTRACHECKS_WARN_ON(a) do { } while (0) #define EXTRACHECKS_WARN_ON_ONCE(a) do { } while (0) diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-3.15.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.15.patch new file mode 100644 index 000000000..0d5a19f0f --- /dev/null +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.15.patch @@ -0,0 +1,13 @@ +diff --git a/drivers/Kconfig b/drivers/Kconfig +index aa43b91..c96860e 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig" + + source "drivers/scsi/Kconfig" + ++source "drivers/scst/Kconfig" ++ + source "drivers/ata/Kconfig" + + source "drivers/md/Kconfig" diff --git a/scst/kernel/in-tree/Makefile.dev_handlers-3.15 b/scst/kernel/in-tree/Makefile.dev_handlers-3.15 new file mode 100644 index 000000000..f933b36f7 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.dev_handlers-3.15 @@ -0,0 +1,14 @@ +ccflags-y += -Wno-unused-parameter + +obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \ + scst_vdisk.o scst_raid.o scst_processor.o scst_user.o + +obj-$(CONFIG_SCST_DISK) += scst_disk.o +obj-$(CONFIG_SCST_TAPE) += scst_tape.o +obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o +obj-$(CONFIG_SCST_MODISK) += scst_modisk.o +obj-$(CONFIG_SCST_CHANGER) += scst_changer.o +obj-$(CONFIG_SCST_RAID) += scst_raid.o +obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o +obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o +obj-$(CONFIG_SCST_USER) += scst_user.o diff --git a/scst/kernel/in-tree/Makefile.drivers.Linux-3.15.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-3.15.patch new file mode 100644 index 000000000..f7213ed4c --- /dev/null +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-3.15.patch @@ -0,0 +1,12 @@ +diff --git a/drivers/Makefile b/drivers/Makefile +index ab93de8..45077ec 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -128,6 +128,7 @@ obj-$(CONFIG_SSB) += ssb/ + obj-$(CONFIG_BCMA) += bcma/ + obj-$(CONFIG_VHOST_RING) += vhost/ + obj-$(CONFIG_VLYNQ) += vlynq/ ++obj-$(CONFIG_SCST) += scst/ + obj-$(CONFIG_STAGING) += staging/ + obj-y += platform/ + #common clk code diff --git a/scst/kernel/in-tree/Makefile.scst-3.15 b/scst/kernel/in-tree/Makefile.scst-3.15 new file mode 100644 index 000000000..53af5f388 --- /dev/null +++ b/scst/kernel/in-tree/Makefile.scst-3.15 @@ -0,0 +1,13 @@ +ccflags-y += -Wno-unused-parameter + +scst-y += scst_main.o +scst-y += scst_pres.o +scst-y += scst_targ.o +scst-y += scst_lib.o +scst-y += scst_sysfs.o +scst-y += scst_mem.o +scst-y += scst_tg.o +scst-y += scst_debug.o + +obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \ + srpt/ scst_local/ diff --git a/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.el7.patch b/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.el7.patch new file mode 120000 index 000000000..6a3acd053 --- /dev/null +++ b/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.el7.patch @@ -0,0 +1 @@ +../scst_exec_req_fifo-3.10.patch \ No newline at end of file diff --git a/scst/kernel/scst_exec_req_fifo-3.15.patch b/scst/kernel/scst_exec_req_fifo-3.15.patch new file mode 100644 index 000000000..665cc2606 --- /dev/null +++ b/scst/kernel/scst_exec_req_fifo-3.15.patch @@ -0,0 +1,528 @@ +=== modified file 'block/blk-map.c' +--- old/block/blk-map.c 2014-06-18 01:32:48 +0000 ++++ new/block/blk-map.c 2014-06-18 01:40:34 +0000 +@@ -5,6 +5,8 @@ + #include + #include + #include ++#include ++#include + #include /* for struct sg_iovec */ + + #include "blk.h" +@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio) + } + EXPORT_SYMBOL(blk_rq_unmap_user); + ++struct blk_kern_sg_work { ++ atomic_t bios_inflight; ++ struct sg_table sg_table; ++ struct scatterlist *src_sgl; ++}; ++ ++static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw) ++{ ++ struct sg_table *sgt = &bw->sg_table; ++ struct scatterlist *sg; ++ int i; ++ ++ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { ++ struct page *pg = sg_page(sg); ++ if (pg == NULL) ++ break; ++ __free_page(pg); ++ } ++ ++ sg_free_table(sgt); ++ kfree(bw); ++ return; ++} ++ ++static void blk_bio_map_kern_endio(struct bio *bio, int err) ++{ ++ struct blk_kern_sg_work *bw = bio->bi_private; ++ ++ if (bw != NULL) { ++ /* Decrement the bios in processing and, if zero, free */ ++ BUG_ON(atomic_read(&bw->bios_inflight) <= 0); ++ if (atomic_dec_and_test(&bw->bios_inflight)) { ++ if ((bio_data_dir(bio) == READ) && (err == 0)) { ++ unsigned long flags; ++ ++ local_irq_save(flags); /* to protect KMs */ ++ sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0); ++ local_irq_restore(flags); ++ } ++ blk_free_kern_sg_work(bw); ++ } ++ } ++ ++ bio_put(bio); ++ return; ++} ++ ++static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work **pbw, ++ gfp_t gfp, gfp_t page_gfp) ++{ ++ int res = 0, i; ++ struct scatterlist *sg; ++ struct scatterlist *new_sgl; ++ int new_sgl_nents; ++ size_t len = 0, to_copy; ++ struct blk_kern_sg_work *bw; ++ ++ bw = kzalloc(sizeof(*bw), gfp); ++ if (bw == NULL) ++ goto out; ++ ++ bw->src_sgl = sgl; ++ ++ for_each_sg(sgl, sg, nents, i) ++ len += sg->length; ++ to_copy = len; ++ ++ new_sgl_nents = PFN_UP(len); ++ ++ res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp); ++ if (res != 0) ++ goto err_free; ++ ++ new_sgl = bw->sg_table.sgl; ++ ++ for_each_sg(new_sgl, sg, new_sgl_nents, i) { ++ struct page *pg; ++ ++ pg = alloc_page(page_gfp); ++ if (pg == NULL) ++ goto err_free; ++ ++ sg_assign_page(sg, pg); ++ sg->length = min_t(size_t, PAGE_SIZE, len); ++ ++ len -= PAGE_SIZE; ++ } ++ ++ if (rq_data_dir(rq) == WRITE) { ++ /* ++ * We need to limit amount of copied data to to_copy, because ++ * sgl might have the last element in sgl not marked as last in ++ * SG chaining. ++ */ ++ sg_copy(new_sgl, sgl, 0, to_copy); ++ } ++ ++ *pbw = bw; ++ /* ++ * REQ_COPY_USER name is misleading. It should be something like ++ * REQ_HAS_TAIL_SPACE_FOR_PADDING. ++ */ ++ rq->cmd_flags |= REQ_COPY_USER; ++ ++out: ++ return res; ++ ++err_free: ++ blk_free_kern_sg_work(bw); ++ res = -ENOMEM; ++ goto out; ++} ++ ++static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, struct blk_kern_sg_work *bw, gfp_t gfp) ++{ ++ int res; ++ struct request_queue *q = rq->q; ++ int rw = rq_data_dir(rq); ++ int max_nr_vecs, i; ++ size_t tot_len; ++ bool need_new_bio; ++ struct scatterlist *sg, *prev_sg = NULL; ++ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL; ++ int bios; ++ ++ if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) { ++ WARN_ON(1); ++ res = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Let's keep each bio allocation inside a single page to decrease ++ * probability of failure. ++ */ ++ max_nr_vecs = min_t(size_t, ++ ((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)), ++ BIO_MAX_PAGES); ++ ++ need_new_bio = true; ++ tot_len = 0; ++ bios = 0; ++ for_each_sg(sgl, sg, nents, i) { ++ struct page *page = sg_page(sg); ++ void *page_addr = page_address(page); ++ size_t len = sg->length, l; ++ size_t offset = sg->offset; ++ ++ tot_len += len; ++ prev_sg = sg; ++ ++ /* ++ * Each segment must be aligned on DMA boundary and ++ * not on stack. The last one may have unaligned ++ * length as long as the total length is aligned to ++ * DMA padding alignment. ++ */ ++ if (i == nents - 1) ++ l = 0; ++ else ++ l = len; ++ if (((sg->offset | l) & queue_dma_alignment(q)) || ++ (page_addr && object_is_on_stack(page_addr + sg->offset))) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ while (len > 0) { ++ size_t bytes; ++ int rc; ++ ++ if (need_new_bio) { ++ bio = bio_kmalloc(gfp, max_nr_vecs); ++ if (bio == NULL) { ++ res = -ENOMEM; ++ goto out_free_bios; ++ } ++ ++ if (rw == WRITE) ++ bio->bi_rw |= REQ_WRITE; ++ ++ bios++; ++ bio->bi_private = bw; ++ bio->bi_end_io = blk_bio_map_kern_endio; ++ ++ if (hbio == NULL) ++ hbio = tbio = bio; ++ else ++ tbio = tbio->bi_next = bio; ++ } ++ ++ bytes = min_t(size_t, len, PAGE_SIZE - offset); ++ ++ rc = bio_add_pc_page(q, bio, page, bytes, offset); ++ if (rc < bytes) { ++ if (unlikely(need_new_bio || (rc < 0))) { ++ if (rc < 0) ++ res = rc; ++ else ++ res = -EIO; ++ goto out_free_bios; ++ } else { ++ need_new_bio = true; ++ len -= rc; ++ offset += rc; ++ continue; ++ } ++ } ++ ++ need_new_bio = false; ++ offset = 0; ++ len -= bytes; ++ page = nth_page(page, 1); ++ } ++ } ++ ++ if (hbio == NULL) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ /* Total length must be aligned on DMA padding alignment */ ++ if ((tot_len & q->dma_pad_mask) && ++ !(rq->cmd_flags & REQ_COPY_USER)) { ++ res = -EINVAL; ++ goto out_free_bios; ++ } ++ ++ if (bw != NULL) ++ atomic_set(&bw->bios_inflight, bios); ++ ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio->bi_next = NULL; ++ ++ blk_queue_bounce(q, &bio); ++ ++ res = blk_rq_append_bio(q, rq, bio); ++ if (unlikely(res != 0)) { ++ bio->bi_next = hbio; ++ hbio = bio; ++ /* We can have one or more bios bounced */ ++ goto out_unmap_bios; ++ } ++ } ++ ++ res = 0; ++ ++ rq->buffer = NULL; ++out: ++ return res; ++ ++out_unmap_bios: ++ blk_rq_unmap_kern_sg(rq, res); ++ ++out_free_bios: ++ while (hbio != NULL) { ++ bio = hbio; ++ hbio = hbio->bi_next; ++ bio_put(bio); ++ } ++ goto out; ++} ++ ++/** ++ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC ++ * @rq: request to fill ++ * @sgl: area to map ++ * @nents: number of elements in @sgl ++ * @gfp: memory allocation flags ++ * ++ * Description: ++ * Data will be mapped directly if possible. Otherwise a bounce ++ * buffer will be used. ++ */ ++int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp) ++{ ++ int res; ++ ++ res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp); ++ if (unlikely(res != 0)) { ++ struct blk_kern_sg_work *bw = NULL; ++ ++ res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw, ++ gfp, rq->q->bounce_gfp | gfp); ++ if (unlikely(res != 0)) ++ goto out; ++ ++ res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl, ++ bw->sg_table.nents, bw, gfp); ++ if (res != 0) { ++ blk_free_kern_sg_work(bw); ++ goto out; ++ } ++ } ++ ++ rq->buffer = NULL; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(blk_rq_map_kern_sg); ++ ++/** ++ * blk_rq_unmap_kern_sg - unmap a request with kernel sg ++ * @rq: request to unmap ++ * @err: non-zero error code ++ * ++ * Description: ++ * Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called ++ * only in case of an error! ++ */ ++void blk_rq_unmap_kern_sg(struct request *rq, int err) ++{ ++ struct bio *bio = rq->bio; ++ ++ while (bio) { ++ struct bio *b = bio; ++ bio = bio->bi_next; ++ b->bi_end_io(b, err); ++ } ++ rq->bio = NULL; ++ ++ return; ++} ++EXPORT_SYMBOL(blk_rq_unmap_kern_sg); ++ + /** + * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage + * @q: request queue where request should be inserted + +=== modified file 'include/linux/blkdev.h' +--- old/include/linux/blkdev.h 2014-06-18 01:32:48 +0000 ++++ new/include/linux/blkdev.h 2014-06-18 01:40:34 +0000 +@@ -717,6 +717,8 @@ extern unsigned long blk_max_low_pfn, bl + #define BLK_DEFAULT_SG_TIMEOUT (60 * HZ) + #define BLK_MIN_SG_TIMEOUT (7 * HZ) + ++#define SCSI_EXEC_REQ_FIFO_DEFINED ++ + #ifdef CONFIG_BOUNCE + extern int init_emergency_isa_pool(void); + extern void blk_queue_bounce(struct request_queue *q, struct bio **bio); +@@ -837,6 +839,9 @@ extern int blk_rq_map_kern(struct reques + extern int blk_rq_map_user_iov(struct request_queue *, struct request *, + struct rq_map_data *, const struct sg_iovec *, + int, unsigned int, gfp_t); ++extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl, ++ int nents, gfp_t gfp); ++extern void blk_rq_unmap_kern_sg(struct request *rq, int err); + extern int blk_execute_rq(struct request_queue *, struct gendisk *, + struct request *, int); + extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, + +=== modified file 'include/linux/scatterlist.h' +--- old/include/linux/scatterlist.h 2014-06-18 01:32:48 +0000 ++++ new/include/linux/scatterlist.h 2014-06-18 01:40:34 +0000 +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + struct sg_table { + struct scatterlist *sgl; /* the list */ +@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt + size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, + void *buf, size_t buflen, off_t skip); + ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len); ++ + /* + * Maximum number of entries that will be allocated in one piece, if + * a list larger than this is required then chaining will be utilized. + +=== modified file 'lib/scatterlist.c' +--- old/lib/scatterlist.c 2014-06-18 01:32:48 +0000 ++++ new/lib/scatterlist.c 2014-06-18 01:40:34 +0000 +@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter + return sg_copy_buffer(sgl, nents, buf, buflen, skip, true); + } + EXPORT_SYMBOL(sg_pcopy_to_buffer); ++ ++ ++/* ++ * Can switch to the next dst_sg element, so, to copy to strictly only ++ * one dst_sg element, it must be either last in the chain, or ++ * copy_len == dst_sg->length. ++ */ ++static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len, ++ size_t *pdst_offs, struct scatterlist *src_sg, ++ size_t copy_len) ++{ ++ int res = 0; ++ struct scatterlist *dst_sg; ++ size_t src_len, dst_len, src_offs, dst_offs; ++ struct page *src_page, *dst_page; ++ ++ dst_sg = *pdst_sg; ++ dst_len = *pdst_len; ++ dst_offs = *pdst_offs; ++ dst_page = sg_page(dst_sg); ++ ++ src_page = sg_page(src_sg); ++ src_len = src_sg->length; ++ src_offs = src_sg->offset; ++ ++ do { ++ void *saddr, *daddr; ++ size_t n; ++ ++ saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) + ++ (src_offs & ~PAGE_MASK); ++ daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) + ++ (dst_offs & ~PAGE_MASK); ++ ++ if (((src_offs & ~PAGE_MASK) == 0) && ++ ((dst_offs & ~PAGE_MASK) == 0) && ++ (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) && ++ (copy_len >= PAGE_SIZE)) { ++ copy_page(daddr, saddr); ++ n = PAGE_SIZE; ++ } else { ++ n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK), ++ PAGE_SIZE - (src_offs & ~PAGE_MASK)); ++ n = min(n, src_len); ++ n = min(n, dst_len); ++ n = min_t(size_t, n, copy_len); ++ memcpy(daddr, saddr, n); ++ } ++ dst_offs += n; ++ src_offs += n; ++ ++ kunmap_atomic(saddr); ++ kunmap_atomic(daddr); ++ ++ res += n; ++ copy_len -= n; ++ if (copy_len == 0) ++ goto out; ++ ++ src_len -= n; ++ dst_len -= n; ++ if (dst_len == 0) { ++ dst_sg = sg_next(dst_sg); ++ if (dst_sg == NULL) ++ goto out; ++ dst_page = sg_page(dst_sg); ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ } ++ } while (src_len > 0); ++ ++out: ++ *pdst_sg = dst_sg; ++ *pdst_len = dst_len; ++ *pdst_offs = dst_offs; ++ return res; ++} ++ ++/** ++ * sg_copy - copy one SG vector to another ++ * @dst_sg: destination SG ++ * @src_sg: source SG ++ * @nents_to_copy: maximum number of entries to copy ++ * @copy_len: maximum amount of data to copy. If 0, then copy all. ++ * ++ * Description: ++ * Data from the source SG vector will be copied to the destination SG ++ * vector. End of the vectors will be determined by sg_next() returning ++ * NULL. Returns number of bytes copied. ++ */ ++int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, ++ int nents_to_copy, size_t copy_len) ++{ ++ int res = 0; ++ size_t dst_len, dst_offs; ++ ++ if (copy_len == 0) ++ copy_len = 0x7FFFFFFF; /* copy all */ ++ ++ if (nents_to_copy == 0) ++ nents_to_copy = 0x7FFFFFFF; /* copy all */ ++ ++ dst_len = dst_sg->length; ++ dst_offs = dst_sg->offset; ++ ++ do { ++ int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs, ++ src_sg, copy_len); ++ copy_len -= copied; ++ res += copied; ++ if ((copy_len == 0) || (dst_sg == NULL)) ++ goto out; ++ ++ nents_to_copy--; ++ if (nents_to_copy == 0) ++ goto out; ++ ++ src_sg = sg_next(src_sg); ++ } while (src_sg != NULL); ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(sg_copy); + diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index 464ff06db..f3f8b960d 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -73,6 +73,7 @@ all: install: all mkdir -p $(DESTDIR)/var/lib/scst/vdev_mode_pages $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install uninstall: diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index 1d4b34faf..ec4e85bc8 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -214,9 +214,9 @@ static int tape_attach(struct scst_device *dev) mode = (buffer[2] & 0x70) >> 4; speed = buffer[2] & 0x0f; density = buffer[4]; - TRACE_DBG("Tape: lun %d. bs %d. type 0x%02x mode 0x%02x " - "speed 0x%02x dens 0x%02x", dev->scsi_dev->lun, - dev->block_size, medium_type, mode, speed, density); + TRACE_DBG("Tape: lun %lld. bs %d. type 0x%02x mode 0x%02x " + "speed 0x%02x dens 0x%02x", (u64)dev->scsi_dev->lun, + dev->block_size, medium_type, mode, speed, density); } else { PRINT_ERROR("MODE_SENSE failed: %x", rc); res = -ENODEV; diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index e2cf60d26..9f8d9e316 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -826,7 +826,6 @@ static int dev_user_parse(struct scst_cmd *cmd) default: sBUG(); - goto out; } done: @@ -1911,7 +1910,7 @@ again: dev_user_unjam_cmd(u, 0, NULL); goto again; case UCMD_STATE_EXECING: - EXTRACHECKS_BUG_ON(1); + EXTRACHECKS_BUG(); } } } diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index e06591426..f1d752b15 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -80,7 +80,7 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define SCST_FIO_VENDOR "SCST_FIO" #define SCST_BIO_VENDOR "SCST_BIO" /* 4 byte ASCII Product Revision Level - left aligned */ -#define SCST_FIO_REV " 300" +#define SCST_FIO_REV " 310" #define MAX_USN_LEN (20+1) /* For '\0' */ #define MAX_INQ_VEND_SPECIFIC_LEN (INQ_BUF_SZ - 96) @@ -109,6 +109,7 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define DEF_NV_CACHE 0 #define DEF_O_DIRECT 0 #define DEF_DUMMY 0 +#define DEF_READ_ZERO 0 #define DEF_REMOVABLE 0 #define DEF_ROTATIONAL 1 #define DEF_THIN_PROVISIONED 0 @@ -160,6 +161,7 @@ struct scst_vdisk_dev { unsigned int blockio:1; unsigned int cdrom_empty:1; unsigned int dummy:1; + unsigned int read_zero:1; unsigned int removable:1; unsigned int thin_provisioned:1; unsigned int thin_provisioned_manually_set:1; @@ -168,6 +170,7 @@ struct scst_vdisk_dev { unsigned int wt_flag_saved:1; unsigned int tst:3; unsigned int format_active:1; + unsigned int discard_zeroes_data:1; struct file *fd; struct block_device *bdev; @@ -200,6 +203,9 @@ struct scst_vdisk_dev { uint8_t inq_vend_specific[MAX_INQ_VEND_SPECIFIC_LEN]; int inq_vend_specific_len; + /* Unmap INQUIRY parameters */ + uint32_t unmap_opt_gran, unmap_align, unmap_max_lba_cnt; + struct scst_device *dev; struct list_head vdev_list_entry; @@ -349,6 +355,10 @@ static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdev_sysfs_dummy_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_rz_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_rz_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t vdev_sysfs_filename_show(struct kobject *kobj, @@ -421,6 +431,9 @@ static struct kobj_attribute vdisk_o_direct_attr = __ATTR(o_direct, S_IRUGO, vdisk_sysfs_o_direct_show, NULL); static struct kobj_attribute vdev_dummy_attr = __ATTR(dummy, S_IRUGO, vdev_sysfs_dummy_show, NULL); +static struct kobj_attribute vdev_read_zero_attr = + __ATTR(read_zero, S_IWUSR|S_IRUGO, vdev_sysfs_rz_show, + vdev_sysfs_rz_store); static struct kobj_attribute vdisk_removable_attr = __ATTR(removable, S_IRUGO, vdisk_sysfs_removable_show, NULL); static struct kobj_attribute vdisk_filename_attr = @@ -516,6 +529,7 @@ static const struct attribute *vdisk_nullio_attrs[] = { &vdisk_rd_only_attr.attr, &vdisk_tst_attr.attr, &vdev_dummy_attr.attr, + &vdev_read_zero_attr.attr, &vdisk_removable_attr.attr, &vdev_t10_vend_id_attr.attr, &vdev_vend_specific_id_attr.attr, @@ -837,28 +851,30 @@ out: static void vdisk_check_tp_support(struct scst_vdisk_dev *virt_dev) { - struct file *fd; + struct file *fd = NULL; + bool fd_open = false; TRACE_ENTRY(); virt_dev->dev_thin_provisioned = 0; if (virt_dev->rd_only || (virt_dev->filename == NULL)) - goto out_check; + goto check; fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600); if (IS_ERR(fd)) { PRINT_ERROR("filp_open(%s) failed: %ld", virt_dev->filename, PTR_ERR(fd)); - goto out_check; + goto check; } + fd_open = true; if (virt_dev->blockio) { struct inode *inode = fd->f_dentry->d_inode; if (!S_ISBLK(inode->i_mode)) { PRINT_ERROR("%s is NOT a block device", virt_dev->filename); - goto out_close; + goto check; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) virt_dev->dev_thin_provisioned = @@ -872,10 +888,7 @@ static void vdisk_check_tp_support(struct scst_vdisk_dev *virt_dev) #endif } -out_close: - filp_close(fd, NULL); - -out_check: +check: if (virt_dev->thin_provisioned_manually_set) { if (virt_dev->thin_provisioned && !virt_dev->dev_thin_provisioned) { PRINT_WARNING("Device %s doesn't support thin " @@ -891,6 +904,45 @@ out_check: } + if (virt_dev->thin_provisioned) { + int block_shift = virt_dev->dev->block_shift; + if (virt_dev->blockio) { + struct request_queue *q; + sBUG_ON(!fd_open); + q = bdev_get_queue(fd->f_dentry->d_inode->i_bdev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || \ + (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) + virt_dev->unmap_opt_gran = q->limits.discard_granularity >> block_shift; + virt_dev->unmap_align = q->limits.discard_alignment >> block_shift; + virt_dev->unmap_max_lba_cnt = q->limits.max_discard_sectors >> (block_shift - 9); + virt_dev->discard_zeroes_data = q->limits.discard_zeroes_data; +#else + sBUG(); +#endif + } else { + virt_dev->unmap_opt_gran = 1; + virt_dev->unmap_align = 0; + /* 256 MB */ + virt_dev->unmap_max_lba_cnt = (256 * 1024 * 1024) >> block_shift; +#if 0 /* + * Might be a big performance and functionality win, but might be + * dangerous as well. But let's be on the safe side and disable it + * for now. + */ + virt_dev->discard_zeroes_data = 1; +#else + virt_dev->discard_zeroes_data = 0; +#endif + } + TRACE_DBG("unmap_gran %d, unmap_alignment %d, max_unmap_lba %u, " + "discard_zeroes_data %d", virt_dev->unmap_opt_gran, + virt_dev->unmap_align, virt_dev->unmap_max_lba_cnt, + virt_dev->discard_zeroes_data); + } + + if (fd_open) + filp_close(fd, NULL); + TRACE_EXIT(); return; } @@ -1347,9 +1399,7 @@ static void vdisk_detach(struct scst_device *dev) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif TRACE_DBG("virt_id %d", dev->virt_id); @@ -1367,9 +1417,7 @@ static int vdisk_open_fd(struct scst_vdisk_dev *virt_dev, bool read_only) { int res; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif sBUG_ON(!virt_dev->filename); virt_dev->fd = vdev_open_fd(virt_dev, read_only); @@ -1390,9 +1438,7 @@ out: static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif if (virt_dev->fd) { filp_close(virt_dev->fd, NULL); @@ -1409,9 +1455,7 @@ static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif if (virt_dev->tgt_dev_cnt++ > 0) goto out; @@ -1437,9 +1481,7 @@ static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev) TRACE_ENTRY(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif if (--virt_dev->tgt_dev_cnt == 0) vdisk_close_fd(virt_dev); @@ -1664,7 +1706,7 @@ static enum compl_status_e vdisk_exec_format_unit(struct vdisk_cmd_params *p) } break; default: - sBUG_ON(1); + sBUG(); break; } } @@ -2407,7 +2449,9 @@ static int prepare_read_page(struct file *filp, int len, unsigned long index, last_index; long end_index, nr; loff_t isize; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) read_descriptor_t desc = { .count = len }; +#endif int error; TRACE_ENTRY(); @@ -2460,8 +2504,13 @@ find_page: /* Did it get truncated before we got the lock? */ if (!page->mapping) goto page_not_up_to_date_locked; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + if (!mapping->a_ops->is_partially_uptodate(page, + offset & ~PAGE_CACHE_MASK, len)) +#else if (!mapping->a_ops->is_partially_uptodate(page, &desc, offset & ~PAGE_CACHE_MASK)) +#endif goto page_not_up_to_date_locked; unlock_page(page); } @@ -2952,13 +3001,11 @@ static int vdisk_unmap_file_range(struct scst_cmd *cmd, scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_write_error)); res = -EIO; - goto out; } #else res = 0; #endif -out: TRACE_EXIT_RES(res); return res; } @@ -2966,7 +3013,11 @@ out: static int vdisk_unmap_range(struct scst_cmd *cmd, struct scst_vdisk_dev *virt_dev, uint64_t start_lba, uint32_t blocks) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) int res, err; +#else + int res; +#endif struct file *fd = virt_dev->fd; TRACE_ENTRY(); @@ -3053,6 +3104,14 @@ static void vdisk_exec_write_same_unmap(struct vdisk_cmd_params *p) goto out; } + if (unlikely((uint64_t)cmd->data_len > cmd->dev->max_write_same_len)) { + PRINT_WARNING("Invalid WRITE SAME data len %lld (max allowed " + "%lld)", (long long)cmd->data_len, + (long long)cmd->dev->max_write_same_len); + scst_set_invalid_field_in_cdb(cmd, cmd->len_off, 0); + goto out; + } + rc = vdisk_unmap_range(cmd, virt_dev, cmd->lba, cmd->data_len >> dev->block_shift); if (rc != 0) @@ -3130,6 +3189,7 @@ static enum compl_status_e vdisk_exec_unmap(struct vdisk_cmd_params *p) struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; struct scst_data_descriptor *pd = cmd->cmd_data_descriptors; int i, cnt = cmd->cmd_data_descriptors_cnt; + uint32_t blocks_to_unmap; TRACE_ENTRY(); @@ -3150,6 +3210,20 @@ static enum compl_status_e vdisk_exec_unmap(struct vdisk_cmd_params *p) if (pd == NULL) goto out; + /* Sanity check to avoid too long latencies */ + blocks_to_unmap = 0; + for (i = 0; i < cnt; i++) { + blocks_to_unmap += pd[i].sdd_blocks; + if (blocks_to_unmap > virt_dev->unmap_max_lba_cnt) { + PRINT_WARNING("Too many UNMAP LBAs %u (max allowed %u, " + "dev %s)", blocks_to_unmap, + virt_dev->unmap_max_lba_cnt, + virt_dev->dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, 0, 0); + goto out; + } + } + for (i = 0; i < cnt; i++) { int rc; @@ -3169,68 +3243,339 @@ out: return CMD_SUCCEEDED; } -static void vdev_blockio_get_unmap_params(struct scst_vdisk_dev *virt_dev, - uint32_t *unmap_gran, uint32_t *unmap_alignment, - uint32_t *max_unmap_lba) +/* Supported VPD Pages VPD page (00h). */ +static int vdisk_sup_vpd(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) { - int block_shift = virt_dev->dev->block_shift; - - TRACE_ENTRY(); - - sBUG_ON(!virt_dev->filename); - - if (virt_dev->blockio) { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) - struct file *fd; - struct request_queue *q; - - fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600); - if (IS_ERR(fd)) { - PRINT_ERROR("filp_open(%s) failed: %ld", - virt_dev->filename, PTR_ERR(fd)); - goto out; + buf[3] = 4; + buf[4] = 0x0; /* this page */ + buf[5] = 0x80; /* unit serial number */ + buf[6] = 0x83; /* device identification */ + buf[7] = 0x86; /* extended inquiry */ + if (cmd->dev->type == TYPE_DISK) { + buf[3] += 2; + buf[8] = 0xB0; /* block limits */ + buf[9] = 0xB1; /* block device charachteristics */ + if (virt_dev->thin_provisioned) { + buf[3] += 1; + buf[10] = 0xB2; /* thin provisioning */ } + } + return buf[3] + 4; +} - q = bdev_get_queue(fd->f_dentry->d_inode->i_bdev); - if (q == NULL) { - PRINT_ERROR("No queue for device %s", virt_dev->filename); - goto out_close; - } - - *unmap_gran = q->limits.discard_granularity >> block_shift; - *unmap_alignment = q->limits.discard_alignment >> block_shift; - *max_unmap_lba = q->limits.max_discard_sectors >> (block_shift - 9); - -out_close: - filp_close(fd, NULL); -#else - sBUG_ON(1); -#endif +/* Unit Serial Number VPD page (80h) */ +static int vdisk_usn_vpd(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) +{ + buf[1] = 0x80; + if (cmd->tgtt->get_serial) { + buf[3] = cmd->tgtt->get_serial(cmd->tgt_dev, &buf[4], + INQ_BUF_SZ - 4); } else { - *unmap_gran = 1; - *unmap_alignment = 0; - *max_unmap_lba = min_t(loff_t, 0xFFFFFFFF, virt_dev->file_size >> block_shift); + int usn_len; + + read_lock(&vdisk_serial_rwlock); + usn_len = strlen(virt_dev->usn); + buf[3] = usn_len; + strncpy(&buf[4], virt_dev->usn, usn_len); + read_unlock(&vdisk_serial_rwlock); + } + return buf[3] + 4; +} + +/* Device Identification VPD page (83h) */ +static int vdisk_dev_id_vpd(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) +{ + int i, resp_len, num = 4; + uint16_t tg_id; + + buf[1] = 0x83; + + read_lock(&vdisk_serial_rwlock); + i = strlen(virt_dev->scsi_device_name); + if (i > 0) { + /* SCSI target device name */ + buf[num + 0] = 0x3; /* ASCII */ + buf[num + 1] = 0x20 | 0x8; /* Target device SCSI name */ + i += 4 - i % 4; /* align to required 4 bytes */ + scst_copy_and_fill_b(&buf[num + 4], virt_dev->scsi_device_name, + i, '\0'); + + buf[num + 3] = i; + num += buf[num + 3]; + + num += 4; + } + read_unlock(&vdisk_serial_rwlock); + + /* T10 vendor identifier field format (faked) */ + buf[num + 0] = 0x2; /* ASCII */ + buf[num + 1] = 0x1; /* Vendor ID */ + read_lock(&vdisk_serial_rwlock); + scst_copy_and_fill(&buf[num + 4], virt_dev->t10_vend_id, 8); + i = strlen(virt_dev->vend_specific_id); + memcpy(&buf[num + 12], virt_dev->vend_specific_id, i); + read_unlock(&vdisk_serial_rwlock); + + buf[num + 3] = 8 + i; + num += buf[num + 3]; + + num += 4; + + /* + * Relative target port identifier + */ + buf[num + 0] = 0x01; /* binary */ + /* Relative target port id */ + buf[num + 1] = 0x10 | 0x04; + + put_unaligned_be16(cmd->tgt->rel_tgt_id, &buf[num + 4 + 2]); + + buf[num + 3] = 4; + num += buf[num + 3]; + + num += 4; + + tg_id = scst_lookup_tg_id(cmd->dev, cmd->tgt); + if (tg_id) { + /* + * Target port group designator + */ + buf[num + 0] = 0x01; /* binary */ + /* Target port group id */ + buf[num + 1] = 0x10 | 0x05; + + put_unaligned_be16(tg_id, &buf[num + 4 + 2]); + + buf[num + 3] = 4; + num += 4 + buf[num + 3]; } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) -out: -#endif - TRACE_DBG("unmap_gran %d, unmap_alignment %d, max_unmap_lba %u", - *unmap_gran, *unmap_alignment, *max_unmap_lba); + /* + * IEEE id + */ + buf[num + 0] = 0x01; /* binary */ - TRACE_EXIT(); - return; + /* EUI-64 */ + buf[num + 1] = 0x02; + buf[num + 2] = 0x00; + buf[num + 3] = 0x08; + + /* IEEE id */ + buf[num + 4] = virt_dev->t10_dev_id[0]; + buf[num + 5] = virt_dev->t10_dev_id[1]; + buf[num + 6] = virt_dev->t10_dev_id[2]; + + /* IEEE ext id */ + buf[num + 7] = virt_dev->t10_dev_id[3]; + buf[num + 8] = virt_dev->t10_dev_id[4]; + buf[num + 9] = virt_dev->t10_dev_id[5]; + buf[num + 10] = virt_dev->t10_dev_id[6]; + buf[num + 11] = virt_dev->t10_dev_id[7]; + num += buf[num + 3]; + + resp_len = num; + put_unaligned_be16(resp_len, &buf[2]); + resp_len += 4; + + return resp_len; +} + +/* Extended INQUIRY Data (86h) */ +static int vdisk_ext_inq(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) +{ + buf[1] = 0x86; + buf[3] = 0x3C; + buf[5] = 7; /* HEADSUP=1, ORDSUP=1, SIMPSUP=1 */ + buf[6] = (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : 1; /* V_SUP */ + buf[7] = 1; /* LUICLR=1 */ + return buf[3] + 4; +} + +/* Block Limits VPD page (B0h) */ +static int vdisk_block_limits(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) +{ + struct scst_device *dev = cmd->dev; + int max_transfer; + + buf[1] = 0xB0; + buf[3] = 0x3C; + buf[4] = 1; /* WSNZ set */ + buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */ + /* Optimal transfer granuality is PAGE_SIZE */ + put_unaligned_be16(max_t(int, PAGE_SIZE / dev->block_size, 1), &buf[6]); + + /* Max transfer len is min of sg limit and 8M */ + max_transfer = min_t(int, cmd->tgt_dev->max_sg_cnt << PAGE_SHIFT, + 8*1024*1024) / dev->block_size; + put_unaligned_be32(max_transfer, &buf[8]); + + /* + * Let's have optimal transfer len 512KB. Better to not + * set it at all, because we don't have such limit, + * but some initiators may not understand that (?). + * From other side, too big transfers are not optimal, + * because SGV cache supports only <4M buffers. + */ + put_unaligned_be32(min_t(int, max_transfer, 512*1024 / dev->block_size), + &buf[12]); + + if (virt_dev->thin_provisioned) { + /* MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT is UNLIMITED */ + put_unaligned_be32(0xFFFFFFFF, &buf[24]); + /* + * MAXIMUM UNMAP LBA COUNT, OPTIMAL UNMAP + * GRANULARITY and ALIGNMENT + */ + put_unaligned_be32(virt_dev->unmap_max_lba_cnt, &buf[20]); + put_unaligned_be32(virt_dev->unmap_opt_gran, &buf[28]); + if (virt_dev->unmap_align != 0) { + put_unaligned_be32(virt_dev->unmap_align, &buf[32]); + buf[32] |= 0x80; + } + } + + /* MAXIMUM WRITE SAME LENGTH (measured in blocks) */ + put_unaligned_be64(dev->max_write_same_len >> dev->block_shift, + &buf[36]); + + return buf[3] + 4; +} + +/* Block Device Characteristics VPD Page (B1h) */ +static int vdisk_bdev_char(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) +{ + buf[1] = 0xB1; + buf[3] = 0x3C; + if (virt_dev->rotational) { + /* 15K RPM */ + put_unaligned_be16(0x3A98, &buf[4]); + } else + put_unaligned_be16(1, &buf[4]); + return buf[3] + 4; +} + +/* Logical Block Provisioning a.k.a. Thin Provisioning VPD page (B2h) */ +static int vdisk_tp_vpd(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) +{ + buf[1] = 0xB2; + buf[3] = 4; + buf[5] = 0xE0; + if (virt_dev->discard_zeroes_data) + buf[5] |= 0x4; /* LBPRZ */ + buf[6] = 2; /* thin provisioned */ + return buf[3] + 4; +} + +/* Standard INQUIRY response */ +static int vdisk_inq(uint8_t *buf, struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev) +{ + int num; + + if (virt_dev->removable) + buf[1] = 0x80; /* removable */ + buf[2] = 6; /* Device complies to SPC-4 */ + buf[3] = 0x02; /* Data in format specified in SPC */ + if (cmd->tgtt->fake_aca) + buf[3] |= 0x20; + buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */ + if (scst_impl_alua_configured(cmd->dev)) + buf[5] = SCST_INQ_TPGS_MODE_IMPLICIT; + buf[6] = 0x10; /* MultiP 1 */ + buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */ + + read_lock(&vdisk_serial_rwlock); + + /* + * 8 byte ASCII Vendor Identification of the target + * - left aligned. + */ + scst_copy_and_fill(&buf[8], virt_dev->t10_vend_id, 8); + + /* + * 16 byte ASCII Product Identification of the target - left + * aligned. + */ + scst_copy_and_fill(&buf[16], virt_dev->prod_id, 16); + + /* + * 4 byte ASCII Product Revision Level of the target - left + * aligned. + */ + scst_copy_and_fill(&buf[32], virt_dev->prod_rev_lvl, 4); + + /* Vendor specific information. */ + if (virt_dev->inq_vend_specific_len <= 20) + memcpy(&buf[36], virt_dev->inq_vend_specific, + virt_dev->inq_vend_specific_len); + + /** Version descriptors **/ + + buf[4] += 58 - 36; + num = 0; + + /* SAM-4 T10/1683-D revision 14 */ + buf[58 + num] = 0x0; + buf[58 + num + 1] = 0x8B; + num += 2; + + /* Physical transport */ + if (cmd->tgtt->get_phys_transport_version != NULL) { + uint16_t v = cmd->tgtt->get_phys_transport_version(cmd->tgt); + if (v != 0) { + put_unaligned_be16(v, &buf[58 + num]); + num += 2; + } + } + + /* SCSI transport */ + if (cmd->tgtt->get_scsi_transport_version != NULL) { + put_unaligned_be16( + cmd->tgtt->get_scsi_transport_version(cmd->tgt), + &buf[58 + num]); + num += 2; + } + + /* SPC-4 T10/1731-D revision 23 */ + buf[58 + num] = 0x4; + buf[58 + num + 1] = 0x63; + num += 2; + + /* Device command set */ + if (virt_dev->command_set_version != 0) { + put_unaligned_be16(virt_dev->command_set_version, + &buf[58 + num]); + num += 2; + } + + /* Vendor specific information. */ + if (virt_dev->inq_vend_specific_len > 20) { + memcpy(&buf[96], virt_dev->inq_vend_specific, + virt_dev->inq_vend_specific_len); + num = 96 - 58 + virt_dev->inq_vend_specific_len; + } + + read_unlock(&vdisk_serial_rwlock); + + buf[4] += num; + return buf[4] + 5; } static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) { struct scst_cmd *cmd = p->cmd; - int32_t length, i, resp_len = 0; + int32_t length, resp_len; uint8_t *address; uint8_t *buf; struct scst_device *dev = cmd->dev; struct scst_vdisk_dev *virt_dev = dev->dh_priv; - uint16_t tg_id; TRACE_ENTRY(); @@ -3257,322 +3602,32 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) /* Vital Product */ if (cmd->cdb[1] & EVPD) { if (0 == cmd->cdb[2]) { - /* supported vital product data pages */ - buf[3] = 4; - buf[4] = 0x0; /* this page */ - buf[5] = 0x80; /* unit serial number */ - buf[6] = 0x83; /* device identification */ - buf[7] = 0x86; /* extended inquiry */ - if (dev->type == TYPE_DISK) { - buf[3] += 2; - buf[8] = 0xB0; /* block limits */ - buf[9] = 0xB1; /* block device charachteristics */ - if (virt_dev->thin_provisioned) { - buf[3] += 1; - buf[10] = 0xB2; /* thin provisioning */ - } - } - resp_len = buf[3] + 4; + resp_len = vdisk_sup_vpd(buf, cmd, virt_dev); } else if (0x80 == cmd->cdb[2]) { - /* unit serial number */ - buf[1] = 0x80; - if (cmd->tgtt->get_serial) { - buf[3] = cmd->tgtt->get_serial(cmd->tgt_dev, - &buf[4], INQ_BUF_SZ - 4); - } else { - int usn_len; - read_lock(&vdisk_serial_rwlock); - usn_len = strlen(virt_dev->usn); - buf[3] = usn_len; - strncpy(&buf[4], virt_dev->usn, usn_len); - read_unlock(&vdisk_serial_rwlock); - } - resp_len = buf[3] + 4; + resp_len = vdisk_usn_vpd(buf, cmd, virt_dev); } else if (0x83 == cmd->cdb[2]) { - /* device identification */ - int num = 4; - - buf[1] = 0x83; - - read_lock(&vdisk_serial_rwlock); - i = strlen(virt_dev->scsi_device_name); - if (i > 0) { - /* SCSI target device name */ - buf[num + 0] = 0x3; /* ASCII */ - buf[num + 1] = 0x20 | 0x8; /* Target device SCSI name */ - i += 4 - i % 4; /* align to required 4 bytes */ - scst_copy_and_fill_b(&buf[num + 4], virt_dev->scsi_device_name, i, '\0'); - - buf[num + 3] = i; - num += buf[num + 3]; - - num += 4; - } - read_unlock(&vdisk_serial_rwlock); - - /* T10 vendor identifier field format (faked) */ - buf[num + 0] = 0x2; /* ASCII */ - buf[num + 1] = 0x1; /* Vendor ID */ - read_lock(&vdisk_serial_rwlock); - scst_copy_and_fill(&buf[num + 4], virt_dev->t10_vend_id, 8); - i = strlen(virt_dev->vend_specific_id); - memcpy(&buf[num + 12], virt_dev->vend_specific_id, i); - read_unlock(&vdisk_serial_rwlock); - - buf[num + 3] = 8 + i; - num += buf[num + 3]; - - num += 4; - - /* - * Relative target port identifier - */ - buf[num + 0] = 0x01; /* binary */ - /* Relative target port id */ - buf[num + 1] = 0x10 | 0x04; - - put_unaligned_be16(cmd->tgt->rel_tgt_id, - &buf[num + 4 + 2]); - - buf[num + 3] = 4; - num += buf[num + 3]; - - num += 4; - - tg_id = scst_lookup_tg_id(dev, cmd->tgt); - if (tg_id) { - /* - * Target port group designator - */ - buf[num + 0] = 0x01; /* binary */ - /* Target port group id */ - buf[num + 1] = 0x10 | 0x05; - - put_unaligned_be16(tg_id, &buf[num + 4 + 2]); - - buf[num + 3] = 4; - num += 4 + buf[num + 3]; - } - - /* - * IEEE id - */ - buf[num + 0] = 0x01; /* binary */ - - /* EUI-64 */ - buf[num + 1] = 0x02; - buf[num + 2] = 0x00; - buf[num + 3] = 0x08; - - /* IEEE id */ - buf[num + 4] = virt_dev->t10_dev_id[0]; - buf[num + 5] = virt_dev->t10_dev_id[1]; - buf[num + 6] = virt_dev->t10_dev_id[2]; - - /* IEEE ext id */ - buf[num + 7] = virt_dev->t10_dev_id[3]; - buf[num + 8] = virt_dev->t10_dev_id[4]; - buf[num + 9] = virt_dev->t10_dev_id[5]; - buf[num + 10] = virt_dev->t10_dev_id[6]; - buf[num + 11] = virt_dev->t10_dev_id[7]; - num += buf[num + 3]; - - resp_len = num; - put_unaligned_be16(resp_len, &buf[2]); - resp_len += 4; + resp_len = vdisk_dev_id_vpd(buf, cmd, virt_dev); } else if (0x86 == cmd->cdb[2]) { - /* Extended INQUIRY */ - buf[1] = 0x86; - buf[3] = 0x3C; - buf[5] = 7; /* HEADSUP=1, ORDSUP=1, SIMPSUP=1 */ - buf[6] = (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : 1; /* V_SUP */ - buf[7] = 1; /* LUICLR=1 */ - resp_len = buf[3] + 4; + resp_len = vdisk_ext_inq(buf, cmd, virt_dev); } else if ((0xB0 == cmd->cdb[2]) && (dev->type == TYPE_DISK)) { - /* Block Limits */ - int max_transfer; - buf[1] = 0xB0; - buf[3] = 0x3C; - buf[4] = 1; /* WSNZ set */ - buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */ - /* Optimal transfer granuality is PAGE_SIZE */ - put_unaligned_be16(max_t(int, PAGE_SIZE/dev->block_size, 1), &buf[6]); - - /* Max transfer len is min of sg limit and 8M */ - max_transfer = min_t(int, - cmd->tgt_dev->max_sg_cnt << PAGE_SHIFT, - 8*1024*1024) / dev->block_size; - put_unaligned_be32(max_transfer, &buf[8]); - - /* - * Let's have optimal transfer len 512KB. Better to not - * set it at all, because we don't have such limit, - * but some initiators may not understand that (?). - * From other side, too big transfers are not optimal, - * because SGV cache supports only <4M buffers. - */ - put_unaligned_be32(min_t(int, - max_transfer, 512*1024 / dev->block_size), - &buf[12]); - - if (virt_dev->thin_provisioned) { - uint32_t gran = 1, align = 0, max_lba = 1; - - /* MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT is UNLIMITED */ - put_unaligned_be32(0xFFFFFFFF, &buf[24]); - if (virt_dev->blockio) { - vdev_blockio_get_unmap_params(virt_dev, - &gran, &align, &max_lba); - } else { - max_lba = min_t(loff_t, 0xFFFFFFFFU, - virt_dev->file_size >> - dev->block_shift); - } - /* - * MAXIMUM UNMAP LBA COUNT, OPTIMAL UNMAP - * GRANULARITY and ALIGNMENT - */ - put_unaligned_be32(max_lba, &buf[20]); - put_unaligned_be32(gran, &buf[28]); - if (align != 0) { - put_unaligned_be32(align, &buf[32]); - buf[32] |= 0x80; - } - } - - /* MAXIMUM WRITE SAME LENGTH (measured in blocks) */ - put_unaligned_be64(dev->max_write_same_len >> - dev->block_shift, &buf[36]); - - resp_len = buf[3] + 4; + resp_len = vdisk_block_limits(buf, cmd, virt_dev); } else if ((0xB1 == cmd->cdb[2]) && (dev->type == TYPE_DISK)) { - /* Block Device Characteristics */ - buf[1] = 0xB1; - buf[3] = 0x3C; - if (virt_dev->rotational) { - /* 15K RPM */ - put_unaligned_be16(0x3A98, &buf[4]); - } else - put_unaligned_be16(1, &buf[4]); - resp_len = buf[3] + 4; + resp_len = vdisk_bdev_char(buf, cmd, virt_dev); } else if ((0xB2 == cmd->cdb[2]) && (dev->type == TYPE_DISK) && virt_dev->thin_provisioned) { - /* Thin Provisioning */ - buf[1] = 0xB2; - buf[3] = 4; - buf[5] = 0xE0; -#if 0 /* - * Might be a big performance and functionality win, but might be - * dangerous as well, although generally nearly always it should be set, - * because nearly all devices should return zero for unmapped blocks. - * But let's be on the safe side and disable it for now. - * - * Changing it change also READ CAPACITY(16)! - */ - buf[5] |= 0x4; /* LBPRZ */ -#endif - buf[6] = 2; /* thin provisioned */ - resp_len = buf[3] + 4; + resp_len = vdisk_tp_vpd(buf, cmd, virt_dev); } else { TRACE_DBG("INQUIRY: Unsupported EVPD page %x", cmd->cdb[2]); scst_set_invalid_field_in_cdb(cmd, 2, 0); goto out_put; } } else { - int num; - if (cmd->cdb[2] != 0) { TRACE_DBG("INQUIRY: Unsupported page %x", cmd->cdb[2]); scst_set_invalid_field_in_cdb(cmd, 2, 0); goto out_put; } - - if (virt_dev->removable) - buf[1] = 0x80; /* removable */ - buf[2] = 6; /* Device complies to SPC-4 */ - buf[3] = 0x02; /* Data in format specified in SPC */ - if (cmd->tgtt->fake_aca) - buf[3] |= 0x20; - buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */ - if (scst_impl_alua_configured(dev)) - buf[5] = SCST_INQ_TPGS_MODE_IMPLICIT; - buf[6] = 0x10; /* MultiP 1 */ - buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */ - - read_lock(&vdisk_serial_rwlock); - - /* - * 8 byte ASCII Vendor Identification of the target - * - left aligned. - */ - scst_copy_and_fill(&buf[8], virt_dev->t10_vend_id, 8); - - /* - * 16 byte ASCII Product Identification of the target - left - * aligned. - */ - scst_copy_and_fill(&buf[16], virt_dev->prod_id, 16); - - /* - * 4 byte ASCII Product Revision Level of the target - left - * aligned. - */ - scst_copy_and_fill(&buf[32], virt_dev->prod_rev_lvl, 4); - - /* Vendor specific information. */ - if (virt_dev->inq_vend_specific_len <= 20) - memcpy(&buf[36], virt_dev->inq_vend_specific, - virt_dev->inq_vend_specific_len); - - /** Version descriptors **/ - - buf[4] += 58 - 36; - num = 0; - - /* SAM-4 T10/1683-D revision 14 */ - buf[58 + num] = 0x0; - buf[58 + num + 1] = 0x8B; - num += 2; - - /* Physical transport */ - if (cmd->tgtt->get_phys_transport_version != NULL) { - uint16_t v = cmd->tgtt->get_phys_transport_version(cmd->tgt); - if (v != 0) { - *((__be16 *)&buf[58 + num]) = cpu_to_be16(v); - num += 2; - } - } - - /* SCSI transport */ - if (cmd->tgtt->get_scsi_transport_version != NULL) { - *((__be16 *)&buf[58 + num]) = - cpu_to_be16(cmd->tgtt->get_scsi_transport_version(cmd->tgt)); - num += 2; - } - - /* SPC-4 T10/1731-D revision 23 */ - buf[58 + num] = 0x4; - buf[58 + num + 1] = 0x63; - num += 2; - - /* Device command set */ - if (virt_dev->command_set_version != 0) { - *((__be16 *)&buf[58 + num]) = - cpu_to_be16(virt_dev->command_set_version); - num += 2; - } - - /* Vendor specific information. */ - if (virt_dev->inq_vend_specific_len > 20) { - memcpy(&buf[96], virt_dev->inq_vend_specific, - virt_dev->inq_vend_specific_len); - num = 96 - 58 + virt_dev->inq_vend_specific_len; - } - - read_unlock(&vdisk_serial_rwlock); - - buf[4] += num; - resp_len = buf[4] + 5; + resp_len = vdisk_inq(buf, cmd, virt_dev); } sBUG_ON(resp_len > INQ_BUF_SZ); @@ -3752,7 +3807,7 @@ static int vdisk_caching_pg(unsigned char *p, int pcontrol, p[2] |= (virt_dev->wt_flag_saved || virt_dev->nv_cache) ? 0 : WCE; break; default: - sBUG_ON(1); + sBUG(); break; } @@ -4428,17 +4483,9 @@ static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p } if (virt_dev->thin_provisioned) { - buffer[14] |= 0x80; /* Add LBPME */ -#if 0 /* - * Might be a big performance and functionality win, but might be - * dangerous as well, although generally nearly always it should be set, - * because nearly all devices should return zero for unmapped blocks. - * But let's be on the safe side and disable it for now. - * - * Changing it change also 0xB2 INQUIRY page! - */ - buffer[14] |= 0x40; /* Add LBPRZ */ -#endif + buffer[14] |= 0x80; /* LBPME */ + if (virt_dev->discard_zeroes_data) + buffer[14] |= 0x40; /* LBPRZ */ } length = scst_get_buf_full_sense(cmd, &address); @@ -4745,6 +4792,30 @@ out: static enum compl_status_e nullio_exec_read(struct vdisk_cmd_params *p) { + struct scst_cmd *cmd = p->cmd; + struct scst_device *dev = cmd->dev; + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + + TRACE_ENTRY(); + + if (virt_dev->read_zero) { + struct scatterlist *sge; + struct page *page; + int i; + void *p; + + for_each_sg(cmd->sg, sge, cmd->sg_cnt, i) { + page = sg_page(sge); + p = kmap(page); + if (sge->offset == 0 && sge->length == PAGE_SIZE) + clear_page(p); + else + memset(p + sge->offset, 0, sge->length); + kunmap(page); + } + } + + TRACE_EXIT(); return CMD_SUCCEEDED; } @@ -4883,7 +4954,7 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p) loff_t loff = p->loff; mm_segment_t old_fs; loff_t err = 0; - ssize_t length, full_len, saved_full_len; + ssize_t length, full_len; uint8_t __user *address; struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; struct file *fd = virt_dev->fd; @@ -4939,7 +5010,6 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p) goto out_set_fs; } - saved_full_len = full_len; eiv = iv; eiv_count = iv_count; restart: @@ -5942,6 +6012,7 @@ static int vdev_create(struct scst_dev_type *devt, virt_dev->rd_only = DEF_RD_ONLY; virt_dev->dummy = DEF_DUMMY; + virt_dev->read_zero = DEF_READ_ZERO; virt_dev->removable = DEF_REMOVABLE; virt_dev->rotational = DEF_ROTATIONAL; virt_dev->thin_provisioned = DEF_THIN_PROVISIONED; @@ -6015,7 +6086,7 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, char *params, const char *const allowed_params[]) { int res = 0; - unsigned long val; + unsigned long long val; char *param, *p, *pp; TRACE_ENTRY(); @@ -6093,9 +6164,9 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) - res = kstrtoul(pp, 0, &val); + res = kstrtoull(pp, 0, &val); #else - res = strict_strtoul(pp, 0, &val); + res = strict_strtoull(pp, 0, &val); #endif if (res != 0) { PRINT_ERROR("strtoul() for %s failed: %d (device %s)", @@ -6137,7 +6208,7 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } else if (!strcasecmp("tst", p)) { if ((val != SCST_TST_0_SINGLE_TASK_SET) && (val != SCST_TST_1_SEP_TASK_SETS)) { - PRINT_ERROR("Invalid TST value %d", (int)val); + PRINT_ERROR("Invalid TST value %lld", val); res = -EINVAL; goto out; } @@ -6160,7 +6231,7 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, res = -EINVAL; goto out; } - TRACE_DBG("block size %ld, block shift %d", + TRACE_DBG("block size %lld, block shift %d", val, virt_dev->blk_shift); } else { PRINT_ERROR("Unknown parameter %s (device %s)", p, @@ -6170,7 +6241,7 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev, } } - if (virt_dev->file_size % (1 << virt_dev->blk_shift) != 0) { + if ((virt_dev->file_size & ((1 << virt_dev->blk_shift) - 1)) != 0) { PRINT_ERROR("Device size %lld is not a multiple of the block" " size %d", virt_dev->file_size, 1 << virt_dev->blk_shift); @@ -6463,7 +6534,7 @@ out: static ssize_t __vcdrom_add_device(const char *device_name, char *params) { int res = 0; - const char *allowed_params[] = { "tst", NULL }; + static const char *const allowed_params[] = { "tst", NULL }; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -6765,7 +6836,7 @@ static int vdev_size_process_store(struct scst_sysfs_work_item *work) int size_shift, res = -EINVAL; if (sscanf(work->buf, "%d %lld", &size_shift, &new_size) != 2 || - new_size > (ULONG_MAX >> size_shift)) + new_size > (ULLONG_MAX >> size_shift)) goto put; new_size <<= size_shift; @@ -6783,7 +6854,7 @@ static int vdev_size_process_store(struct scst_sysfs_work_item *work) if (!virt_dev->nullio) { res = -EPERM; sBUG(); - } else if (new_size % (1 << virt_dev->blk_shift) == 0) { + } else if ((new_size & ((1 << virt_dev->blk_shift) - 1)) == 0) { virt_dev->file_size = new_size; virt_dev->nblocks = virt_dev->file_size >> dev->block_shift; } else { @@ -7012,6 +7083,51 @@ static ssize_t vdev_sysfs_dummy_show(struct kobject *kobj, virt_dev->dummy != DEF_DUMMY ? SCST_SYSFS_KEY_MARK "\n" : ""); } +static ssize_t vdev_sysfs_rz_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_device *dev = container_of(kobj, struct scst_device, + dev_kobj); + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + bool read_zero = virt_dev->read_zero; + + return sprintf(buf, "%d\n%s", read_zero, read_zero != DEF_READ_ZERO ? + SCST_SYSFS_KEY_MARK "\n" : ""); +} + +static ssize_t vdev_sysfs_rz_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t count) +{ + struct scst_device *dev = container_of(kobj, struct scst_device, + dev_kobj); + struct scst_vdisk_dev *virt_dev = dev->dh_priv; + long read_zero; + int res; + char ch[16]; + + sprintf(ch, "%.*s", min_t(int, sizeof(ch) - 1, count), buf); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + res = kstrtol(ch, 0, &read_zero); +#else + res = strict_strtol(ch, 0, &read_zero); +#endif + if (res) + goto out; + res = -EINVAL; + if (read_zero != 0 && read_zero != 1) + goto out; + + spin_lock(&virt_dev->flags_lock); + virt_dev->read_zero = read_zero; + spin_unlock(&virt_dev->flags_lock); + + res = count; + +out: + return res; +} + static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index afc92707a..4a8236f98 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -52,6 +52,9 @@ #include "scst_mem.h" #include "scst_pres.h" +static void scst_del_acn(struct scst_acn *acn); +static void scst_free_acn(struct scst_acn *acn, bool reassign); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) struct scsi_io_context { void *data; @@ -76,6 +79,27 @@ static int strncasecmp(const char *s1, const char *s2, size_t n) } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap) +{ + unsigned int len; + char *p; + va_list aq; + + va_copy(aq, ap); + len = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); + + p = kmalloc_track_caller(len + 1, gfp); + if (!p) + return NULL; + + vsnprintf(p, len + 1, fmt, ap); + + return p; +} +#endif + #if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) && !defined(HAVE_SG_COPY) static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) @@ -2397,21 +2421,22 @@ void scst_free_aen(struct scst_aen *aen) void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev, int key, int asc, int ascq) { - struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt; + struct scst_session *sess = tgt_dev->sess; + struct scst_tgt_template *tgtt = sess->tgt->tgtt; uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; int sl; TRACE_ENTRY(); - if ((tgt_dev->sess->init_phase != SCST_SESS_IPH_READY) || - (tgt_dev->sess->shut_phase != SCST_SESS_SPH_READY)) + if (sess->init_phase != SCST_SESS_IPH_READY || + sess->shut_phase != SCST_SESS_SPH_READY) goto out; if (tgtt->report_aen != NULL) { struct scst_aen *aen; int rc; - aen = scst_alloc_aen(tgt_dev->sess, tgt_dev->lun); + aen = scst_alloc_aen(sess, tgt_dev->lun); if (aen == NULL) goto queue_ua; @@ -2514,6 +2539,7 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess, local_bh_disable(); +#if !defined(__CHECKER__) for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { head = &sess->sess_tgt_dev_list[i]; @@ -2523,6 +2549,7 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess, spin_lock(&tgt_dev->tgt_dev_lock); } } +#endif for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { head = &sess->sess_tgt_dev_list[i]; @@ -2543,6 +2570,7 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess, } } +#if !defined(__CHECKER__) for (i = SESS_TGT_DEV_LIST_HASH_SIZE-1; i >= 0; i--) { head = &sess->sess_tgt_dev_list[i]; @@ -2551,6 +2579,7 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess, spin_unlock(&tgt_dev->tgt_dev_lock); } } +#endif local_bh_enable(); @@ -2673,7 +2702,7 @@ void scst_aen_done(struct scst_aen *aen) SCST_SET_UA_FLAG_AT_HEAD); mutex_unlock(&scst_mutex); } else { - struct list_head *head; + struct scst_session *sess = aen->sess; struct scst_tgt_dev *tgt_dev; uint64_t lun; @@ -2682,17 +2711,13 @@ void scst_aen_done(struct scst_aen *aen) mutex_lock(&scst_mutex); /* tgt_dev might get dead, so we need to reseek it */ - head = &aen->sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)]; - list_for_each_entry(tgt_dev, head, - sess_tgt_dev_list_entry) { - if (tgt_dev->lun == lun) { - TRACE_MGMT_DBG("Requeuing failed AEN UA for " - "tgt_dev %p", tgt_dev); - scst_check_set_UA(tgt_dev, aen->aen_sense, - aen->aen_sense_len, - SCST_SET_UA_FLAG_AT_HEAD); - break; - } + tgt_dev = scst_lookup_tgt_dev(sess, lun); + if (tgt_dev) { + TRACE_MGMT_DBG("Requeuing failed AEN UA for tgt_dev %p", + tgt_dev); + scst_check_set_UA(tgt_dev, aen->aen_sense, + aen->aen_sense_len, + SCST_SET_UA_FLAG_AT_HEAD); } mutex_unlock(&scst_mutex); @@ -2840,6 +2865,8 @@ next: TRACE_DBG("Moving sess %p from acg %s to acg %s", sess, old_acg->acg_name, acg->acg_name); list_move_tail(&sess->acg_sess_list_entry, &acg->acg_sess_list); + scst_get_acg(acg); + scst_put_acg(old_acg); #ifndef CONFIG_SCST_PROC scst_recreate_sess_luns_link(sess); @@ -3105,19 +3132,20 @@ next: return; } -static void scst_adjust_sg(struct scst_cmd *cmd, struct scatterlist *sg, - int *sg_cnt, int adjust_len) +static bool __scst_adjust_sg(struct scst_cmd *cmd, struct scatterlist *sg, + int *sg_cnt, int adjust_len, struct scst_orig_sg_data *orig_sg) { struct scatterlist *sgi; int i, l; + bool res = false; TRACE_ENTRY(); l = 0; for_each_sg(sg, sgi, *sg_cnt, i) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - TRACE_DBG("i %d, sg_cnt %d, sg %p, page_link %lx", i, - *sg_cnt, sg, sgi->page_link); + TRACE_DBG("i %d, sg_cnt %d, sg %p, page_link %lx, len %d", i, + *sg_cnt, sg, sgi->page_link, sgi->length); #else TRACE_DBG("i %d, sg_cnt %d, sg %p, page_link %lx", i, *sg_cnt, sg, 0UL); @@ -3125,26 +3153,57 @@ static void scst_adjust_sg(struct scst_cmd *cmd, struct scatterlist *sg, l += sgi->length; if (l >= adjust_len) { int left = adjust_len - (l - sgi->length); -#ifdef CONFIG_SCST_DEBUG - TRACE(TRACE_SG_OP|TRACE_MEMORY, "cmd %p (tag %llu), " - "sg %p, sg_cnt %d, adjust_len %d, i %d, " - "sg[j].length %d, left %d", + + TRACE_DBG_FLAG(TRACE_SG_OP|TRACE_MEMORY|TRACE_DEBUG, + "cmd %p (tag %llu), sg %p, sg_cnt %d, " + "adjust_len %d, i %d, sg[j].length %d, left %d", cmd, (long long unsigned int)cmd->tag, sg, *sg_cnt, adjust_len, i, sgi->length, left); -#endif - cmd->p_orig_sg_cnt = sg_cnt; - cmd->orig_sg_cnt = *sg_cnt; - cmd->orig_sg_entry = sgi; - cmd->orig_entry_offs = sgi->offset; - cmd->orig_entry_len = sgi->length; + + orig_sg->p_orig_sg_cnt = sg_cnt; + orig_sg->orig_sg_cnt = *sg_cnt; + orig_sg->orig_sg_entry = sgi; + orig_sg->orig_entry_offs = sgi->offset; + orig_sg->orig_entry_len = sgi->length; *sg_cnt = (left > 0) ? i+1 : i; sgi->length = left; - cmd->sg_buff_modified = 1; + res = true; break; } } + TRACE_EXIT_RES(res); + return res; +} + +/* + * Makes cmd's SG shorter on adjust_len bytes. Reg_sg is true for cmd->sg + * and false for cmd->write_sg. + */ +static void scst_adjust_sg(struct scst_cmd *cmd, bool reg_sg, + int adjust_len) +{ + struct scatterlist *sg; + int *sg_cnt; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(cmd->sg_buff_modified); + + if (reg_sg) { + sg = cmd->sg; + sg_cnt = &cmd->sg_cnt; + } else { + sg = *cmd->write_sg; + sg_cnt = cmd->write_sg_cnt; + } + + TRACE_DBG("reg_sg %d, adjust_len %d", reg_sg, adjust_len); + + cmd->sg_buff_modified = __scst_adjust_sg(cmd, sg, sg_cnt, adjust_len, + &cmd->orig_sg); + TRACE_EXIT(); return; } @@ -3156,13 +3215,20 @@ static void scst_adjust_sg(struct scst_cmd *cmd, struct scatterlist *sg, */ void scst_restore_sg_buff(struct scst_cmd *cmd) { - TRACE_MEM("cmd %p, sg %p, orig_sg_entry %p, orig_entry_offs %d, " - "orig_entry_len %d, orig_sg_cnt %d", cmd, cmd->sg, - cmd->orig_sg_entry, cmd->orig_entry_offs, cmd->orig_entry_len, - cmd->orig_sg_cnt); - cmd->orig_sg_entry->offset = cmd->orig_entry_offs; - cmd->orig_sg_entry->length = cmd->orig_entry_len; - *cmd->p_orig_sg_cnt = cmd->orig_sg_cnt; + TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_MEMORY, "cmd %p, sg %p, " + "orig_sg_entry %p, orig_entry_offs %d, orig_entry_len %d, " + "orig_sg_cnt %d", cmd, cmd->sg, cmd->orig_sg.orig_sg_entry, + cmd->orig_sg.orig_entry_offs, cmd->orig_sg.orig_entry_len, + cmd->orig_sg.orig_sg_cnt); + + EXTRACHECKS_BUG_ON(!cmd->sg_buff_modified); + + if (cmd->sg_buff_modified) { + cmd->orig_sg.orig_sg_entry->offset = cmd->orig_sg.orig_entry_offs; + cmd->orig_sg.orig_sg_entry->length = cmd->orig_sg.orig_entry_len; + *cmd->orig_sg.p_orig_sg_cnt = cmd->orig_sg.orig_sg_cnt; + } + cmd->sg_buff_modified = 0; } EXPORT_SYMBOL(scst_restore_sg_buff); @@ -3200,7 +3266,7 @@ void scst_set_resp_data_len(struct scst_cmd *cmd, int resp_data_len) goto out; } - scst_adjust_sg(cmd, cmd->sg, &cmd->sg_cnt, resp_data_len); + scst_adjust_sg(cmd, true, resp_data_len); cmd->resid_possible = 1; @@ -3218,7 +3284,7 @@ void scst_limit_sg_write_len(struct scst_cmd *cmd) cmd->write_len, cmd, *cmd->write_sg, *cmd->write_sg_cnt); scst_check_restore_sg_buff(cmd); - scst_adjust_sg(cmd, *cmd->write_sg, cmd->write_sg_cnt, cmd->write_len); + scst_adjust_sg(cmd, false, cmd->write_len); TRACE_EXIT(); return; @@ -3241,8 +3307,7 @@ void scst_adjust_resp_data_len(struct scst_cmd *cmd) "sg_cnt %d)", cmd->adjusted_resp_data_len, cmd, cmd->sg, cmd->sg_cnt); scst_check_restore_sg_buff(cmd); - scst_adjust_sg(cmd, cmd->sg, &cmd->sg_cnt, - cmd->adjusted_resp_data_len); + scst_adjust_sg(cmd, true, cmd->adjusted_resp_data_len); } out: @@ -3761,9 +3826,7 @@ void scst_free_device(struct scst_device *dev) bool scst_device_is_exported(struct scst_device *dev) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif WARN_ON_ONCE(!dev->dev_tgt_dev_list.next); @@ -3811,20 +3874,35 @@ out: * The activity supposed to be suspended and scst_mutex held or the * corresponding target supposed to be stopped. */ -static void scst_del_free_acg_dev(struct scst_acg_dev *acg_dev, bool del_sysfs) +static void scst_del_acg_dev(struct scst_acg_dev *acg_dev, bool del_sysfs) { - TRACE_ENTRY(); - - TRACE_DBG("Removing acg_dev %p from acg_dev_list and dev_acg_dev_list", - acg_dev); - list_del(&acg_dev->acg_dev_list_entry); + TRACE_DBG("Removing acg_dev %p from dev_acg_dev_list", acg_dev); list_del(&acg_dev->dev_acg_dev_list_entry); if (del_sysfs) scst_acg_dev_sysfs_del(acg_dev); +} +/* + * The activity supposed to be suspended and scst_mutex held or the + * corresponding target supposed to be stopped. + */ +static void scst_free_acg_dev(struct scst_acg_dev *acg_dev) +{ kmem_cache_free(scst_acgd_cachep, acg_dev); +} +/* + * The activity supposed to be suspended and scst_mutex held or the + * corresponding target supposed to be stopped. + */ +static void scst_del_free_acg_dev(struct scst_acg_dev *acg_dev, bool del_sysfs) +{ + TRACE_ENTRY(); + TRACE_DBG("Removing acg_dev %p from acg_dev_list", acg_dev); + list_del(&acg_dev->acg_dev_list_entry); + scst_del_acg_dev(acg_dev, del_sysfs); + scst_free_acg_dev(acg_dev); TRACE_EXIT(); return; } @@ -3949,6 +4027,7 @@ struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, goto out; } + kref_init(&acg->acg_kref); acg->tgt = tgt; INIT_LIST_HEAD(&acg->acg_dev_list); INIT_LIST_HEAD(&acg->acg_sess_list); @@ -4000,37 +4079,28 @@ out_free: goto out; } -/* The activity supposed to be suspended and scst_mutex held */ -void scst_del_free_acg(struct scst_acg *acg) +/** + * scst_del_acg - delete an ACG from the per-target ACG list and from sysfs + * + * The caller must hold scst_mutex and activity must have been suspended. + * + * Note: It is the responsibility of the caller to make sure that + * scst_put_acg() gets invoked. + */ +static void scst_del_acg(struct scst_acg *acg) { struct scst_acn *acn, *acnt; struct scst_acg_dev *acg_dev, *acg_dev_tmp; - TRACE_ENTRY(); + scst_assert_activity_suspended(); + lockdep_assert_held(&scst_mutex); - TRACE_DBG("Clearing acg %s from list", acg->acg_name); - - sBUG_ON(!list_empty(&acg->acg_sess_list)); - - /* Freeing acg_devs */ list_for_each_entry_safe(acg_dev, acg_dev_tmp, &acg->acg_dev_list, - acg_dev_list_entry) { - struct scst_tgt_dev *tgt_dev, *tt; - list_for_each_entry_safe(tgt_dev, tt, - &acg_dev->dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - if (tgt_dev->acg_dev == acg_dev) - scst_free_tgt_dev(tgt_dev); - } - scst_del_free_acg_dev(acg_dev, true); - } + acg_dev_list_entry) + scst_del_acg_dev(acg_dev, true); - /* Freeing names */ - list_for_each_entry_safe(acn, acnt, &acg->acn_list, acn_list_entry) { - scst_del_free_acn(acn, - list_is_last(&acn->acn_list_entry, &acg->acn_list)); - } - INIT_LIST_HEAD(&acg->acn_list); + list_for_each_entry_safe(acn, acnt, &acg->acn_list, acn_list_entry) + scst_del_acn(acn); #ifdef CONFIG_SCST_PROC list_del(&acg->acg_list_entry); @@ -4040,19 +4110,99 @@ void scst_del_free_acg(struct scst_acg *acg) list_del(&acg->acg_list_entry); scst_acg_sysfs_del(acg); - } else + } else { acg->tgt->default_acg = NULL; + } #endif +} - sBUG_ON(!list_empty(&acg->acg_sess_list)); - sBUG_ON(!list_empty(&acg->acg_dev_list)); - sBUG_ON(!list_empty(&acg->acn_list)); +/** + * scst_free_acg - free an ACG + * + * The caller must hold scst_mutex and activity must have been suspended. + */ +static void scst_free_acg(struct scst_acg *acg) +{ + struct scst_acg_dev *acg_dev, *acg_dev_tmp; + struct scst_acn *acn, *acnt; + + TRACE_DBG("Freeing acg %s/%s", acg->tgt->tgt_name, acg->acg_name); + + list_for_each_entry_safe(acg_dev, acg_dev_tmp, &acg->acg_dev_list, + acg_dev_list_entry) { + struct scst_tgt_dev *tgt_dev, *tt; + list_for_each_entry_safe(tgt_dev, tt, + &acg_dev->dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + if (tgt_dev->acg_dev == acg_dev) + scst_free_tgt_dev(tgt_dev); + } + scst_free_acg_dev(acg_dev); + } + + list_for_each_entry_safe(acn, acnt, &acg->acn_list, acn_list_entry) { + scst_free_acn(acn, + list_is_last(&acn->acn_list_entry, &acg->acn_list)); + } kfree(acg->acg_name); kfree(acg); +} - TRACE_EXIT(); - return; +static void scst_release_acg(struct kref *kref) +{ + struct scst_acg *acg = container_of(kref, struct scst_acg, acg_kref); + + scst_free_acg(acg); +} + +void scst_put_acg(struct scst_acg *acg) +{ + kref_put(&acg->acg_kref, scst_release_acg); +} + +void scst_get_acg(struct scst_acg *acg) +{ + kref_get(&acg->acg_kref); +} + +/** + * scst_close_del_free_acg - close sessions, delete and free an ACG + * + * The caller must hold scst_mutex and activity must have been suspended. + * + * Note: deleting and freeing the ACG happens asynchronously. Each time a + * session is closed the ACG reference count is decremented, and if that + * reference count drops to zero the ACG is freed. + */ +int scst_del_free_acg(struct scst_acg *acg, bool close_sessions) +{ + struct scst_tgt *tgt = acg->tgt; + struct scst_session *sess, *sess_tmp; + + scst_assert_activity_suspended(); + lockdep_assert_held(&scst_mutex); + + if ((!close_sessions && !list_empty(&acg->acg_sess_list)) || + (close_sessions && !tgt->tgtt->close_session)) + return -EBUSY; + + scst_del_acg(acg); + + if (close_sessions) { + TRACE_DBG("Closing sessions for group %s/%s", tgt->tgt_name, + acg->acg_name); + list_for_each_entry_safe(sess, sess_tmp, &acg->acg_sess_list, + acg_sess_list_entry) { + TRACE_DBG("Closing session %s/%s/%s", tgt->tgt_name, + acg->acg_name, sess->initiator_name); + tgt->tgtt->close_session(sess); + } + } + + scst_put_acg(acg); + + return 0; } #ifndef CONFIG_SCST_PROC @@ -4082,17 +4232,18 @@ static struct scst_tgt_dev *scst_find_shared_io_tgt_dev( struct scst_tgt_dev *tgt_dev) { struct scst_tgt_dev *res = NULL; + struct scst_session *sess = tgt_dev->sess; struct scst_acg *acg = tgt_dev->acg_dev->acg; struct scst_tgt_dev *t; TRACE_ENTRY(); TRACE_DBG("tgt_dev %s (acg %p, io_grouping_type %d)", - tgt_dev->sess->initiator_name, acg, acg->acg_io_grouping_type); + sess->initiator_name, acg, acg->acg_io_grouping_type); switch (acg->acg_io_grouping_type) { case SCST_IO_GROUPING_AUTO: - if (tgt_dev->sess->initiator_name == NULL) + if (sess->initiator_name == NULL) goto out; list_for_each_entry(t, &tgt_dev->dev->dev_tgt_dev_list, @@ -4107,7 +4258,7 @@ static struct scst_tgt_dev *scst_find_shared_io_tgt_dev( /* We check other ACG's as well */ if (strcmp(t->sess->initiator_name, - tgt_dev->sess->initiator_name) == 0) + sess->initiator_name) == 0) goto found; } break; @@ -4243,6 +4394,8 @@ static int scst_ioc_keeper_thread(void *arg) int scst_tgt_dev_setup_threads(struct scst_tgt_dev *tgt_dev) { int res = 0; + struct scst_session *sess = tgt_dev->sess; + struct scst_tgt_template *tgtt = sess->tgt->tgtt; struct scst_device *dev = tgt_dev->dev; struct scst_async_io_context_keeper *aic_keeper; @@ -4300,7 +4453,7 @@ int scst_tgt_dev_setup_threads(struct scst_tgt_dev *tgt_dev) tgt_dev->aic_keeper = aic_keeper; res = scst_add_threads(tgt_dev->active_cmd_threads, NULL, NULL, - tgt_dev->sess->tgt->tgtt->threads_num); + tgtt->threads_num); goto out; } @@ -4325,8 +4478,8 @@ int scst_tgt_dev_setup_threads(struct scst_tgt_dev *tgt_dev) } res = scst_add_threads(tgt_dev->active_cmd_threads, NULL, - tgt_dev, - dev->threads_num + tgt_dev->sess->tgt->tgtt->threads_num); + tgt_dev, + dev->threads_num + tgtt->threads_num); if (res != 0) { /* Let's clear here, because no threads could be run */ tgt_dev->active_cmd_threads->io_context = NULL; @@ -4338,7 +4491,7 @@ int scst_tgt_dev_setup_threads(struct scst_tgt_dev *tgt_dev) tgt_dev->active_cmd_threads = &dev->dev_cmd_threads; res = scst_add_threads(tgt_dev->active_cmd_threads, dev, NULL, - tgt_dev->sess->tgt->tgtt->threads_num); + tgtt->threads_num); break; } default: @@ -4380,6 +4533,8 @@ static void scst_aic_keeper_release(struct kref *kref) /* scst_mutex supposed to be held */ void scst_tgt_dev_stop_threads(struct scst_tgt_dev *tgt_dev) { + struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt; + TRACE_ENTRY(); if (tgt_dev->dev->threads_num < 0) @@ -4394,7 +4549,7 @@ void scst_tgt_dev_stop_threads(struct scst_tgt_dev *tgt_dev) } else if (tgt_dev->active_cmd_threads == &tgt_dev->dev->dev_cmd_threads) { /* Per device shared threads */ scst_del_threads(tgt_dev->active_cmd_threads, - tgt_dev->sess->tgt->tgtt->threads_num); + tgtt->threads_num); } else if (tgt_dev->active_cmd_threads == &tgt_dev->tgt_dev_cmd_threads) { /* Per tgt_dev threads */ scst_del_threads(tgt_dev->active_cmd_threads, -1); @@ -4418,6 +4573,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess, struct scst_acg_dev *acg_dev, struct scst_tgt_dev **out_tgt_dev) { int res = 0; + struct scst_tgt_template *tgtt = sess->tgt->tgtt; int ini_sg, ini_unchecked_isa_dma, ini_use_clustering; struct scst_tgt_dev *tgt_dev; struct scst_device *dev = acg_dev->dev; @@ -4558,7 +4714,7 @@ out_pr_clear: scst_pr_clear_tgt_dev(tgt_dev); out_dec_free: - if (tgt_dev->sess->tgt->tgtt->get_initiator_port_transport_id == NULL) + if (tgtt->get_initiator_port_transport_id == NULL) dev->not_pr_supporting_tgt_devs_num--; out_free: @@ -4591,6 +4747,7 @@ void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA) */ static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev) { + struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt; struct scst_device *dev = tgt_dev->dev; TRACE_ENTRY(); @@ -4603,7 +4760,7 @@ static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev) scst_tgt_dev_sysfs_del(tgt_dev); - if (tgt_dev->sess->tgt->tgtt->get_initiator_port_transport_id == NULL) + if (tgtt->get_initiator_port_transport_id == NULL) dev->not_pr_supporting_tgt_devs_num--; scst_clear_reservation(tgt_dev); @@ -4737,20 +4894,29 @@ out_free: } /* The activity supposed to be suspended and scst_mutex held */ -void scst_del_free_acn(struct scst_acn *acn, bool reassign) +static void scst_del_acn(struct scst_acn *acn) { - TRACE_ENTRY(); - list_del(&acn->acn_list_entry); scst_acn_sysfs_del(acn); +} +/* The activity supposed to be suspended and scst_mutex held */ +static void scst_free_acn(struct scst_acn *acn, bool reassign) +{ kfree(acn->name); kfree(acn); if (reassign) scst_check_reassign_sessions(); +} +/* The activity supposed to be suspended and scst_mutex held */ +void scst_del_free_acn(struct scst_acn *acn, bool reassign) +{ + TRACE_ENTRY(); + scst_del_acn(acn); + scst_free_acn(acn, reassign); TRACE_EXIT(); return; } @@ -4982,7 +5148,6 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, { struct scst_cmd *ws_cmd = wsp->ws_orig_cmd; struct scatterlist *ws_sg = wsp->ws_sg; - int ws_sg_cnt = wsp->ws_sg_cnt; int res; uint8_t write16_cdb[16]; int len = blocks << ws_cmd->dev->block_shift; @@ -4990,7 +5155,7 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, TRACE_ENTRY(); - EXTRACHECKS_BUG_ON(blocks > ws_sg_cnt); + EXTRACHECKS_BUG_ON(blocks > wsp->ws_sg_cnt); if (unlikely(test_bit(SCST_CMD_ABORTED, &ws_cmd->cmd_flags)) || unlikely(ws_cmd->completed)) { @@ -5434,6 +5599,7 @@ void scst_free_session(struct scst_session *sess) TRACE_DBG("Removing session %p from acg %s", sess, sess->acg->acg_name); list_del(&sess->acg_sess_list_entry); + scst_put_acg(sess->acg); mutex_unlock(&scst_mutex); @@ -6410,7 +6576,12 @@ int scst_get_buf_full(struct scst_cmd *cmd, uint8_t **buf) len = scst_get_buf_next(cmd, &tmp_buf); } +#ifdef __COVERITY__ + /* Help Coverity recognize that vmalloc(0) returns NULL. */ + *buf = full_size ? vmalloc(full_size) : NULL; +#else *buf = vmalloc(full_size); +#endif if (*buf == NULL) { TRACE(TRACE_OUT_OF_MEM, "vmalloc() failed for opcode " "%s", scst_get_opcode_name(cmd)); @@ -8126,20 +8297,18 @@ again: goto out_unlock; } else TRACE_MGMT_DBG("Setting pending UA cmd %p (tgt_dev %p, dev %s, " - "initiator %s)", cmd->tgt_dev, cmd, cmd->dev->virt_name, + "initiator %s)", cmd, cmd->tgt_dev, cmd->dev->virt_name, cmd->sess->initiator_name); UA_entry = list_first_entry(&cmd->tgt_dev->UA_list, typeof(*UA_entry), UA_list_entry); - TRACE_MGMT_DBG("Setting pending UA %p to cmd %p", UA_entry, cmd); - - TRACE_DBG("next %p UA_entry %p", - cmd->tgt_dev->UA_list.next, UA_entry); + TRACE_DBG("Setting pending UA %p to cmd %p", UA_entry, cmd); if (UA_entry->global_UA && first) { TRACE_MGMT_DBG("Global UA %p detected", UA_entry); +#if !defined(__CHECKER__) spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock); /* @@ -8159,6 +8328,7 @@ again: spin_lock(&tgt_dev->tgt_dev_lock); } } +#endif first = false; global_unlock = true; @@ -8221,6 +8391,7 @@ again: out_unlock: if (global_unlock) { +#if !defined(__CHECKER__) for (i = SESS_TGT_DEV_LIST_HASH_SIZE-1; i >= 0; i--) { struct list_head *head = &sess->sess_tgt_dev_list[i]; struct scst_tgt_dev *tgt_dev; @@ -8232,6 +8403,7 @@ out_unlock: local_bh_enable(); spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock); +#endif } spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock); @@ -9023,16 +9195,11 @@ static void scst_process_qerr(struct scst_cmd *cmd) int scst_process_check_condition(struct scst_cmd *cmd) { int res; - struct scst_order_data *order_data; - struct scst_device *dev; TRACE_ENTRY(); EXTRACHECKS_BUG_ON(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags)); - order_data = cmd->cur_order_data; - dev = cmd->dev; - TRACE_DBG("CHECK CONDITION for cmd %p (tgt_dev %p)", cmd, cmd->tgt_dev); scst_process_qerr(cmd); @@ -9394,7 +9561,7 @@ int scst_parse_descriptors(struct scst_cmd *cmd) res = scst_parse_unmap_descriptors(cmd); break; default: - sBUG_ON(1); + sBUG(); res = -1; break; } @@ -9412,7 +9579,7 @@ static void scst_free_descriptors(struct scst_cmd *cmd) scst_free_unmap_descriptors(cmd); break; default: - sBUG_ON(1); + sBUG(); break; } @@ -9749,7 +9916,8 @@ void scst_vfs_unlink_and_put(struct nameidata *nd) #else void scst_vfs_unlink_and_put(struct path *path) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) && \ + (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7) vfs_unlink(path->dentry->d_parent->d_inode, path->dentry); #else vfs_unlink(path->dentry->d_parent->d_inode, path->dentry, NULL); diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 867e7f6b5..209e9ce54 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -179,6 +179,7 @@ cpumask_t default_cpu_mask; static unsigned int scst_max_cmd_mem; unsigned int scst_max_dev_cmd_mem; +int scst_forcibly_close_sessions; module_param_named(scst_threads, scst_threads, int, 0); MODULE_PARM_DESC(scst_threads, "SCSI target threads count"); @@ -191,6 +192,13 @@ module_param_named(scst_max_dev_cmd_mem, scst_max_dev_cmd_mem, int, S_IRUGO); MODULE_PARM_DESC(scst_max_dev_cmd_mem, "Maximum memory allowed to be consumed " "by all SCSI commands of a device at any given time in MB"); +module_param_named(forcibly_close_sessions, scst_forcibly_close_sessions, int, + S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(forcibly_close_sessions, +"If enabled, close the sessions associated with an access control group (ACG)" +" when an ACG is deleted via sysfs instead of returning -EBUSY"); + + struct scst_dev_type scst_null_devtype = { .name = "none", .threads_num = -1, @@ -669,11 +677,11 @@ again: scst_tg_tgt_remove_by_tgt(tgt); #ifndef CONFIG_SCST_PROC - scst_del_free_acg(tgt->default_acg); + scst_del_free_acg(tgt->default_acg, false); list_for_each_entry_safe(acg, acg_tmp, &tgt->tgt_acg_list, acg_list_entry) { - scst_del_free_acg(acg); + scst_del_free_acg(acg, false); } #endif @@ -1051,6 +1059,7 @@ EXPORT_SYMBOL_GPL(scst_suspend_activity); static void __scst_resume_activity(void) { struct scst_cmd_threads *l; + struct scst_mgmt_cmd *m; TRACE_ENTRY(); @@ -1077,15 +1086,14 @@ static void __scst_resume_activity(void) wake_up_all(&scst_init_cmd_list_waitQ); spin_lock_irq(&scst_mcmd_lock); - if (!list_empty(&scst_delayed_mgmt_cmd_list)) { - struct scst_mgmt_cmd *m; - m = list_first_entry(&scst_delayed_mgmt_cmd_list, typeof(*m), - mgmt_cmd_list_entry); + list_for_each_entry(m, &scst_delayed_mgmt_cmd_list, + mgmt_cmd_list_entry) { TRACE_MGMT_DBG("Moving delayed mgmt cmd %p to head of active " "mgmt cmd list", m); - list_move(&m->mgmt_cmd_list_entry, &scst_active_mgmt_cmd_list); } + list_splice(&scst_delayed_mgmt_cmd_list, &scst_active_mgmt_cmd_list); spin_unlock_irq(&scst_mcmd_lock); + wake_up_all(&scst_mgmt_cmd_list_waitQ); out: @@ -1145,9 +1153,9 @@ static int scst_register_device(struct scsi_device *scsidp) dev->type = scsidp->type; - dev->virt_name = kasprintf(GFP_KERNEL, "%d:%d:%d:%d", - scsidp->host->host_no, - scsidp->channel, scsidp->id, scsidp->lun); + dev->virt_name = kasprintf(GFP_KERNEL, "%d:%d:%d:%lld", + scsidp->host->host_no, scsidp->channel, + scsidp->id, (u64)scsidp->lun); if (dev->virt_name == NULL) { PRINT_ERROR("%s", "Unable to alloc device name"); res = -ENOMEM; @@ -1190,9 +1198,9 @@ static int scst_register_device(struct scsi_device *scsidp) goto out_del_unlocked; #endif - PRINT_INFO("Attached to scsi%d, channel %d, id %d, lun %d, " - "type %d", scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->type); + PRINT_INFO("Attached to scsi%d, channel %d, id %d, lun %lld, type %d", + scsidp->host->host_no, scsidp->channel, scsidp->id, + (u64)scsidp->lun, scsidp->type); out: TRACE_EXIT_RES(res); @@ -1226,9 +1234,7 @@ static struct scst_device *__scst_lookup_device(struct scsi_device *scsidp) { struct scst_device *d; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(d, &scst_dev_list, dev_list_entry) if (d->scsi_dev == scsidp) @@ -1261,9 +1267,9 @@ static void scst_unregister_device(struct scsi_device *scsidp) } if (dev == NULL) { - PRINT_ERROR("SCST device for SCSI device %d:%d:%d:%d not found", - scsidp->host->host_no, scsidp->channel, scsidp->id, - scsidp->lun); + PRINT_ERROR("SCST device for SCSI device %d:%d:%d:%lld not found", + scsidp->host->host_no, scsidp->channel, scsidp->id, + (u64)scsidp->lun); goto out_unlock; } @@ -1287,9 +1293,9 @@ static void scst_unregister_device(struct scsi_device *scsidp) scst_dev_sysfs_del(dev); - PRINT_INFO("Detached from scsi%d, channel %d, id %d, lun %d, type %d", - scsidp->host->host_no, scsidp->channel, scsidp->id, - scsidp->lun, scsidp->type); + PRINT_INFO("Detached from scsi%d, channel %d, id %d, lun %lld, type %d", + scsidp->host->host_no, scsidp->channel, scsidp->id, + (u64)scsidp->lun, scsidp->type); scst_free_device(dev); @@ -2089,7 +2095,7 @@ assign: dev->threads_num = handler->threads_num; dev->threads_pool_type = handler->threads_pool_type; - dev->max_write_same_len = 512 * 1024 * 1024; /* 512 MB */ + dev->max_write_same_len = 256 * 1024 * 1024; /* 256 MB */ if (handler->attach) { TRACE_DBG("Calling new dev handler's attach(%p)", dev); @@ -2455,7 +2461,7 @@ static int __init init_scst(void) mutex_init(&scst_suspend_mutex); mutex_init(&scst_cmd_threads_mutex); INIT_LIST_HEAD(&scst_cmd_threads_list); - cpus_setall(default_cpu_mask); + cpumask_setall(&default_cpu_mask); scst_init_threads(&scst_main_cmd_threads); @@ -2656,7 +2662,7 @@ out_thread_free: #ifdef CONFIG_SCST_PROC out_free_acg: - scst_del_free_acg(scst_default_acg); + scst_del_free_acg(scst_default_acg, false); #endif out_destroy_sgv_pool: @@ -2738,7 +2744,7 @@ static void __exit exit_scst(void) scsi_unregister_interface(&scst_interface); #ifdef CONFIG_SCST_PROC - scst_del_free_acg(scst_default_acg); + scst_del_free_acg(scst_default_acg, false); #endif scst_sgv_pools_deinit(); diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index c20edd010..15981530d 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -456,8 +456,10 @@ static struct scst_dev_registrant *scst_pr_add_registrant( * We can't use scst_mutex here, because of the circular * locking dependency with dev_pr_mutex. */ +#if !defined(__CHECKER__) if (!dev_lock_locked) spin_lock_bh(&dev->dev_lock); +#endif list_for_each_entry(t, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { if (tid_equal(t->sess->transport_id, transport_id) && (t->sess->tgt->rel_tgt_id == rel_tgt_id) && @@ -472,8 +474,10 @@ static struct scst_dev_registrant *scst_pr_add_registrant( break; } } +#if !defined(__CHECKER__) if (!dev_lock_locked) spin_unlock_bh(&dev->dev_lock); +#endif list_add_tail(®->dev_registrants_list_entry, &dev->dev_registrants_list); @@ -858,15 +862,16 @@ out: static void scst_pr_remove_device_files(struct scst_tgt_dev *tgt_dev) { - int res = 0; struct scst_device *dev = tgt_dev->dev; TRACE_ENTRY(); scst_assert_pr_mutex_held(dev); - res = dev->pr_file_name ? scst_remove_file(dev->pr_file_name) : -ENOENT; - res = dev->pr_file_name1 ? scst_remove_file(dev->pr_file_name1) : -ENOENT; + if (dev->pr_file_name) + scst_remove_file(dev->pr_file_name); + if (dev->pr_file_name1) + scst_remove_file(dev->pr_file_name1); TRACE_EXIT(); return; @@ -2566,7 +2571,7 @@ void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) { struct scst_device *dev = cmd->dev; - uint8_t b[24]; + uint8_t b[24] = { }; int size = 0; TRACE_ENTRY(); @@ -2579,8 +2584,6 @@ void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, goto out; } - memset(b, 0, sizeof(b)); - put_unaligned_be32(dev->pr_generation, &b[0]); if (!dev->pr_is_set) { diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index ac0eeee89..bba383e43 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -148,6 +148,8 @@ extern int scst_threads; extern unsigned int scst_max_dev_cmd_mem; +extern int scst_forcibly_close_sessions; + extern mempool_t *scst_mgmt_mempool; extern mempool_t *scst_mgmt_stub_mempool; extern mempool_t *scst_ua_mempool; @@ -339,7 +341,9 @@ bool scst_device_is_exported(struct scst_device *dev); struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, const char *acg_name, bool tgt_acg); -void scst_del_free_acg(struct scst_acg *acg); +int scst_del_free_acg(struct scst_acg *acg, bool close_sessions); +void scst_get_acg(struct scst_acg *acg); +void scst_put_acg(struct scst_acg *acg); struct scst_acg *scst_tgt_find_acg(struct scst_tgt *tgt, const char *name); struct scst_acg *scst_find_acg(const struct scst_session *sess); @@ -348,6 +352,7 @@ void scst_check_reassign_sessions(void); int scst_sess_alloc_tgt_devs(struct scst_session *sess); void scst_sess_free_tgt_devs(struct scst_session *sess); +struct scst_tgt_dev *scst_lookup_tgt_dev(struct scst_session *sess, u64 lun); void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA); int scst_acg_add_lun(struct scst_acg *acg, struct kobject *parent, @@ -628,9 +633,7 @@ static inline void scst_reserve_dev(struct scst_device *dev, static inline void scst_clear_dev_reservation(struct scst_device *dev) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&dev->dev_lock); -#endif dev->reserved_by = NULL; } diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index 2a3bc1981..d00101511 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -991,7 +991,7 @@ static int scst_proc_del_free_acg(struct scst_acg *acg, int remove_proc) } if (remove_proc) scst_proc_del_acg_tree(acg_proc_root, acg->acg_name); - scst_del_free_acg(acg); + scst_del_free_acg(acg, false); } out: TRACE_EXIT_RES(res); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index d9768fa5a..88e714933 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -1885,7 +1885,7 @@ static ssize_t __scst_acg_cpu_mask_show(struct scst_acg *acg, char *buf) res = cpumask_scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, &acg->acg_cpu_mask); #endif - if (!cpus_equal(acg->acg_cpu_mask, default_cpu_mask)) + if (!cpumask_equal(&acg->acg_cpu_mask, &default_cpu_mask)) res += sprintf(&buf[res], "\n%s\n", SCST_SYSFS_KEY_MARK); return res; @@ -1991,7 +1991,7 @@ static ssize_t __scst_acg_cpu_mask_store(struct scst_acg *acg, goto out_release; } - if (cpus_equal(acg->acg_cpu_mask, work->cpu_mask)) + if (cpumask_equal(&acg->acg_cpu_mask, &work->cpu_mask)) goto out; work->tgt = acg->tgt; @@ -2122,12 +2122,16 @@ static int scst_process_ini_group_mgmt_store(char *buffer, res = -EINVAL; goto out_unlock; } - if (!scst_acg_sess_is_empty(acg)) { - PRINT_ERROR("Group %s is not empty", acg->acg_name); - res = -EBUSY; + res = scst_del_free_acg(acg, scst_forcibly_close_sessions); + if (res) { + if (scst_forcibly_close_sessions) + PRINT_ERROR("Removing group %s failed", + acg->acg_name); + else + PRINT_ERROR("Group %s is not empty", + acg->acg_name); goto out_unlock; } - scst_del_free_acg(acg); break; } @@ -5081,7 +5085,8 @@ static int scst_process_devt_pass_through_mgmt_store(char *buffer, { int res = 0; char *pp, *action, *devstr; - unsigned int host, channel, id, lun; + unsigned int host, channel, id; + u64 lun; struct scst_device *d, *dev = NULL; TRACE_ENTRY(); @@ -5103,10 +5108,10 @@ static int scst_process_devt_pass_through_mgmt_store(char *buffer, goto out_syntax_err; } - if (sscanf(devstr, "%u:%u:%u:%u", &host, &channel, &id, &lun) != 4) + if (sscanf(devstr, "%u:%u:%u:%llu", &host, &channel, &id, &lun) != 4) goto out_syntax_err; - TRACE_DBG("Dev %d:%d:%d:%d", host, channel, id, lun); + TRACE_DBG("Dev %d:%d:%d:%lld", host, channel, id, lun); res = mutex_lock_interruptible(&scst_mutex); if (res != 0) @@ -5123,13 +5128,13 @@ static int scst_process_devt_pass_through_mgmt_store(char *buffer, d->scsi_dev->id == id && d->scsi_dev->lun == lun) { dev = d; - TRACE_DBG("Dev %p (%d:%d:%d:%d) found", + TRACE_DBG("Dev %p (%d:%d:%d:%lld) found", dev, host, channel, id, lun); break; } } if (dev == NULL) { - PRINT_ERROR("Device %d:%d:%d:%d not found", + PRINT_ERROR("Device %d:%d:%d:%lld not found", host, channel, id, lun); res = -EINVAL; goto out_unlock; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index c1465cd1d..a490321d7 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -1697,7 +1697,6 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd) goto out; default: sBUG(); - goto out; } } @@ -1801,8 +1800,6 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state, { TRACE_ENTRY(); - EXTRACHECKS_BUG_ON(cmd->pr_abort_counter != NULL); - scst_set_exec_time(cmd); TRACE(TRACE_SCSI, "cmd %p, status %x, msg_status %x, host_status %x, " @@ -2288,7 +2285,7 @@ static int scst_report_supported_opcodes(struct scst_cmd *cmd) } break; default: - sBUG_ON(1); + sBUG(); goto out_compl; } @@ -4330,6 +4327,25 @@ out: return; } +struct scst_tgt_dev *scst_lookup_tgt_dev(struct scst_session *sess, u64 lun) +{ + struct list_head *head; + struct scst_tgt_dev *tgt_dev; + +#ifdef CONFIG_SCST_EXTRACHECKS + if (scst_get_cmd_counter() == 0) + lockdep_assert_held(&scst_mutex); +#endif + + head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)]; + list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { + if (tgt_dev->lun == lun) + return tgt_dev; + } + + return NULL; +} + /* * Returns 0 on success, > 0 when we need to wait for unblock, * < 0 if there is no device (lun) or device type handler. @@ -4341,30 +4357,21 @@ static int scst_translate_lun(struct scst_cmd *cmd) { struct scst_tgt_dev *tgt_dev = NULL; int res; + bool nul_dev = false; TRACE_ENTRY(); cmd->cpu_cmd_counter = scst_get(); if (likely(!test_bit(SCST_FLAG_SUSPENDED, &scst_flags))) { - struct list_head *head = - &cmd->sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(cmd->lun)]; TRACE_DBG("Finding tgt_dev for cmd %p (lun %lld)", cmd, (long long unsigned int)cmd->lun); res = -1; - list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { - if (tgt_dev->lun == cmd->lun) { - TRACE_DBG("tgt_dev %p found", tgt_dev); - - if (unlikely(tgt_dev->dev->handler == - &scst_null_devtype)) { - PRINT_INFO("Dev handler for device " - "%lld is NULL, the device will not " - "be visible remotely", - (long long unsigned int)cmd->lun); - break; - } + tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun); + if (tgt_dev) { + TRACE_DBG("tgt_dev %p found", tgt_dev); + if (likely(tgt_dev->dev->handler != &scst_null_devtype)) { cmd->cmd_threads = tgt_dev->active_cmd_threads; cmd->tgt_dev = tgt_dev; cmd->cur_order_data = tgt_dev->curr_order_data; @@ -4372,15 +4379,21 @@ static int scst_translate_lun(struct scst_cmd *cmd) cmd->devt = tgt_dev->dev->handler; res = 0; - break; + } else { + PRINT_INFO("Dev handler for device %lld is NULL, " + "the device will not be visible remotely", + (long long unsigned int)cmd->lun); + nul_dev = true; } } - if (res != 0) { - TRACE(TRACE_MINOR, - "tgt_dev for LUN %lld not found, command to " - "unexisting LU (initiator %s, target %s)?", - (long long unsigned int)cmd->lun, - cmd->sess->initiator_name, cmd->tgt->tgt_name); + if (unlikely(res != 0)) { + if (!nul_dev) { + TRACE(TRACE_MINOR, + "tgt_dev for LUN %lld not found, command to " + "unexisting LU (initiator %s, target %s)?", + (long long unsigned int)cmd->lun, + cmd->sess->initiator_name, cmd->tgt->tgt_name); + } scst_put(cmd->cpu_cmd_counter); } } else { @@ -4855,12 +4868,10 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic) default: PRINT_CRIT_ERROR("cmd %p is in invalid state %d)", cmd, cmd->state); +#if !defined(__CHECKER__) spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock); - sBUG(); -#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 - spin_lock_irq(&cmd->cmd_threads->cmd_list_lock); - break; #endif + sBUG(); } #endif wake_up(&cmd->cmd_threads->cmd_list_waitQ); @@ -4993,7 +5004,6 @@ out: static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) { struct scst_tgt_dev *tgt_dev; - struct list_head *head; int res; TRACE_ENTRY(); @@ -5005,19 +5015,15 @@ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) if (unlikely(res != 0)) goto out; - res = -1; - - head = &mcmd->sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(mcmd->lun)]; - list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { - if (tgt_dev->lun == mcmd->lun) { - TRACE_DBG("tgt_dev %p found", tgt_dev); - mcmd->mcmd_tgt_dev = tgt_dev; - res = 0; - break; - } - } - if (mcmd->mcmd_tgt_dev == NULL) + tgt_dev = scst_lookup_tgt_dev(mcmd->sess, mcmd->lun); + if (tgt_dev) { + TRACE_DBG("tgt_dev %p found", tgt_dev); + mcmd->mcmd_tgt_dev = tgt_dev; + res = 0; + } else { scst_put(mcmd->cpu_cmd_counter); + res = -1; + } out: TRACE_EXIT_HRES(res); @@ -5475,15 +5481,15 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd) "cmd_finish_wait_count %d, cmd_done_wait_count %d)", mcmd, mcmd->state, mcmd->fn, mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count); +#if !defined(__CHECKER__) spin_unlock_irq(&scst_mcmd_lock); +#endif res = -1; sBUG(); - goto out; } spin_unlock_irq(&scst_mcmd_lock); -out: return res; } @@ -5645,28 +5651,20 @@ static int scst_abort_task_set(struct scst_mgmt_cmd *mcmd) return res; } -static int scst_is_cmd_belongs_to_dev(struct scst_cmd *cmd, - struct scst_device *dev) +static bool scst_is_cmd_belongs_to_dev(struct scst_cmd *cmd, + struct scst_device *dev) { - struct scst_tgt_dev *tgt_dev = NULL; - struct list_head *head; - int res = 0; + struct scst_tgt_dev *tgt_dev; + bool res; TRACE_ENTRY(); - TRACE_DBG("Finding match for dev %s and cmd %p (lun %lld)", dev->virt_name, - cmd, (long long unsigned int)cmd->lun); + TRACE_DBG("Finding match for dev %s and cmd %p (lun %lld)", + dev->virt_name, cmd, (long long unsigned int)cmd->lun); - head = &cmd->sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(cmd->lun)]; - list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) { - if (tgt_dev->lun == cmd->lun) { - TRACE_DBG("dev %s found", tgt_dev->dev->virt_name); - res = (tgt_dev->dev == dev); - goto out; - } - } + tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun); + res = tgt_dev && tgt_dev->dev == dev; -out: TRACE_EXIT_HRES(res); return res; } @@ -5996,6 +5994,8 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd) TRACE(TRACE_MGMT, "Resetting host %d bus ", dev->scsi_dev->host->host_no); rc = scsi_reset_provider(dev->scsi_dev, SCSI_TRY_RESET_DEVICE); + TRACE(TRACE_MGMT, "scsi_reset_provider(%s) returned %d", + dev->virt_name, rc); #if 0 if (rc != SUCCESS && mcmd->status == SCST_MGMT_STATUS_SUCCESS) scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_FAILED); @@ -6506,11 +6506,6 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd) mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count); sBUG(); -#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 < 6 - /* For suppressing a gcc compiler warning */ - res = -1; - goto out; -#endif } } @@ -6991,9 +6986,7 @@ static char *scst_get_unique_sess_name(struct list_head *sysfs_sess_list, int len = 0, n = 1; BUG_ON(!initiator_name); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif restart: list_for_each_entry(s, sysfs_sess_list, sysfs_sess_list_entry) { @@ -7024,7 +7017,7 @@ restart: static int scst_init_session(struct scst_session *sess) { int res = 0; - struct scst_cmd *cmd; + struct scst_cmd *cmd, *cmd_tmp; struct scst_mgmt_cmd *mcmd, *tm; int mwake = 0; @@ -7038,6 +7031,7 @@ static int scst_init_session(struct scst_session *sess) "(target %s)", sess->acg->acg_name, sess->initiator_name, sess->tgt->tgt_name); + scst_get_acg(sess->acg); list_add_tail(&sess->acg_sess_list_entry, &sess->acg->acg_sess_list); TRACE_DBG("Adding sess %p to tgt->sess_list", sess); @@ -7094,16 +7088,14 @@ failed: else sess->init_phase = SCST_SESS_IPH_FAILED; -restart: - list_for_each_entry(cmd, &sess->init_deferred_cmd_list, - cmd_list_entry) { + list_for_each_entry_safe(cmd, cmd_tmp, &sess->init_deferred_cmd_list, + cmd_list_entry) { TRACE_DBG("Deleting cmd %p from init deferred cmd list", cmd); list_del(&cmd->cmd_list_entry); atomic_dec(&sess->sess_cmd_count); spin_unlock_irq(&sess->sess_list_lock); scst_cmd_init_done(cmd, SCST_CONTEXT_THREAD); spin_lock_irq(&sess->sess_list_lock); - goto restart; } spin_lock(&scst_mcmd_lock); diff --git a/scst/src/scst_tg.c b/scst/src/scst_tg.c index e9a3c91b9..62c7b70f5 100644 --- a/scst/src/scst_tg.c +++ b/scst/src/scst_tg.c @@ -77,9 +77,7 @@ static struct scst_device *__lookup_dev(const char *name) { struct scst_device *dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dev, &scst_dev_list, dev_list_entry) if (strcmp(dev->virt_name, name) == 0) @@ -94,9 +92,7 @@ static struct scst_tgt *__lookup_tgt(const char *name) struct scst_tgt_template *t; struct scst_tgt *tgt; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(t, &scst_template_list, scst_template_list_entry) list_for_each_entry(tgt, &t->tgt_list, tgt_list_entry) @@ -113,9 +109,7 @@ static struct scst_tg_tgt *__lookup_dg_tgt(struct scst_dev_group *dg, struct scst_target_group *tg; struct scst_tg_tgt *tg_tgt; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif BUG_ON(!dg); BUG_ON(!tgt_name); @@ -133,9 +127,7 @@ __lookup_tg_by_name(struct scst_dev_group *dg, const char *name) { struct scst_target_group *tg; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(tg, &dg->tg_list, entry) if (strcmp(tg->name, name) == 0) @@ -151,9 +143,7 @@ __lookup_tg_by_tgt(struct scst_dev_group *dg, const struct scst_tgt *tgt) struct scst_target_group *tg; struct scst_tg_tgt *tg_tgt; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(tg, &dg->tg_list, entry) list_for_each_entry(tg_tgt, &tg->tgt_list, entry) @@ -169,9 +159,7 @@ static struct scst_dg_dev *__lookup_dg_dev_by_dev(struct scst_dev_group *dg, { struct scst_dg_dev *dgd; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dgd, &dg->dev_list, entry) if (dgd->dev == dev) @@ -186,9 +174,7 @@ static struct scst_dg_dev *__lookup_dg_dev_by_name(struct scst_dev_group *dg, { struct scst_dg_dev *dgd; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dgd, &dg->dev_list, entry) if (strcmp(dgd->dev->virt_name, name) == 0) @@ -203,9 +189,7 @@ static struct scst_dg_dev *__global_lookup_dg_dev_by_name(const char *name) struct scst_dev_group *dg; struct scst_dg_dev *dgd; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dg, &scst_dev_group_list, entry) { dgd = __lookup_dg_dev_by_name(dg, name); @@ -220,9 +204,7 @@ static struct scst_dev_group *__lookup_dg_by_name(const char *name) { struct scst_dev_group *dg; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dg, &scst_dev_group_list, entry) if (strcmp(dg->name, name) == 0) @@ -236,9 +218,7 @@ static struct scst_dev_group *__lookup_dg_by_dev(struct scst_device *dev) { struct scst_dev_group *dg; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dg, &scst_dev_group_list, entry) if (__lookup_dg_dev_by_dev(dg, dev)) @@ -355,9 +335,7 @@ static void scst_check_alua_invariant(void) struct scst_target_group *tg; enum scst_tg_state expected_state; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif if (!alua_invariant_check) return; @@ -392,9 +370,7 @@ static void scst_check_alua_invariant(void) static void scst_update_tgt_dev_alua_filter(struct scst_tgt_dev *tgt_dev, enum scst_tg_state state) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif tgt_dev->alua_filter = scst_alua_filter[state]; } @@ -404,9 +380,7 @@ static void scst_tg_change_tgt_dev_state(struct scst_tgt_dev *tgt_dev, enum scst_tg_state state, bool gen_ua) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed", tgt_dev); scst_update_tgt_dev_alua_filter(tgt_dev, state); @@ -421,9 +395,7 @@ void scst_tg_init_tgt_dev(struct scst_tgt_dev *tgt_dev) struct scst_dev_group *dg; struct scst_target_group *tg; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif dg = __lookup_dg_by_dev(tgt_dev->dev); if (dg) { @@ -445,9 +417,7 @@ static void scst_update_tgt_alua_filter(struct scst_target_group *tg, struct scst_dg_dev *dgd; struct scst_tgt_dev *tgt_dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dgd, &tg->dg->dev_list, entry) { list_for_each_entry(tgt_dev, &dgd->dev->dev_tgt_dev_list, @@ -471,9 +441,7 @@ static void scst_reset_tgt_alua_filter(struct scst_target_group *tg, struct scst_dg_dev *dgd; struct scst_tgt_dev *tgt_dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dgd, &tg->dg->dev_list, entry) { list_for_each_entry(tgt_dev, &dgd->dev->dev_tgt_dev_list, @@ -584,9 +552,7 @@ void scst_tg_tgt_remove_by_tgt(struct scst_tgt *tgt) struct scst_target_group *tg; struct scst_tg_tgt *t, *t2; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif BUG_ON(!tgt); list_for_each_entry(dg, &scst_dev_group_list, entry) @@ -723,9 +689,7 @@ static void __scst_tg_set_state(struct scst_target_group *tg, struct scst_tgt *tgt; sBUG_ON(state >= ARRAY_SIZE(scst_alua_filter)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif if (tg->state == state) return; @@ -789,9 +753,7 @@ static void __scst_gen_alua_state_changed_ua(struct scst_target_group *tg) struct scst_tg_tgt *tg_tgt; struct scst_tgt *tgt; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(dg_dev, &tg->dg->dev_list, entry) { dev = dg_dev->dev; @@ -814,9 +776,7 @@ static void __scst_tg_set_preferred(struct scst_target_group *tg, { bool prev_preferred; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif if (tg->preferred == preferred) return; @@ -859,9 +819,7 @@ static void scst_update_dev_alua_filter(struct scst_dev_group *dg, struct scst_tgt_dev *tgt_dev; struct scst_target_group *tg; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { @@ -881,9 +839,7 @@ static void scst_reset_dev_alua_filter(struct scst_device *dev) { struct scst_tgt_dev *tgt_dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) @@ -1063,9 +1019,7 @@ static void __scst_dg_remove(struct scst_dev_group *dg) struct scst_dg_dev *dgdev; struct scst_target_group *tg; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&scst_mutex); -#endif list_del(&dg->entry); scst_dg_sysfs_del(dg); diff --git a/scst_local/Makefile b/scst_local/Makefile index 2dee7552a..7eba6e431 100644 --- a/scst_local/Makefile +++ b/scst_local/Makefile @@ -61,6 +61,7 @@ all: Modules.symvers Module.symvers install: all $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ SCST_INC_DIR=$(SCST_INC_DIR) modules_install SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null) diff --git a/scst_local/in-tree/Makefile-3.15 b/scst_local/in-tree/Makefile-3.15 new file mode 100644 index 000000000..8cbbbff63 --- /dev/null +++ b/scst_local/in-tree/Makefile-3.15 @@ -0,0 +1,2 @@ +obj-$(CONFIG_SCST_LOCAL) += scst_local.o + diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index e872bdeaa..31d18165c 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -88,7 +88,7 @@ static unsigned long scst_local_trace_flag = SCST_LOCAL_DEFAULT_LOG_FLAGS; #define scsi_bufflen(cmd) ((cmd)->request_bufflen) #endif -#define SCST_LOCAL_VERSION "3.0" +#define SCST_LOCAL_VERSION "3.1" static const char *scst_local_version_date = "20110901"; /* Some statistics */ @@ -142,6 +142,8 @@ struct scst_local_sess { spinlock_t aen_lock; struct list_head aen_work_list; /* protected by aen_lock */ + struct work_struct remove_work; + struct list_head sessions_list_entry; }; @@ -152,6 +154,8 @@ static int __scst_local_add_adapter(struct scst_local_tgt *tgt, const char *initiator_name, bool locked); static int scst_local_add_adapter(struct scst_local_tgt *tgt, const char *initiator_name); +static void scst_local_close_session_impl(struct scst_local_sess *sess, + bool async); static void scst_local_remove_adapter(struct scst_local_sess *sess); static int scst_local_add_target(const char *target_name, struct scst_local_tgt **out_tgt); @@ -786,7 +790,7 @@ static ssize_t scst_local_sysfs_mgmt_cmd(char *buf) res = -EINVAL; goto out_unlock; } - scst_local_remove_adapter(sess); + scst_local_close_session_impl(sess, false); } res = 0; @@ -831,7 +835,7 @@ static int scst_local_abort(struct scsi_cmnd *SCpnt) static int scst_local_device_reset(struct scsi_cmnd *SCpnt) { struct scst_local_sess *sess; - __be16 lun; + struct scsi_lun lun; int ret; DECLARE_COMPLETION_ONSTACK(dev_reset_completion); @@ -839,10 +843,11 @@ static int scst_local_device_reset(struct scsi_cmnd *SCpnt) sess = to_scst_lcl_sess(scsi_get_device(SCpnt->device->host)); - lun = cpu_to_be16(SCpnt->device->lun); + int_to_scsilun(SCpnt->device->lun, &lun); ret = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_LUN_RESET, - &lun, sizeof(lun), false, &dev_reset_completion); + lun.scsi_lun, sizeof(lun), false, + &dev_reset_completion); /* Now wait for the completion ... */ wait_for_completion_interruptible(&dev_reset_completion); @@ -860,7 +865,7 @@ static int scst_local_device_reset(struct scsi_cmnd *SCpnt) static int scst_local_target_reset(struct scsi_cmnd *SCpnt) { struct scst_local_sess *sess; - __be16 lun; + struct scsi_lun lun; int ret; DECLARE_COMPLETION_ONSTACK(dev_reset_completion); @@ -868,10 +873,11 @@ static int scst_local_target_reset(struct scsi_cmnd *SCpnt) sess = to_scst_lcl_sess(scsi_get_device(SCpnt->device->host)); - lun = cpu_to_be16(SCpnt->device->lun); + int_to_scsilun(SCpnt->device->lun, &lun); ret = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_TARGET_RESET, - &lun, sizeof(lun), false, &dev_reset_completion); + lun.scsi_lun, sizeof(lun), false, + &dev_reset_completion); /* Now wait for the completion ... */ wait_for_completion_interruptible(&dev_reset_completion); @@ -953,13 +959,14 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt, struct scst_local_sess *sess; struct scatterlist *sgl = NULL; int sgl_count = 0; - __be16 lun; + struct scsi_lun lun; struct scst_cmd *scst_cmd = NULL; scst_data_direction dir; TRACE_ENTRY(); - TRACE_DBG("lun %d, cmd: 0x%02X", SCpnt->device->lun, SCpnt->cmnd[0]); + TRACE_DBG("lun %lld, cmd: 0x%02X", (u64)SCpnt->device->lun, + SCpnt->cmnd[0]); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) /* @@ -1002,9 +1009,9 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt, * get into mem alloc deadlock when mounting file systems over * our devices. */ - lun = cpu_to_be16(SCpnt->device->lun); - scst_cmd = scst_rx_cmd(sess->scst_sess, (const uint8_t *)&lun, - sizeof(lun), SCpnt->cmnd, SCpnt->cmd_len, true); + int_to_scsilun(SCpnt->device->lun, &lun); + scst_cmd = scst_rx_cmd(sess->scst_sess, lun.scsi_lun, sizeof(lun), + SCpnt->cmnd, SCpnt->cmd_len, true); if (!scst_cmd) { PRINT_ERROR("%s", "scst_rx_cmd() failed"); return SCSI_MLQUEUE_HOST_BUSY; @@ -1148,14 +1155,15 @@ static int scst_local_get_max_queue_depth(struct scsi_device *sdev) { int res; struct scst_local_sess *sess; - __be16 lun; + struct scsi_lun lun; TRACE_ENTRY(); sess = to_scst_lcl_sess(scsi_get_device(sdev->host)); - lun = cpu_to_be16(sdev->lun); + int_to_scsilun(sdev->lun, &lun); res = scst_get_max_lun_commands(sess->scst_sess, - scst_unpack_lun((const uint8_t *)&lun, sizeof(lun))); + scst_unpack_lun(lun.scsi_lun, + sizeof(lun))); TRACE_EXIT_RES(res); return res; @@ -1385,6 +1393,56 @@ static int scst_local_targ_release(struct scst_tgt *tgt) return 0; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void scst_remove_work_fn(void *ctx) +#else +static void scst_remove_work_fn(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct scst_local_sess *sess = ctx; +#else + struct scst_local_sess *sess = + container_of(work, struct scst_local_sess, remove_work); +#endif + + scst_local_remove_adapter(sess); +} + +static void scst_local_close_session_impl(struct scst_local_sess *sess, + bool async) +{ + bool unregistering; + + spin_lock(&sess->aen_lock); + unregistering = sess->unregistering; + sess->unregistering = 1; + spin_unlock(&sess->aen_lock); + + if (!unregistering) { + if (async) + schedule_work(&sess->remove_work); + else + scst_local_remove_adapter(sess); + } +} + +/* + * Perform removal from the context of another thread since the caller may + * already hold an SCST mutex, since scst_local_remove_adapter() triggers a + * call of device_unregister(), since device_unregister() invokes + * device_del(), since device_del() locks the same mutex that is held while + * invoking scst_add() from class_interface_register() and since scst_add() + * also may lock an SCST mutex. + */ +static int scst_local_close_session(struct scst_session *scst_sess) +{ + struct scst_local_sess *sess = scst_sess_get_tgt_priv(scst_sess); + + scst_local_close_session_impl(sess, true); + return 0; +} + static int scst_local_targ_xmit_response(struct scst_cmd *scst_cmd) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) @@ -1525,6 +1583,7 @@ static struct scst_tgt_template scst_local_targ_tmpl = { #endif .detect = scst_local_targ_detect, .release = scst_local_targ_release, + .close_session = scst_local_close_session, .pre_exec = scst_local_targ_pre_exec, .xmit_response = scst_local_targ_xmit_response, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) @@ -1619,7 +1678,7 @@ static int scst_local_driver_probe(struct device *dev) sess->shost = hpnt; hpnt->max_id = 0; /* Don't want more than one id */ - hpnt->max_lun = 0xFFFF; + hpnt->max_lun = -1ll; /* * Because of a change in the size of this field at 2.6.26 @@ -1783,8 +1842,10 @@ static int __scst_local_add_adapter(struct scst_local_tgt *tgt, */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)) INIT_WORK(&sess->aen_work, scst_aen_work_fn, sess); + INIT_WORK(&sess->remove_work, scst_remove_work_fn, sess); #else INIT_WORK(&sess->aen_work, scst_aen_work_fn); + INIT_WORK(&sess->remove_work, scst_remove_work_fn); #endif spin_lock_init(&sess->aen_lock); INIT_LIST_HEAD(&sess->aen_work_list); @@ -1927,11 +1988,7 @@ static void __scst_local_remove_target(struct scst_local_tgt *tgt) list_for_each_entry_safe(sess, ts, &tgt->sessions_list, sessions_list_entry) { - spin_lock(&sess->aen_lock); - sess->unregistering = 1; - spin_unlock(&sess->aen_lock); - - scst_local_remove_adapter(sess); + scst_local_close_session_impl(sess, false); } list_del(&tgt->tgts_list_entry); diff --git a/scstadmin/LICENSE b/scstadmin/LICENSE index 08ddefd04..2a3b0f804 100644 --- a/scstadmin/LICENSE +++ b/scstadmin/LICENSE @@ -2,7 +2,6 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -304,8 +303,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + with this program. Also add information on how to contact you by electronic and paper mail. diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t index 10f88b048..f1b6bcac7 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/03-targets.t @@ -8,6 +8,7 @@ BEGIN { } use Data::Dumper; +$Data::Dumper::Sortkeys = 1; use SCST::SCST; sub addTargets { diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/04-alua.t b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/04-alua.t index c2b39a7d1..72db337a2 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/04-alua.t +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/04-alua.t @@ -8,6 +8,7 @@ BEGIN { } use Data::Dumper; +$Data::Dumper::Sortkeys = 1; use SCST::SCST; sub setup { diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/05-dynattr.t b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/05-dynattr.t index 02ff569ca..d31c739bf 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/05-dynattr.t +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/05-dynattr.t @@ -8,6 +8,7 @@ BEGIN { } use Data::Dumper; +$Data::Dumper::Sortkeys = 1; use SCST::SCST; sub setup { diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf index 5e248d128..77c6f5389 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf @@ -45,7 +45,6 @@ DEVICE_GROUP dg01 { TARGET_GROUP tg01b { group_id 2 - preferred 0 state active TARGET tgt_b { diff --git a/scstadmin/scstadmin.sysfs/scstadmin b/scstadmin/scstadmin.sysfs/scstadmin index 8ee27556c..2d37936b7 100755 --- a/scstadmin/scstadmin.sysfs/scstadmin +++ b/scstadmin/scstadmin.sysfs/scstadmin @@ -1,6 +1,6 @@ #!/usr/bin/perl -$Version = 'SCST Configurator v3.0.0-pre2'; +$Version = 'SCST Configurator v3.1.0-pre1'; # Configures SCST # diff --git a/srpt/LICENSE b/srpt/LICENSE index 7dd67d52b..bfaa85cb7 100644 --- a/srpt/LICENSE +++ b/srpt/LICENSE @@ -40,6 +40,5 @@ is the GNU Public License: GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. diff --git a/srpt/Makefile b/srpt/Makefile index a97d930df..95bb21216 100644 --- a/srpt/Makefile +++ b/srpt/Makefile @@ -48,44 +48,50 @@ MODULE_SYMVERS:=$(shell if [ -e $(KDIR)/Module.symvers ]; then \ echo Module.symvers; else echo Modules.symvers; fi) # Name of the OFED kernel RPM. -OFED_KERNEL_IB_RPM:=$(shell for r in kernel-ib mlnx-ofa_kernel compat-rdma; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) +OFED_KERNEL_IB_RPM:=$(shell for r in mlnx-ofa_kernel compat-rdma kernel-ib; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) # Name of the OFED kernel development RPM. -OFED_KERNEL_IB_DEVEL_RPM:=$(shell for r in kernel-ib-devel mlnx-ofa_kernel-devel compat-rdma-devel; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) +OFED_KERNEL_IB_DEVEL_RPM:=$(shell for r in mlnx-ofa_kernel-devel compat-rdma-devel kernel-ib-devel; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done) -ifeq ($(OFED_KERNEL_IB_RPM),kernel-ib) +OFED_FLAVOR=$(shell /usr/bin/ofed_info 2>/dev/null | head -n1 | sed -n 's/^MLNX_OFED.*/MOFED/p;s/^OFED-.*/OFED/p') + +ifneq ($(OFED_KERNEL_IB_RPM),) +ifeq ($(OFED_KERNEL_IB_RPM),compat-rdma) +# OFED 3.x +OFED_KERNEL_DIR:=/usr/src/compat-rdma +OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/include +else OFED_KERNEL_DIR:=/usr/src/ofa_kernel -# Read OFED 1.x's config.mk, which contains the definition of the variable -# BACKPORT_INCLUDES. +ifeq ($(OFED_FLAVOR),MOFED) +# Mellanox OFED with or without kernel-ib RPM +OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/include +else +# OFED 1.5 include $(OFED_KERNEL_DIR)/config.mk OFED_CFLAGS:=$(BACKPORT_INCLUDES) -I$(OFED_KERNEL_DIR)/include endif -ifeq ($(OFED_KERNEL_IB_RPM),mlnx-ofa_kernel) -OFED_KERNEL_DIR:=/usr/src/ofa_kernel/default -OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/default/include endif -ifeq ($(OFED_KERNEL_IB_RPM),compat-rdma) -OFED_KERNEL_DIR:=/usr/src/compat-rdma -OFED_CFLAGS:=-I$(OFED_KERNEL_DIR)/include -endif -ifneq ($(OFED_KERNEL_IB_RPM),) +# Any OFED version OFED_MODULE_SYMVERS:=$(OFED_KERNEL_DIR)/Module.symvers endif -# Path of the OFED ib_srpt.ko kernel module. -OFED_SRPT_PATH:=/lib/modules/$(KVER)/updates/kernel/drivers/infiniband/ulp/srpt/ib_srpt.ko - -# Whether or not the OFED ib_srpt.ko kernel module has been installed. -OFED_SRPT_INSTALLED:=$(shell if [ -e $(OFED_SRPT_PATH) ]; then echo true; else echo false; fi) +HAVE_KCFLAGS = $(shell $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/kcflags KCFLAGS=-DKCFLAGS_MACRO=1 >/dev/null 2>&1 && echo true || echo false) +HAVE_PRE_CFLAGS = $(shell $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/pre_cflags PRE_CFLAGS=-DPRE_CFLAGS_MACRO=1 >/dev/null 2>&1 && echo true || echo false) +AUTOCONF_FLAGS = $(shell $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/conftest/gid_change PRE_CFLAGS="$(OFED_CFLAGS)" >/dev/null 2>&1 && echo -DHAVE_IB_EVENT_GID_CHANGE) all: src/$(MODULE_SYMVERS) $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src \ - PRE_CFLAGS="$(OFED_CFLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) modules + PRE_CFLAGS="$(OFED_CFLAGS) $(AUTOCONF_FLAGS)" \ + KCFLAGS="$(AUTOCONF_FLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) modules install: all src/ib_srpt.ko + @[ -z "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && \ + find /lib/modules/$(KVER) -name ib_srpt.ko -exec rm {} \; ; \ + true $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/src \ PRE_CFLAGS="$(OFED_CFLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) \ + $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install uninstall: @@ -93,6 +99,11 @@ uninstall: -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) + @if [ "$(HAVE_KCFLAGS)" = false -a "$(HAVE_PRE_CFLAGS)" = false -a \ + -n "$(AUTOCONF_FLAGS)" ]; then \ + echo "Error: the kernel build system has not yet been patched.";\ + false; \ + fi @if [ -n "$(OFED_KERNEL_IB_RPM)" ]; then \ if [ -z "$(OFED_KERNEL_IB_DEVEL_RPM)" ]; then \ echo "Error: the OFED package $(OFED_KERNEL_IB_RPM)-devel has" \ @@ -103,20 +114,12 @@ src/Module.symvers src/Modules.symvers: $(SCST_SYMVERS_DIR)/$(MODULE_SYMVERS) "must be removed first" \ " (/lib/modules/$(KVER)/kernel/drivers/infiniband)."; \ false; \ - elif $(OFED_SRPT_INSTALLED); then \ - echo "Error: OFED has been built with srpt=y in ofed.conf."; \ - echo "Rebuild OFED with srpt=n."; \ - false; \ - elif [ -e $(KDIR)/scripts/Makefile.lib ] \ - && ! grep -wq '^c_flags .*PRE_CFLAGS' \ - $(KDIR)/scripts/Makefile.lib \ - && ! grep -wq '^LINUXINCLUDE .*PRE_CFLAGS' \ - $(KDIR)/Makefile; then \ + elif [ "$(HAVE_PRE_CFLAGS)" = false ]; then \ echo "Error: the kernel build system has not yet been patched.";\ false; \ else \ - echo " Building against $(OFED_KERNEL_IB_RPM) InfiniBand" \ - "kernel headers."; \ + echo " Building against $(OFED_FLAVOR) $(OFED_KERNEL_IB_RPM)" \ + "InfiniBand kernel headers."; \ ( \ grep -v drivers/infiniband/ $<; \ cat $(OFED_MODULE_SYMVERS) \ diff --git a/srpt/README b/srpt/README index f5940962c..ae06ffd62 100644 --- a/srpt/README +++ b/srpt/README @@ -50,7 +50,8 @@ The ib_srpt kernel module supports the following parameters: Mode (1) is choosen if both one_target_per_port and use_node_guid_in_target_name are false. Mode (2) is choosen if one_target_per_port is false and use_node_guid_in_target_name is true. Mode - (3) is choosen if one_target_per_port is true. + (3) is choosen if one_target_per_port is true. This last mode is the + default mode. * rdma_cm_port (number) A 16-bit number that specifies the port number to be registered via the RDMA/CM. Must be specified to make communication over RoCE or iWARP diff --git a/srpt/conftest/gid_change/Makefile b/srpt/conftest/gid_change/Makefile new file mode 100644 index 000000000..e81c05753 --- /dev/null +++ b/srpt/conftest/gid_change/Makefile @@ -0,0 +1 @@ +obj-m += gid_change.o diff --git a/srpt/conftest/gid_change/gid_change.c b/srpt/conftest/gid_change/gid_change.c new file mode 100644 index 000000000..bb60773fb --- /dev/null +++ b/srpt/conftest/gid_change/gid_change.c @@ -0,0 +1,9 @@ +#include +#include + +static int modinit(void) +{ + return IB_EVENT_GID_CHANGE; +} + +module_init(modinit); diff --git a/srpt/conftest/kcflags/Makefile b/srpt/conftest/kcflags/Makefile new file mode 100644 index 000000000..59e12dda0 --- /dev/null +++ b/srpt/conftest/kcflags/Makefile @@ -0,0 +1 @@ +obj-m += kcflags.o diff --git a/srpt/conftest/kcflags/kcflags.c b/srpt/conftest/kcflags/kcflags.c new file mode 100644 index 000000000..fff6e202f --- /dev/null +++ b/srpt/conftest/kcflags/kcflags.c @@ -0,0 +1,8 @@ +#include + +static int modinit(void) +{ + return KCFLAGS_MACRO; +} + +module_init(modinit); diff --git a/srpt/conftest/pre_cflags/Makefile b/srpt/conftest/pre_cflags/Makefile new file mode 100644 index 000000000..3c8c550f2 --- /dev/null +++ b/srpt/conftest/pre_cflags/Makefile @@ -0,0 +1 @@ +obj-m += pre_cflags.o diff --git a/srpt/conftest/pre_cflags/pre_cflags.c b/srpt/conftest/pre_cflags/pre_cflags.c new file mode 100644 index 000000000..1602d7115 --- /dev/null +++ b/srpt/conftest/pre_cflags/pre_cflags.c @@ -0,0 +1,8 @@ +#include + +static int modinit(void) +{ + return PRE_CFLAGS_MACRO; +} + +module_init(modinit); diff --git a/srpt/patches/kernel-3.15-pre-cflags.patch b/srpt/patches/kernel-3.15-pre-cflags.patch new file mode 100644 index 000000000..3964ee179 --- /dev/null +++ b/srpt/patches/kernel-3.15-pre-cflags.patch @@ -0,0 +1,12 @@ +diff --git a/Makefile b/Makefile +index 540f7b2..078307f 100644 +--- a/Makefile ++++ b/Makefile +@@ -361,6 +361,7 @@ USERINCLUDE := \ + # Use LINUXINCLUDE when you must reference the include/ directory. + # Needed to be compatible with the O= option + LINUXINCLUDE := \ ++ $(PRE_CFLAGS) \ + -I$(srctree)/arch/$(hdr-arch)/include \ + -Iarch/$(hdr-arch)/include/generated \ + $(if $(KBUILD_SRC), -I$(srctree)/include) \ diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 62acead56..8b5998914 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -151,9 +151,9 @@ MODULE_PARM_DESC(use_node_guid_in_target_name, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \ || defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5 -static int one_target_per_port; +static int one_target_per_port = true; #else -static bool one_target_per_port; +static bool one_target_per_port = true; #endif module_param(one_target_per_port, bool, 0444); MODULE_PARM_DESC(one_target_per_port, @@ -1542,8 +1542,9 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch, int status, const u8 *sense_data, int sense_data_len) { + struct scst_cmd *cmd = &ioctx->scmnd; struct srp_rsp *srp_rsp; - int max_sense_len; + int resid, max_sense_len; /* * The lowest bit of all SAM-3 status codes is zero (see also @@ -1560,6 +1561,23 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch, srp_rsp->tag = tag; srp_rsp->status = status; + if (unlikely(scst_get_resid(cmd, &resid, NULL) && resid != 0)) { + if (scst_cmd_get_data_direction(cmd) & SCST_DATA_READ) { + if (resid > 0) + srp_rsp->flags |= SRP_RSP_FLAG_DIUNDER; + else if (resid < 0) + srp_rsp->flags |= SRP_RSP_FLAG_DIOVER; + srp_rsp->data_in_res_cnt = cpu_to_be32(abs(resid)); + } + if (scst_cmd_get_data_direction(cmd) & SCST_DATA_WRITE) { + if (resid > 0) + srp_rsp->flags |= SRP_RSP_FLAG_DOUNDER; + else if (resid < 0) + srp_rsp->flags |= SRP_RSP_FLAG_DOOVER; + srp_rsp->data_out_res_cnt = cpu_to_be32(abs(resid)); + } + } + if (!scst_sense_valid(sense_data)) sense_data_len = 0; else { @@ -1938,9 +1956,11 @@ static void srpt_process_send_completion(struct ib_cq *cq, } else if (opcode == SRPT_RDMA_READ_LAST || opcode == SRPT_RDMA_WRITE_LAST) { PRINT_INFO("RDMA t %d for idx %u failed with status %d." + "%s", opcode, index, wc->status, + wc->status == IB_WC_WR_FLUSH_ERR ? " If this has not been triggered by a cable" " pull, please check the involved IB HCA's" - " and cables.", opcode, index, wc->status); + " and cables." : ""); srpt_handle_rdma_err_comp(ch, ch->ioctx_ring[index], opcode, srpt_xmt_rsp_context); } else if (opcode == SRPT_RDMA_ZEROLENGTH_WRITE) { @@ -2057,6 +2077,16 @@ static int srpt_compl_thread(void *arg) ch = arg; BUG_ON(!ch); + while (ch->state < CH_LIVE) { + set_current_state(TASK_INTERRUPTIBLE); + if (srpt_process_completion(ch, poll_budget) >= poll_budget) + cond_resched(); + else + schedule(); + } + + srpt_process_wait_list(ch); + while (ch->state < CH_DISCONNECTED) { set_current_state(TASK_INTERRUPTIBLE); if (srpt_process_completion(ch, poll_budget) >= poll_budget) @@ -2247,9 +2277,7 @@ static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) struct srpt_nexus *nexus; struct srpt_rdma_ch *ch; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) lockdep_assert_held(&srpt_tgt->mutex); -#endif list_for_each_entry(nexus, &srpt_tgt->nexus_list, entry) { list_for_each_entry(ch, &nexus->ch_list, list) { diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 135091311..0e30e16ea 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -51,7 +51,7 @@ #if defined(RHEL_MAJOR) && RHEL_MAJOR -0 == 5 #define vlan_dev_vlan_id(dev) (panic("RHEL 5 misses vlan_dev_vlan_id()"),0) #endif -#if defined(RHEL_MAJOR) +#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 6 #define __ethtool_get_settings(dev, cmd) (panic("RHEL misses __ethtool_get_settings()"),0) #endif #include @@ -142,12 +142,7 @@ enum { }; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) && \ - !(defined(CONFIG_SUSE_KERNEL) && \ - LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76)) && \ - !(defined(RHEL_MAJOR) && \ - (RHEL_MAJOR -0 > 6 || \ - RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 5 || \ - RHEL_MAJOR -0 == 5 && RHEL_MINOR -0 >= 9)) + !defined(HAVE_IB_EVENT_GID_CHANGE) /* See also patch "IB/core: Add GID change event" (commit 761d90ed4). */ enum { IB_EVENT_GID_CHANGE = 18 }; #endif diff --git a/usr/fileio/README b/usr/fileio/README index 79e43ccc8..2411bf479 100644 --- a/usr/fileio/README +++ b/usr/fileio/README @@ -1,7 +1,7 @@ User space FILEIO handler ========================= -Version 3.0.0, XX XXXXX 2014 +Version 3.1.0, XX XXXXX 2014 ---------------------------- User space program fileio_tgt uses interface of SCST's scst_user dev diff --git a/usr/fileio/common.h b/usr/fileio/common.h index 4f23fc46d..957377795 100644 --- a/usr/fileio/common.h +++ b/usr/fileio/common.h @@ -25,7 +25,7 @@ /* 8 byte ASCII Vendor */ #define VENDOR "SCST_USR" /* 4 byte ASCII Product Revision Level - left aligned */ -#define FIO_REV " 300" +#define FIO_REV " 310" #define MAX_USN_LEN (20+1) /* For '\0' */ diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index 9d583c4e5..cfb5d21b6 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -66,7 +66,7 @@ unsigned long trace_flag = DEFAULT_LOG_FLAGS; #endif /* defined(DEBUG) || defined(TRACING) */ #define DEF_BLOCK_SHIFT 9 -#define VERSION_STR "3.0.0-pre2" +#define VERSION_STR "3.1.0-pre1" #define THREADS 7 #define MAX_VDEVS 10 diff --git a/www/downloads.html b/www/downloads.html index 93e59c599..6d1253267 100644 --- a/www/downloads.html +++ b/www/downloads.html @@ -39,6 +39,11 @@

                            The latest stable version of SCST core is 2.2.1. The latest updates for it you can find it in the SVN branch 2.2.x.

                            +

                            SCST 3.0 release candidate is available for download from the SCST SVN branch 3.0.x. You can download it using either + web-based SVN repository viewer or using anonymous access:

                            + +

                            svn checkout svn://svn.code.sf.net/p/scst/svn/branches/3.0.x scst-3.0

                            +

                            You can also download prebuilt SCST modules for Scientific Linux CERN 5 (RHEL5-based), Ubuntu, @@ -52,11 +57,11 @@ NOTE! Both those projects are very early in the development process, so not recommended for production use yet.

                            -

                            The latest development version of SCST core is 3.0. You can download it as well as target drivers and user space +

                            The latest development version of SCST is 3.1. You can download it as well as target drivers and user space utilities directly from the SCST SVN. You can access it using either - web-based SVN repository viewer or using anonymous access:

                            + web-based SVN repository viewer or using anonymous access:

                            -

                            svn checkout svn://svn.code.sf.net/p/scst/svn/trunk scst-svn

                            +

                            svn checkout svn://svn.code.sf.net/p/scst/svn/trunk scst-trunk

                            Also you can find in the SCST SVN the latest updates for the stable branches. More information about accessing SVN repository may be found here. Or, alternatively, you can download it as a GNU tarball from diff --git a/www/target_qla2x00t.html b/www/target_qla2x00t.html index d804fdbf1..b4dd81ba8 100644 --- a/www/target_qla2x00t.html +++ b/www/target_qla2x00t.html @@ -63,6 +63,8 @@

                            The latest stable version is 2.2.0. Requires Linux kernel version 2.6.26.x or higher and SCST version 2.2.0 or higher.

                            +

                            Driver for the latest QLogic 16Gb/10G FC/FCoE adapters you can find in git.qlogic.com/scst.git

                            +

                            Gentoo HOWTO

                            HOWTO For iSCSI-SCST

                            Gentoo HOWTO For iSCSI-SCST

                            -

                            Alpine Linux HOWTO

                            HOWTO For QLogic Target Driver

                            SCST SGV Cache Description

                            Articles

                            -

                            Accelerating VDI Using SCST and SSDs - by Marc Smith

                            +

                            By Marc Smith:

                            +

                            Accelerating VDI Using SCST and SSDs

                            +

                            Building & Using a Highly Available ESOS Disk Array

                            +

                            Open Storage: Dual-Controller OSS Disk Array

                            SCST 0.9.6 graphs

                            init_scst

                            scst_cmd_thread

                            diff --git a/www/target_qla2x00t.html b/www/target_qla2x00t.html index b4dd81ba8..524ee5e87 100644 --- a/www/target_qla2x00t.html +++ b/www/target_qla2x00t.html @@ -58,17 +58,20 @@

                            Target driver qla2x00t for QLogic FC adapters

                            SCST QLogic - This is target driver for QLogic qla2xxx (22xx/23xx/24xx/25xx) Fibre Channel adapters. It is stable and well tested.

                            + This is target driver for QLogic qla2xxx (22xx++) Fibre Channel adapters.

                            -

                            The latest stable version is 2.2.0. Requires Linux kernel version 2.6.26.x or higher and - SCST version 2.2.0 or higher.

                            +

                            The latest stable version is 3.0.0. Requires Linux kernel version 2.6.26.x or higher and + SCST version 3.0.0 or higher.

                            -

                            Driver for the latest QLogic 16Gb/10G FC/FCoE adapters you can find in git.qlogic.com/scst.git

                            +

                            Driver in qla2x00t subdirectory is the old one, forked from qla2xxx from kernel 2.6.26. It is not maintained anymore.

                            + +

                            You can find the latest version of this driver in + git://git.qlogic.com/scst-qla2xxx.git. It is now maintained by QLogic, hence + located in the QLogic's git. This driver also supports FCoE. See SVN root README for instructions how to integrate it + into the SCST build tree.

                             
                            From 9246693e8703961330d5aab11db9133bbecd28ce Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 31 Aug 2014 11:35:24 +0000 Subject: [PATCH 059/128] Allow SCST to load on kernels that have LIO target with iSER compiled Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5741 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/include/iscsit_transport.h | 4 ++-- iscsi-scst/kernel/iscsi.c | 4 ++-- iscsi-scst/kernel/iscsit_transport.c | 8 ++++---- iscsi-scst/kernel/isert-scst/isert.c | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/iscsi-scst/include/iscsit_transport.h b/iscsi-scst/include/iscsit_transport.h index 6cbcedbcc..64eade892 100644 --- a/iscsi-scst/include/iscsit_transport.h +++ b/iscsi-scst/include/iscsit_transport.h @@ -63,8 +63,8 @@ struct iscsit_transport { struct list_head transport_list_entry; } ____cacheline_aligned; -extern int iscsit_register_transport(struct iscsit_transport *t); -extern void iscsit_unregister_transport(struct iscsit_transport *t); +extern int iscsit_reg_transport(struct iscsit_transport *t); +extern void iscsit_unreg_transport(struct iscsit_transport *t); extern struct iscsit_transport *iscsit_get_transport(enum iscsit_transport_type type); #endif /* __ISCSI_TRANSPORT_H__ */ diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index dbc79f752..60c50f9c9 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -4249,7 +4249,7 @@ static int __init iscsi_init(void) PRINT_INFO("iSCSI SCST Target - version %s", ISCSI_VERSION_STRING); - err = iscsit_register_transport(&iscsi_tcp_transport); + err = iscsit_reg_transport(&iscsi_tcp_transport); if (err) goto out; @@ -4403,7 +4403,7 @@ static void __exit iscsi_exit(void) scst_unregister_target_template(&iscsi_template); - iscsit_unregister_transport(&iscsi_tcp_transport); + iscsit_unreg_transport(&iscsi_tcp_transport); #if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) net_set_get_put_page_callbacks(NULL, NULL); diff --git a/iscsi-scst/kernel/iscsit_transport.c b/iscsi-scst/kernel/iscsit_transport.c index d913e3889..5e3c44ca2 100644 --- a/iscsi-scst/kernel/iscsit_transport.c +++ b/iscsi-scst/kernel/iscsit_transport.c @@ -29,7 +29,7 @@ struct iscsit_transport *iscsit_get_transport(enum iscsit_transport_type type) return t; } -int iscsit_register_transport(struct iscsit_transport *t) +int iscsit_reg_transport(struct iscsit_transport *t) { struct iscsit_transport *tmp; int ret = 0; @@ -50,9 +50,9 @@ int iscsit_register_transport(struct iscsit_transport *t) return ret; } -EXPORT_SYMBOL(iscsit_register_transport); +EXPORT_SYMBOL(iscsit_reg_transport); -void iscsit_unregister_transport(struct iscsit_transport *t) +void iscsit_unreg_transport(struct iscsit_transport *t) { mutex_lock(&transport_mutex); list_del(&t->transport_list_entry); @@ -60,5 +60,5 @@ void iscsit_unregister_transport(struct iscsit_transport *t) PRINT_INFO("Unregistered iSCSI transport: %s\n", t->name); } -EXPORT_SYMBOL(iscsit_unregister_transport); +EXPORT_SYMBOL(iscsit_unreg_transport); diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c index ac6b003d0..a66bfae00 100644 --- a/iscsi-scst/kernel/isert-scst/isert.c +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -472,7 +472,7 @@ static struct iscsit_transport isert_transport = { static void isert_cleanup_module(void) { - iscsit_unregister_transport(&isert_transport); + iscsit_unreg_transport(&isert_transport); isert_cleanup_login_devs(); } @@ -480,7 +480,7 @@ static int __init isert_init_module(void) { int ret; - ret = iscsit_register_transport(&isert_transport); + ret = iscsit_reg_transport(&isert_transport); if (ret) return ret; From 262e8b4db0e995945841b7565b021ee1b744bd28 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 1 Sep 2014 12:34:33 +0000 Subject: [PATCH 060/128] isert: Remove an unused variable Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5743 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 6c30f28a8..a327e1400 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -532,13 +532,11 @@ static int isert_poll_cq(struct isert_cq *cq) static void isert_cq_comp_work_cb(struct work_struct *work) { struct isert_cq *cq_desc; - struct isert_device *isert_dev; int ret; TRACE_ENTRY(); cq_desc = container_of(work, struct isert_cq, cq_comp_work); - isert_dev = cq_desc->dev; ret = isert_poll_cq(cq_desc); if (unlikely(ret < 0)) { /* poll error */ pr_err("ib_poll_cq failed\n"); From 66b51069b3ec331d8ba8405329408b60fffc0777 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 4 Sep 2014 06:36:01 +0000 Subject: [PATCH 061/128] Merged revisions 5740,5744-5745,5751-5755,5758,5760,5762,5764 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5740 | vlnb | 2014-08-28 04:26:11 +0300 (Thu, 28 Aug 2014) | 11 lines scst_lib: Fix READ POSITION parsing For code 08h (EXTENDED FORM) minimal response length is 32, see table "READ POSITION data format, extended form". In SSC-[2,3] table "READ POSITION service action codes" requests minimum response lenght 28 bytes, but it is an apparent typo, because the actual data format is 32 bytes long. In SSC-4 it is fixed. Signed-off-by: Bart Van Assche ........ r5744 | bvassche | 2014-09-02 09:33:32 +0300 (Tue, 02 Sep 2014) | 4 lines scripts/rebuild-rhel-kernel-rpm: Fix for invocation from current directory Reported-by: Hiroyuki Sato ........ r5745 | bvassche | 2014-09-02 09:35:06 +0300 (Tue, 02 Sep 2014) | 1 line scripts/generate-patched-kernel: Fix for invocation from current directory ........ r5751 | bvassche | 2014-09-03 13:23:51 +0300 (Wed, 03 Sep 2014) | 4 lines scst: Build fix for hex_to_bin() for RHEL 6.1 and later Reported-by: Yan Burman ........ r5752 | bvassche | 2014-09-03 13:47:16 +0300 (Wed, 03 Sep 2014) | 1 line scst.h: Make vzalloc() available on RHEL 6.0 ........ r5753 | bvassche | 2014-09-03 13:50:52 +0300 (Wed, 03 Sep 2014) | 1 line scst: Refine r5751, the hex_to_bin() build fix for RHEL >= 6.1 ........ r5754 | bvassche | 2014-09-03 13:58:03 +0300 (Wed, 03 Sep 2014) | 1 line scst_vdisk: RHEL 6.0 build fix ........ r5755 | bvassche | 2014-09-03 14:00:55 +0300 (Wed, 03 Sep 2014) | 1 line scst_vdisk: Follow-up for r5754 ........ r5758 | bvassche | 2014-09-03 16:43:23 +0300 (Wed, 03 Sep 2014) | 1 line scst.h: RHEL 5.10 build fix ........ r5760 | bvassche | 2014-09-03 16:52:16 +0300 (Wed, 03 Sep 2014) | 1 line scst.h: RHEL 5.10 build fix ........ r5762 | bvassche | 2014-09-03 17:00:14 +0300 (Wed, 03 Sep 2014) | 1 line scst.h: Fix definition of __aligned() ........ r5764 | bvassche | 2014-09-03 17:24:52 +0300 (Wed, 03 Sep 2014) | 1 line scst_vdisk: Use parentheses around && inside || ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5767 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scripts/generate-patched-kernel | 1 + scripts/rebuild-rhel-kernel-rpm | 1 + scst/include/scst.h | 17 +++++++++++++---- scst/src/dev_handlers/scst_vdisk.c | 10 ++++++---- scst/src/scst_lib.c | 6 ++++-- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/scripts/generate-patched-kernel b/scripts/generate-patched-kernel index a0cbdbb96..2faba70fd 100755 --- a/scripts/generate-patched-kernel +++ b/scripts/generate-patched-kernel @@ -28,6 +28,7 @@ script_dir="$(dirname $0)" if [ "${script_dir#/}" = "${script_dir}" ]; then script_dir="$PWD/$script_dir" fi +scriptdir="${scriptdir%/.}" scst_dir="$(dirname "${script_dir}")" source "${script_dir}/kernel-functions" diff --git a/scripts/rebuild-rhel-kernel-rpm b/scripts/rebuild-rhel-kernel-rpm index 7452e3542..f03da6df9 100755 --- a/scripts/rebuild-rhel-kernel-rpm +++ b/scripts/rebuild-rhel-kernel-rpm @@ -40,6 +40,7 @@ scriptdir="$(dirname "$0")" if [ "${scriptdir:0:1}" != "/" ]; then scriptdir="$PWD/${scriptdir}" fi +scriptdir="${scriptdir%/.}" source "${scriptdir}/rhel-rpm-functions" scst_dir="$(dirname "$scriptdir")" downloaddir=$HOME/software/downloads diff --git a/scst/include/scst.h b/scst/include/scst.h index 7e853dd64..b47ce4b7e 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -81,9 +81,13 @@ typedef _Bool bool; #define false 0 #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) && !defined(RHEL_MAJOR) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) +#ifndef __packed #define __packed __attribute__((packed)) -#define __aligned __attribute__((aligned)) +#endif +#ifndef __aligned +#define __aligned(x) __attribute__((aligned(x))) +#endif #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) @@ -242,7 +246,9 @@ static inline unsigned int queue_max_hw_sectors(struct request_queue *q) #endif #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \ + (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 || \ + RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 1) extern int hex_to_bin(char ch); #endif @@ -4847,7 +4853,10 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data, void (*done)(void *data, char *sense, int result, int resid)); #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && !defined(RHEL_MAJOR) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && \ + (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 5 || \ + RHEL_MAJOR -0 == 5 && RHEL_MINOR -0 < 10 || \ + RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 1) /* * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit * e1ca7788dec6773b1a2bce51b7141948f2b8bccf). diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 71d42d22c..e1906b78e 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -5254,16 +5254,18 @@ static void vdisk_bio_set_failfast(struct bio *bio) static void vdisk_bio_set_hoq(struct bio *bio) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ - defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + (defined(RHEL_MAJOR) && \ + (RHEL_MAJOR -0 > 6 || RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 > 0)) bio->bi_rw |= REQ_SYNC; #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) bio->bi_rw |= 1 << BIO_RW_SYNCIO; #else bio->bi_rw |= 1 << BIO_RW_SYNC; #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ - defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + (defined(RHEL_MAJOR) && \ + (RHEL_MAJOR -0 > 6 || RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 > 0)) bio->bi_rw |= REQ_META; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) /* diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index ae416cb0a..f05ac814e 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -100,7 +100,9 @@ char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap) } #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \ + (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 || \ + RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 1) /* * See also "lib: introduce common method to convert hex digits" (commit * 903788892ea0fc7fcaf7e8e5fac9a77379fc215b). @@ -6838,7 +6840,7 @@ static int get_cdb_info_read_pos(struct scst_cmd *cmd, cmd->bufflen = 32; break; case 8: - cmd->bufflen = max(28, cmd->bufflen); + cmd->bufflen = max(32, cmd->bufflen); break; default: PRINT_ERROR("READ POSITION: Invalid service action %x", From c4fd1dc2edcb08af46087eeb3581b77a9cdd6f03 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 7 Sep 2014 13:39:12 +0000 Subject: [PATCH 062/128] isert: Make sure we wait for all flushes to finish We wait untill all flushes finish by posting a special send WR with zero sized sge, and wait for it to flush. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5780 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 2 ++ iscsi-scst/kernel/isert-scst/iser_rdma.c | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index 8c1883c39..97f947940 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -104,6 +104,7 @@ struct isert_cq { }; #define ISERT_CONNECTION_ABORTED 0 +#define ISERT_DRAIN_POSTED 1 struct isert_connection { struct iscsi_conn iscsi ____cacheline_aligned; @@ -159,6 +160,7 @@ struct isert_connection { unsigned long flags; struct work_struct close_work; + struct isert_wr drain_wr; struct kref kref; void *priv_data; /* for connection tracking */ diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index a327e1400..8ead9081e 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -119,9 +119,24 @@ int isert_post_send(struct isert_connection *isert_conn, void isert_conn_disconnect(struct isert_connection *isert_conn) { + struct ib_send_wr *bad_wr; int err = rdma_disconnect(isert_conn->cm_id); if (unlikely(err)) pr_err("Failed to rdma disconnect, err:%d\n", err); + + if (!test_and_set_bit(ISERT_DRAIN_POSTED, &isert_conn->flags)) { + isert_wr_set_fields(&isert_conn->drain_wr, isert_conn, NULL); + isert_conn->drain_wr.wr_op = ISER_WR_SEND; + isert_conn->drain_wr.send_wr.wr_id = _ptr_to_u64(&isert_conn->drain_wr); + isert_conn->drain_wr.send_wr.opcode = IB_WR_SEND; + err = ib_post_send(isert_conn->qp, &isert_conn->drain_wr.send_wr, &bad_wr); + if (unlikely(err)) { + pr_err("Failed to post drain wr, err:%d\n", err); + /* We need to decrement iser_conn->kref in order to be able to cleanup + * the connection */ + isert_conn_free(isert_conn); + } + } } static int isert_pdu_handle_hello_req(struct isert_cmnd *pdu) @@ -485,7 +500,10 @@ static void isert_handle_wc_error(struct ib_wc *wc) switch (wr->wr_op) { case ISER_WR_SEND: - isert_pdu_err(&isert_pdu->iscsi); + if (unlikely(wr->send_wr.num_sge == 0)) /* Drain WR */ + isert_conn_free(isert_conn); + else + isert_pdu_err(&isert_pdu->iscsi); break; case ISER_WR_RDMA_READ: isert_pdu_err(&isert_pdu->iscsi); @@ -1016,6 +1034,7 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id, } kref_init(&isert_conn->kref); + kref_get(&isert_conn->kref); TRACE_EXIT(); return isert_conn; From 00ad4d0209dcc7858e8c4faebc36513a4a1eaeb8 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 7 Sep 2014 13:39:18 +0000 Subject: [PATCH 063/128] isert: Cleanup iscsi connection as soon as possible to allow lower session reinstate times Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5781 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 1 + iscsi-scst/kernel/isert-scst/iser_rdma.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index 97f947940..e0ba95d6c 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -105,6 +105,7 @@ struct isert_cq { #define ISERT_CONNECTION_ABORTED 0 #define ISERT_DRAIN_POSTED 1 +#define ISERT_DRAIN_FAILED 2 struct isert_connection { struct iscsi_conn iscsi ____cacheline_aligned; diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 8ead9081e..cfd9b3648 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -134,6 +134,7 @@ void isert_conn_disconnect(struct isert_connection *isert_conn) pr_err("Failed to post drain wr, err:%d\n", err); /* We need to decrement iser_conn->kref in order to be able to cleanup * the connection */ + set_bit(ISERT_DRAIN_FAILED, &isert_conn->flags); isert_conn_free(isert_conn); } } @@ -500,10 +501,14 @@ static void isert_handle_wc_error(struct ib_wc *wc) switch (wr->wr_op) { case ISER_WR_SEND: - if (unlikely(wr->send_wr.num_sge == 0)) /* Drain WR */ + if (unlikely(wr->send_wr.num_sge == 0)) { /* Drain WR */ + /* notify upper layer */ + if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) + isert_connection_closed(&isert_conn->iscsi); isert_conn_free(isert_conn); - else + } else { isert_pdu_err(&isert_pdu->iscsi); + } break; case ISER_WR_RDMA_READ: isert_pdu_err(&isert_pdu->iscsi); @@ -1111,7 +1116,7 @@ static void isert_conn_closed_do_work(struct work_struct *work) #endif /* notify upper layer */ - if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) + if (test_bit(ISERT_DRAIN_FAILED, &isert_conn->flags)) isert_connection_closed(&isert_conn->iscsi); isert_conn_free(isert_conn); From 3c47e991579d7c486bd621bb1427b94abd0f4939 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 7 Sep 2014 13:51:58 +0000 Subject: [PATCH 064/128] Merged revisions 5769,5779 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5769 | bvassche | 2014-09-04 15:56:48 +0300 (Thu, 04 Sep 2014) | 1 line ib_srpt: Bump driver version from 3.0.0-pre to 3.1.0-pre ........ r5779 | bvassche | 2014-09-06 09:30:46 +0300 (Sat, 06 Sep 2014) | 1 line nightly build: Update kernel versions ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5783 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- nightly/conf/nightly.conf | 6 +++--- srpt/src/ib_srpt.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index 6e99671ba..9bb4b1048 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,13 +3,13 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.16.1 \ +3.16.2 \ 3.15.10-nc \ -3.14.17-nc \ +3.14.18-nc \ 3.13.11-nc \ 3.12.21-nc \ 3.11.10-nc \ -3.10.53-nc \ +3.10.54-nc \ 3.9.11-nc \ 3.8.13-nc \ 3.7.10-nc \ diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index d55f1c7ab..4ec301ecc 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -69,7 +69,7 @@ /* Name of this kernel module. */ #define DRV_NAME "ib_srpt" -#define DRV_VERSION "3.0.0-pre" +#define DRV_VERSION "3.1.0-pre" #define DRV_RELDATE "(not yet released)" #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) /* Flags to be used in SCST debug tracing statements. */ From 760b4a55b72c3edd5b6124e5727b6d7a8171e7f7 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 10 Sep 2014 08:52:44 +0000 Subject: [PATCH 065/128] isert: Fix deadlock that can be caused by calling flush_workqueue() from within that same workqueue We no longer need to do flush of the workqueue, since we cleanup when all flushes are done now Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5787 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index cfd9b3648..9de2231cb 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1073,8 +1073,6 @@ static void isert_kref_free(struct kref *kref) pr_info("isert_conn_free conn:%p\n", isert_conn); - flush_workqueue(isert_conn->cq_desc->cq_workqueue); - isert_free_conn_resources(isert_conn); isert_conn_qp_destroy(isert_conn); From 914b9d4f4f7d76fcac7f423afee5d8ebb1bef717 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 10 Sep 2014 08:52:54 +0000 Subject: [PATCH 066/128] isert: Remove a leftover comment from the old days Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5788 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 9de2231cb..710ab8a6b 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -1059,8 +1059,6 @@ fail_get: return ERR_PTR(err); } -/* start closing process; - * only when all buffers released, can free */ static void isert_kref_free(struct kref *kref) { struct isert_connection *isert_conn = container_of(kref, From c682d02ebf89177ec22375c833f06d07c485e2cf Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 10 Sep 2014 08:53:00 +0000 Subject: [PATCH 067/128] isert: Fix possible deadlock when calling cancel_work_sync() on the workqueue we are running from Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5789 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 1 + iscsi-scst/kernel/isert-scst/iser_rdma.c | 28 +++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index e0ba95d6c..d4cde7d98 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -161,6 +161,7 @@ struct isert_connection { unsigned long flags; struct work_struct close_work; + struct work_struct drain_work; struct isert_wr drain_wr; struct kref kref; diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 710ab8a6b..4e402520a 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -486,6 +486,32 @@ static const char *wr_status_str(enum ib_wc_status status) } } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void isert_conn_drained_do_work(void *ctx) +#else +static void isert_conn_drained_do_work(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct isert_connection *isert_conn = ctx; +#else + struct isert_connection *isert_conn = + container_of(work, struct isert_connection, drain_work); +#endif + + isert_conn_free(isert_conn); +} + +static void isert_sched_conn_drained(struct isert_connection *isert_conn) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&isert_conn->drain_work, isert_conn_drained_do_work, isert_conn); +#else + INIT_WORK(&isert_conn->drain_work, isert_conn_drained_do_work); +#endif + isert_conn_queue_work(&isert_conn->drain_work); +} + static void isert_handle_wc_error(struct ib_wc *wc) { struct isert_wr *wr = _u64_to_ptr(wc->wr_id); @@ -505,7 +531,7 @@ static void isert_handle_wc_error(struct ib_wc *wc) /* notify upper layer */ if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) isert_connection_closed(&isert_conn->iscsi); - isert_conn_free(isert_conn); + isert_sched_conn_drained(isert_conn); } else { isert_pdu_err(&isert_pdu->iscsi); } From d9872582b45ae70972857ee8287bf1afdaebd166 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 10 Sep 2014 08:53:05 +0000 Subject: [PATCH 068/128] isert: Rename static version of isert_conn_free() to make its name unique We had two functions called isert_conn_free() a static one and a global one. Rename the static one so that it will be less confusing while reading the code. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5790 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c index a66bfae00..54020794b 100644 --- a/iscsi-scst/kernel/isert-scst/isert.c +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -269,7 +269,7 @@ static int isert_conn_activate(struct iscsi_conn *conn) return 0; } -static void isert_conn_free(struct iscsi_conn *conn) +static void isert_free_conn(struct iscsi_conn *conn) { isert_free_connection(conn); } @@ -454,7 +454,7 @@ static struct iscsit_transport isert_transport = { .transport_type = ISCSI_RDMA, .iscsit_conn_alloc = isert_conn_alloc, .iscsit_conn_activate = isert_conn_activate, - .iscsit_conn_free = isert_conn_free, + .iscsit_conn_free = isert_free_conn, .iscsit_alloc_cmd = isert_cmnd_alloc, .iscsit_free_cmd = isert_cmnd_free, .iscsit_preprocessing_done = isert_preprocessing_done, From 08c58c92ea8ed40bdce13c976c75186ecda2258b Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 10 Sep 2014 08:53:11 +0000 Subject: [PATCH 069/128] isert: Improve IO processing latency while closing connections Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5791 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 4e402520a..4867a2543 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -499,6 +499,10 @@ static void isert_conn_drained_do_work(struct work_struct *work) container_of(work, struct isert_connection, drain_work); #endif + /* notify upper layer */ + if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) + isert_connection_closed(&isert_conn->iscsi); + isert_conn_free(isert_conn); } @@ -528,9 +532,6 @@ static void isert_handle_wc_error(struct ib_wc *wc) switch (wr->wr_op) { case ISER_WR_SEND: if (unlikely(wr->send_wr.num_sge == 0)) { /* Drain WR */ - /* notify upper layer */ - if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags)) - isert_connection_closed(&isert_conn->iscsi); isert_sched_conn_drained(isert_conn); } else { isert_pdu_err(&isert_pdu->iscsi); From 7bc8357709b91a8233f213832f11635427d0dc97 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Thu, 11 Sep 2014 10:06:03 +0000 Subject: [PATCH 070/128] isert: Fix checkpatch whitespace errors Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5797 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_rdma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 4867a2543..1fb2b08ba 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -128,7 +128,7 @@ void isert_conn_disconnect(struct isert_connection *isert_conn) isert_wr_set_fields(&isert_conn->drain_wr, isert_conn, NULL); isert_conn->drain_wr.wr_op = ISER_WR_SEND; isert_conn->drain_wr.send_wr.wr_id = _ptr_to_u64(&isert_conn->drain_wr); - isert_conn->drain_wr.send_wr.opcode = IB_WR_SEND; + isert_conn->drain_wr.send_wr.opcode = IB_WR_SEND; err = ib_post_send(isert_conn->qp, &isert_conn->drain_wr.send_wr, &bad_wr); if (unlikely(err)) { pr_err("Failed to post drain wr, err:%d\n", err); @@ -1609,4 +1609,3 @@ struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len) } return portal; } - From f704a4afeed366010ec9b5151f30905dc01dfc42 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 29 Sep 2014 08:31:12 +0000 Subject: [PATCH 071/128] Merged revisions 5785-5786,5793-5796,5798,5801-5802,5804-5806,5808,5810-5811,5814,5816-5817 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5785 | bvassche | 2014-09-09 14:09:20 +0300 (Tue, 09 Sep 2014) | 9 lines scst_local: Change max_lun into SCST_MAX_LUN (16383) Today SCST does not support LUN numbers >= 16384. Additionally, there is a bug in older Linux initiator systems that prevents proper handling of LUN numbers >= 2**32. See also Hannes Reinecke, scsi_scan: Fixup scsilun_to_int(), June 25, 2014 (commit ID d9e5d6183715e691b37afd3785c311d05cd1338d). Hence set max_lun to 16383. ........ r5786 | bvassche | 2014-09-09 14:27:27 +0300 (Tue, 09 Sep 2014) | 6 lines scst_local: Set max_id to 1 The value 0 is not valid for the max_id member of struct Scsi_Host. Signed-off-by: Sebastian Herbszt ........ r5793 | bvassche | 2014-09-10 14:42:54 +0300 (Wed, 10 Sep 2014) | 1 line scstadmin: Sync saved configuration files ........ r5794 | bvassche | 2014-09-10 14:44:14 +0300 (Wed, 10 Sep 2014) | 2 lines scstadmin test 06-cont-on-err.t: Filter out scstadmin version number ........ r5795 | bvassche | 2014-09-10 15:18:09 +0300 (Wed, 10 Sep 2014) | 1 line ib_srpt: Add max_sge_delta kernel module parameter ........ r5796 | bvassche | 2014-09-10 15:20:30 +0300 (Wed, 10 Sep 2014) | 1 line ib_srpt: Update Subversion ignore lists ........ r5798 | bvassche | 2014-09-12 14:16:35 +0300 (Fri, 12 Sep 2014) | 1 line fcst/Makefile: Add release-archive target ........ r5801 | bvassche | 2014-09-12 14:20:16 +0300 (Fri, 12 Sep 2014) | 1 line fcst: Change version number from 0.3 into 3.1.0-pre ........ r5802 | vlnb | 2014-09-13 04:13:29 +0300 (Sat, 13 Sep 2014) | 3 lines Fix autofinding SCST headers in fileio_tgt ........ r5804 | vlnb | 2014-09-13 04:35:12 +0300 (Sat, 13 Sep 2014) | 3 lines Web updates ........ r5805 | vlnb | 2014-09-13 04:37:12 +0300 (Sat, 13 Sep 2014) | 3 lines Update root README to use symlink instead of bind mount for QLogic git driver integration ........ r5806 | bvassche | 2014-09-15 15:30:43 +0300 (Mon, 15 Sep 2014) | 1 line ib_srpt: Make "make -j install" work for n >= 2 if "make all" has not been run first ........ r5808 | bvassche | 2014-09-16 14:06:00 +0300 (Tue, 16 Sep 2014) | 6 lines scst/src/Makefile: Make "make install" without prior "make" work Avoid that MOD_VERS and MODS_VERS evaluate to an empty string. Reported-by: Yan Burman ........ r5810 | bvassche | 2014-09-17 13:54:25 +0300 (Wed, 17 Sep 2014) | 1 line scst_vdisk: Insert a blank line ........ r5811 | bvassche | 2014-09-17 13:56:40 +0300 (Wed, 17 Sep 2014) | 14 lines vdisk_blockio: Make large COMPARE AND WRITE requests work for stacked block devices Stacked block devices impose weird restrictions on S/G-lists. Hence make the COMPARE AND WRITE implementation independent of these restrictions. Additionally, reduce the MAXIMUM COMPARE AND WRITE LENGTH limit from 0xff (no limit) to 0xfe to reduce the maximum amount of memory allocated during a COMPARE AND WRITE. Also serialize COMPARE AND WRITE operations, fix the offset reported for miscompares and fix the start offset of the region that is synchronized if the FUA bit has been set. Reported-by: Vishal Tripathi ........ r5814 | bvassche | 2014-09-18 10:08:49 +0300 (Thu, 18 Sep 2014) | 1 line nightly build: Update kernel versions ........ r5816 | vlnb | 2014-09-20 09:31:43 +0300 (Sat, 20 Sep 2014) | 3 lines Web updates ........ r5817 | bvassche | 2014-09-28 21:54:04 +0200 (Sun, 28 Sep 2014) | 1 line scripts/rebuild-rhel-kernel-rpm: Enable put_page_callback patch for RHEL 7 ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5819 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- README | 6 +- fcst/Makefile | 7 +- fcst/fcst.h | 2 +- nightly/conf/nightly.conf | 8 +- scripts/rebuild-rhel-kernel-rpm | 4 +- scst/src/Makefile | 17 +-- scst/src/dev_handlers/scst_vdisk.c | 105 +++++++++++++++--- scst_local/scst_local.c | 4 +- .../scst-0.9.10/t/06-cont-on-err.t | 2 +- .../scst-0.9.10/t/after-restore.conf | 2 +- scstadmin/scstadmin.sysfs/scstadmin | 2 + srpt/Makefile | 2 +- srpt/README | 9 ++ srpt/src/ib_srpt.c | 18 +-- usr/fileio/Makefile | 5 +- www/downloads.html | 13 +-- www/handler_fileio_tgt.html | 2 +- www/scst_admin.html | 2 +- www/target_fcoe.html | 4 +- www/target_iscsi.html | 2 +- www/target_iser.html | 3 +- www/target_local.html | 2 +- www/target_qla2x00t.html | 6 +- www/target_srp.html | 2 +- 24 files changed, 149 insertions(+), 80 deletions(-) diff --git a/README b/README index e42fd5e92..52d3edfce 100644 --- a/README +++ b/README @@ -45,10 +45,8 @@ To integrate it into the SCST build tree you need: 2. Create in the SCST root, i.e. this directory, a subdirectory with name qla2x00t_git -3. Bind mount drivers/scsi/qla2xxx in the cloned git tree to the -qla2x00t_git subdirectory, like: - -# mount --bind /home/u/scst-qla2xxx/drivers/scsi/qla2xxx /home/u/trunk/qla2x00t_git +3. Symlink drivers/scsi/qla2xxx subdirectory in the cloned git tree to the +qla2x00t_git subdirectory Thats all. Now "make all" and other common and QLA specific root Makefile targets "magically" start working. The bind mount is necessary diff --git a/fcst/Makefile b/fcst/Makefile index 1200bcb77..d70f7626c 100644 --- a/fcst/Makefile +++ b/fcst/Makefile @@ -3,6 +3,7 @@ # Based on ../mvsas_tgt/Makefile # # Copyright (C) 2006 - 2008 Jacky Feng +# Copyright (C) 2011 - 2014 Bart Van Assche # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -137,4 +138,8 @@ extraclean: clean -$(MAKE) clean $(call set_var,build_mode,BUILDMODE,PERF) -.PHONY: all tgt install uninstall clean extraclean +release-archive: + ../scripts/generate-release-archive fcst "$$(sed -n 's/^#define[[:blank:]]FT_VERSION[[:blank:]]*\"\([^\"]*\)\".*/\1/p' fcst.h)" + +.PHONY: all tgt install uninstall clean extraclean 2debug 2release 2perf \ + release-archive diff --git a/fcst/fcst.h b/fcst/fcst.h index 50f25b4a5..6067b84bd 100644 --- a/fcst/fcst.h +++ b/fcst/fcst.h @@ -26,7 +26,7 @@ #include "scst.h" #endif -#define FT_VERSION "0.3" +#define FT_VERSION "3.1.0-pre" #define FT_MODULE "fcst" #define FT_MAX_HW_PENDING_TIME 20 /* max I/O time in seconds */ diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index 9bb4b1048..de74deda5 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,13 +3,13 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.16.2 \ +3.16.3 \ 3.15.10-nc \ -3.14.18-nc \ +3.14.19-nc \ 3.13.11-nc \ -3.12.21-nc \ +3.12.28-nc \ 3.11.10-nc \ -3.10.54-nc \ +3.10.55-nc \ 3.9.11-nc \ 3.8.13-nc \ 3.7.10-nc \ diff --git a/scripts/rebuild-rhel-kernel-rpm b/scripts/rebuild-rhel-kernel-rpm index f03da6df9..e4a96c1d7 100755 --- a/scripts/rebuild-rhel-kernel-rpm +++ b/scripts/rebuild-rhel-kernel-rpm @@ -383,7 +383,7 @@ patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $? Source2001: cpupower.config +Patch200: scst_exec_req_fifo.patch -+#Patch201: put_page_callback.patch ++Patch201: put_page_callback.patch + # empty final patch to facilitate testing of kernel patches Patch999999: linux-kernel-test.patch @@ -393,7 +393,7 @@ patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $? cp $RPM_SOURCE_DIR/kernel-%{version}-*.config . +ApplyPatch scst_exec_req_fifo.patch -+#ApplyPatch put_page_callback.patch ++ApplyPatch put_page_callback.patch + ApplyOptionalPatch linux-kernel-test.patch diff --git a/scst/src/Makefile b/scst/src/Makefile index 99dae97be..4d7088d57 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -95,9 +95,10 @@ all: $(SCST_INTF_VER_FILE) scst: $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_DEV=n -MODS_VERS := $(shell ls Modules.symvers 2>/dev/null) -# It's renamed in 2.6.18 -MOD_VERS := $(shell ls Module.symvers 2>/dev/null) +# The file Modules.symvers has been renamed in the 2.6.18 kernel to +# Module.symvers. Find out which name to use by looking in $(KDIR). +MODULE_SYMVERS:=$(shell if [ -e $(KDIR)/Module.symvers ]; then \ + echo Module.symvers; else echo Modules.symvers; fi) install: all @if [ -z "$(DESTDIR)" ] && \ @@ -116,14 +117,8 @@ install: all install -m 644 ../include/scst_user.h $(INSTALL_DIR_H) install -m 644 ../include/scst_const.h $(INSTALL_DIR_H) install -m 644 ../include/scst_itf_ver.h $(INSTALL_DIR_H) -ifneq ($(MODS_VERS),) - rm -f $(INSTALL_DIR_H)/Module.symvers - install -m 644 Modules.symvers $(INSTALL_DIR_H) -endif -ifneq ($(MOD_VERS),) - rm -f $(INSTALL_DIR_H)/Modules.symvers - install -m 644 Module.symvers $(INSTALL_DIR_H) -endif + rm -f $(INSTALL_DIR_H)/$(MODULE_SYMVERS) + install -m 644 $(MODULE_SYMVERS) $(INSTALL_DIR_H) -/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER) mkdir -p $(DESTDIR)/var/lib/scst/pr mkdir -p $(DESTDIR)/var/lib/scst/vdev_mode_pages diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index e1906b78e..64fa92490 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -119,6 +119,9 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define DEF_TST SCST_TST_1_SEP_TASK_SETS #define DEF_TMF_ONLY 0 +#define NO_CAW_LEN_LIM 0xff +#define DEF_CAW_LEN_LIM 0xfe + /* * Since we can't control backstorage device's reordering, we have to always * report unrestricted reordering. @@ -213,6 +216,10 @@ struct scst_vdisk_dev { /* Unmap INQUIRY parameters */ uint32_t unmap_opt_gran, unmap_align, unmap_max_lba_cnt; + /* Block limits INQUIRY parameters */ + uint8_t caw_len_lim; + struct mutex caw_mutex; + struct scst_device *dev; struct list_head vdev_list_entry; @@ -3464,7 +3471,7 @@ static int vdisk_block_limits(uint8_t *buf, struct scst_cmd *cmd, buf[1] = 0xB0; buf[3] = 0x3C; buf[4] = 1; /* WSNZ set */ - buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */ + buf[5] = virt_dev->caw_len_lim; /* Optimal transfer granuality is PAGE_SIZE */ put_unaligned_be16(max_t(int, PAGE_SIZE / dev->block_size, 1), &buf[6]); @@ -5593,6 +5600,17 @@ static void blockio_end_sync_io(struct bio *bio, int error) #endif } +/** + * blockio_rw_sync() - read or write up to @len bytes from a block I/O device + * + * Returns: + * - A negative value if an error occurred. + * - Zero if len == 0. + * - A positive value <= len if I/O succeeded. + * + * Note: + * Increments *@loff with the number of bytes transferred upon success. + */ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff, unsigned rw) { @@ -5643,15 +5661,26 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, bytes = min_t(size_t, PAGE_SIZE - off, buf + len - p); q = is_vmalloc ? vmalloc_to_page(p) : virt_to_page(p); rc = bio_add_page(bio, q, bytes, off); - if (WARN_ON_ONCE(rc < bytes)) - goto free; + if (rc < bytes) { + if (rc <= 0 && p == buf) { + goto free; + } else { + if (rc > 0) + p += rc; + break; + } + } } submit_bio(rw, bio); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0)) submitted = true; #endif wait_for_completion(&s.c); - ret = (unsigned long)s.error ? : len; + ret = (unsigned long)s.error; + if (likely(ret == 0)) { + ret = p - buf; + *loff += ret; + } free: bio_put(bio); @@ -5664,6 +5693,7 @@ out: return ret; } +/* Note: Updates *@loff if reading succeeded. */ static ssize_t fileio_read_sync(struct file *fd, void *buf, size_t len, loff_t *loff) { @@ -5688,6 +5718,7 @@ out: return ret; } +/* Note: Updates *@loff if writing succeeded. */ static ssize_t fileio_write_sync(struct file *fd, void *buf, size_t len, loff_t *loff) { @@ -5711,26 +5742,47 @@ out: return ret; } + +/* Note: Updates *@loff if reading succeeded except for NULLIO devices. */ static ssize_t vdev_read_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff) { - if (virt_dev->nullio) + ssize_t read, res; + + if (virt_dev->nullio) { return len; - else if (virt_dev->blockio) - return blockio_rw_sync(virt_dev, buf, len, loff, READ_SYNC); - else + } else if (virt_dev->blockio) { + for (read = 0; read < len; read += res) { + res = blockio_rw_sync(virt_dev, buf + read, len - read, + loff, READ_SYNC); + if (res < 0) + return res; + } + return read; + } else { return fileio_read_sync(virt_dev->fd, buf, len, loff); + } } +/* Note: Updates *@loff if reading succeeded except for NULLIO devices. */ static ssize_t vdev_write_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff) { - if (virt_dev->nullio) + ssize_t written, res; + + if (virt_dev->nullio) { return len; - else if (virt_dev->blockio) - return blockio_rw_sync(virt_dev, buf, len, loff, WRITE_SYNC); - else + } else if (virt_dev->blockio) { + for (written = 0; written < len; written += res) { + res = blockio_rw_sync(virt_dev, buf + written, + len - written, loff, WRITE_SYNC); + if (res < 0) + return res; + } + return written; + } else { return fileio_write_sync(virt_dev->fd, buf, len, loff); + } } static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) @@ -5855,6 +5907,16 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) if (data_len == 0) goto out; + if (virt_dev->caw_len_lim != NO_CAW_LEN_LIM && + (data_len > virt_dev->caw_len_lim << dev->block_shift)) { + PRINT_ERROR("COMPARE AND WRITE: data length %u exceeds" + " limit %u << %u = %u", data_len, + virt_dev->caw_len_lim, dev->block_shift, + virt_dev->caw_len_lim << dev->block_shift); + scst_set_invalid_field_in_cdb(cmd, 13, 0); + goto out; + } + length = scst_get_buf_full(cmd, &caw_buf); read_buf = vmalloc(data_len); if (length < 0 || !read_buf) { @@ -5872,6 +5934,8 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) goto out; } + mutex_lock(&virt_dev->caw_mutex); + loff = p->loff; read = vdev_read_sync(virt_dev, read_buf, data_len, &loff); if (read < data_len) { @@ -5882,7 +5946,7 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) else scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_read_error)); - goto out; + goto unlock; } if (memcmp(caw_buf, read_buf, data_len) != 0) { @@ -5899,9 +5963,8 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) * INFORMATION field. */ scst_set_cmd_error_and_inf(cmd, - SCST_LOAD_SENSE(scst_sense_miscompare_error), - p->loff + i); - goto out; + SCST_LOAD_SENSE(scst_sense_miscompare_error), i); + goto unlock; } loff = p->loff; @@ -5915,12 +5978,15 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) else scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_write_error)); - goto out; + goto unlock; } if (p->fua) - vdisk_fsync(loff, scst_cmd_get_data_len(cmd), cmd->dev, + vdisk_fsync(p->loff, scst_cmd_get_data_len(cmd), cmd->dev, cmd->cmd_gfp_mask, cmd, false); +unlock: + mutex_unlock(&virt_dev->caw_mutex); + out: if (read_buf) vfree(read_buf); @@ -6154,6 +6220,8 @@ static int vdev_create(struct scst_dev_type *devt, } spin_lock_init(&virt_dev->flags_lock); + mutex_init(&virt_dev->caw_mutex); + virt_dev->vdev_devt = devt; virt_dev->rd_only = DEF_RD_ONLY; @@ -6163,6 +6231,7 @@ static int vdev_create(struct scst_dev_type *devt, virt_dev->rotational = DEF_ROTATIONAL; virt_dev->thin_provisioned = DEF_THIN_PROVISIONED; virt_dev->tst = DEF_TST; + virt_dev->caw_len_lim = DEF_CAW_LEN_LIM; virt_dev->blk_shift = DEF_DISK_BLOCK_SHIFT; diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 31d18165c..771532619 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -1677,8 +1677,8 @@ static int scst_local_driver_probe(struct device *dev) sess->shost = hpnt; - hpnt->max_id = 0; /* Don't want more than one id */ - hpnt->max_lun = -1ll; + hpnt->max_id = 1; /* Don't want more than one id */ + hpnt->max_lun = SCST_MAX_LUN; /* * Because of a change in the size of this field at 2.6.26 diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/06-cont-on-err.t b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/06-cont-on-err.t index 2aa100c6a..883f020f3 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/06-cont-on-err.t +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/06-cont-on-err.t @@ -46,7 +46,7 @@ sub testRestoreConfig { system("$scstadmin -cont_on_err -no_lip -config $to_be_restored" . " >/dev/null"); system("$scstadmin -write_config $tmpfilename1 >/dev/null"); - system("awk 'BEGIN {t = 0 } /^TARGET_DRIVER.*{\$/ { if (\$0 != \"TARGET_DRIVER scst_local {\") t = 1 } /^}\$/ { if (t == 1) t = 2 } /^\$/ { if (t == 2) { t = 3 } } /^./ { if (t == 3) { t = 0 } } { if (t == 0) print }' <$tmpfilename1 >$tmpfilename2"); + system("awk 'BEGIN {t = 0 } /^# Automatically generated by SCST Configurator v/ { \$0 = \"# Automatically generated by SCST Configurator v...\" } /^TARGET_DRIVER.*{\$/ { if (\$0 != \"TARGET_DRIVER scst_local {\") t = 1 } /^}\$/ { if (t == 1) t = 2 } /^\$/ { if (t == 2) { t = 3 } } /^./ { if (t == 3) { t = 0 } } { if (t == 0) print }' <$tmpfilename1 >$tmpfilename2"); my $compare_result = system("diff -u $tmpfilename2 $expected"); ok($compare_result, 0); if ($compare_result == 0) { diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf index 77c6f5389..da8c75c37 100644 --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/t/after-restore.conf @@ -1,4 +1,4 @@ -# Automatically generated by SCST Configurator v3.0.0-pre2. +# Automatically generated by SCST Configurator v... HANDLER vdisk_fileio { diff --git a/scstadmin/scstadmin.sysfs/scstadmin b/scstadmin/scstadmin.sysfs/scstadmin index 2d37936b7..98f783fcf 100755 --- a/scstadmin/scstadmin.sysfs/scstadmin +++ b/scstadmin/scstadmin.sysfs/scstadmin @@ -1751,6 +1751,8 @@ sub writeConfiguration { } } + $io->flush; + $io->sync; close $io; return 0; diff --git a/srpt/Makefile b/srpt/Makefile index 20e5709ea..555525018 100644 --- a/srpt/Makefile +++ b/srpt/Makefile @@ -87,7 +87,7 @@ all: src/$(MODULE_SYMVERS) PRE_CFLAGS="$(OFED_CFLAGS) $(AUTOCONF_FLAGS)" \ KCFLAGS="$(AUTOCONF_FLAGS)" SCST_INC_DIR=$(SCST_INC_DIR) modules -install: all src/ib_srpt.ko +install: all @[ -z "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && \ find /lib/modules/$(KVER) -name ib_srpt.ko -exec rm {} \; ; \ true diff --git a/srpt/README b/srpt/README index ae06ffd62..0d805bdb3 100644 --- a/srpt/README +++ b/srpt/README @@ -38,6 +38,15 @@ Building and installing the SRP target driver is possible as follows: fi The ib_srpt kernel module supports the following parameters: + +* max_sge_delta (unsigned): Number to subtract from max_sge. Some but not + all HCA's allow to use up to max_sge S/G-list elements in RDMA + communication. The default value of this parameter is 3 and works with all + HCA's. If you know that the HCA's that are used by the ib_srpt driver allow + to use S/G-lists that are longer than max_sge - 3 then you can decrease this + parameter. Note: setting this parameter too low will cause SRP every login + to fail and will cause a message similar to the following to be logged on + the target system: "ib_srpt: RDMA t ... for idx ... failed with status 12". * one_target_per_port (boolean) and * use_node_guid_in_target_name (boolean) ib_srpt can operate in one of the following three modes: diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 4ec301ecc..437aa5e83 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -171,6 +171,10 @@ MODULE_PARM_DESC(srpt_service_guid, "Using this value for ioc_guid, id_ext, and cm_listen_id" " instead of using the node_guid of the first HCA."); +static unsigned max_sge_delta = 3; +module_param(max_sge_delta, uint, 0444); +MODULE_PARM_DESC(max_sge_delta, "Number to subtract from max_sge."); + /* * Note: changing any of the two constants below into SCST_CONTEXT_DIRECT is * dangerous because it might cause IB completions to be processed too late @@ -2151,19 +2155,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch) qp_init->sq_sig_type = IB_SIGNAL_REQ_WR; qp_init->qp_type = IB_QPT_RC; qp_init->cap.max_send_wr = srpt_sq_size; - /* - * A quote from the OFED 1.5.3.1 release notes - * (docs/release_notes/mthca_release_notes.txt), section "Known Issues": - * In mem-free devices, RC QPs can be created with a maximum of - * (max_sge - 1) entries only; UD QPs can be created with a maximum of - * (max_sge - 3) entries. - * A quote from the OFED 1.2.5 release notes - * (docs/mthca_release_notes.txt), section "Known Issues": - * In mem-free devices, RC QPs can be created with a maximum of - * (max_sge - 3) entries only. - */ - ch->max_sge = sdev->dev_attr.max_sge - 3; - WARN_ON(ch->max_sge < 1); + ch->max_sge = max_t(int, 1, sdev->dev_attr.max_sge - max_sge_delta); qp_init->cap.max_send_sge = ch->max_sge; if (ch->using_rdma_cm) { diff --git a/usr/fileio/Makefile b/usr/fileio/Makefile index 6b193e219..6c8f68991 100644 --- a/usr/fileio/Makefile +++ b/usr/fileio/Makefile @@ -26,8 +26,9 @@ OBJS_F = $(SRCS_F:.c=.o) #SRCS_C = #OBJS_C = $(SRCS_C:.c=.o) -SCST_INC_DIR := ../../scst/include -#SCST_INC_DIR := $(PREFIX)/include/scst +SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \ + then echo "$$PWD/../../scst/include"; \ + else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi) INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \ diff --git a/www/downloads.html b/www/downloads.html index 6d1253267..e7ce54f76 100644 --- a/www/downloads.html +++ b/www/downloads.html @@ -36,13 +36,8 @@

                            SCST Downloads

                            -

                            The latest stable version of SCST core is 2.2.1. The latest updates for it - you can find it in the SVN branch 2.2.x.

                            - -

                            SCST 3.0 release candidate is available for download from the SCST SVN branch 3.0.x. You can download it using either - web-based SVN repository viewer or using anonymous access:

                            - -

                            svn checkout svn://svn.code.sf.net/p/scst/svn/branches/3.0.x scst-3.0

                            +

                            The latest stable version of SCST is 3.0.0. The latest updates for it + you can find it in the SVN branch 3.0.x.

                            You can also download prebuilt SCST modules for Scientific Linux CERN 5 (RHEL5-based), @@ -87,9 +82,11 @@

                            srpt

                            +

                            fcoe

                            +

                            scst_local

                            -

                            fileio_tgt

                            +

                            fileio_tgt

                            doc-src

                            diff --git a/www/handler_fileio_tgt.html b/www/handler_fileio_tgt.html index f1e1c52c4..65e76ee9f 100644 --- a/www/handler_fileio_tgt.html +++ b/www/handler_fileio_tgt.html @@ -68,7 +68,7 @@ All the words about BLOCKIO mode from SCST's README file apply to O_DIRECT mode as well.

                            -

                            The latest stable version is 2.2.0. Requires SCST version 2.2.0 or higher.

                            +

                            The latest stable version is 3.0.0. Requires SCST version 3.0.0 or higher.

                            You can find the latest development version of this handler in the SCST SVN. See the download page how to setup access to it.

                            -

                            The latest stable version is 2.2.0.

                            +

                            The latest stable version is 3.0.0.

                            -

                            It is on the preliminary stage. You can download it from the SCST SVN repository. See the download - page how to setup access to it. +

                            The latest stable version is 3.0.0. You can download the latest development version from the SCST SVN repository. See the download + page how to setup access to it.





                            -

                            The latest stable version is 2.2.0. Requires Linux kernel version 2.6.18.x or higher and SCST version 2.2.0 or higher. +

                            The latest stable version is 3.0.0. Requires Linux kernel version 2.6.18.x or higher and SCST version 3.0.0 or higher. Tested mostly on i386 and x86_64, but should work on any other supported by Linux platform.

                            You can find the latest development version of this driver in the SCST SVN. See the download page how to setup access to it.

                            diff --git a/www/target_iser.html b/www/target_iser.html index cc9448569..8cb848f44 100644 --- a/www/target_iser.html +++ b/www/target_iser.html @@ -59,7 +59,8 @@

                            iSCSI Extensions for RDMA (iSER) driver for iSCSI-SCST

                            ISER extension for ISCSI-SCST has been developed by Yan Burman and Mellanox Technologies (thank you!).

                            -

                            It is currently in a beta stage. You can download it from the "iser" SCST SVN branch.

                            +

                            Current version is 3.0.0. You can find the latest development version in the SCST SVN. See the download page how to setup + access to it.




                            This driver was made by Richard Sharpe.

                            -

                            It is on the beta stage. The latest stabe version is 1.0.0. You can download +

                            It is on the beta stage. The latest stabe version is 3.0.0. You can download the latest development version from the SCST SVN repository. See the download page how to setup access to it.




                            The latest stable version is 3.0.0. Requires Linux kernel version 2.6.26.x or higher and SCST version 3.0.0 or higher.

                            -

                            Driver in qla2x00t subdirectory is the old one, forked from qla2xxx from kernel 2.6.26. It is not maintained anymore.

                            - -

                            You can find the latest version of this driver in +

                            Driver in qla2x00t subdirectory is the old one, forked from qla2xxx from kernel 2.6.26. It is not maintained anymore. + You can find the latest version of this driver in git://git.qlogic.com/scst-qla2xxx.git. It is now maintained by QLogic, hence located in the QLogic's git. This driver also supports FCoE. See SVN root README for instructions how to integrate it into the SCST build tree.

                             
                            diff --git a/www/target_srp.html b/www/target_srp.html index 53ae21f5e..afc613f51 100644 --- a/www/target_srp.html +++ b/www/target_srp.html @@ -63,7 +63,7 @@ It is maintained by Bart Van Assche.

                            This driver is mainline Linux kernel ready and going to be pushed to it together with other SCST patches.

                            -

                            The latest stable version is 2.2.0.

                            +

                            The latest stable version is 3.0.0.




                            iSCSI Extensions for RDMA (iSER) driver for iSCSI-SCST

                            ISER extension for ISCSI-SCST has been developed by Yan Burman and Mellanox Technologies (thank you!).

                            -

                            Current version is 3.0.0. You can find the latest development version in the SCST SVN. See the download page how to setup +

                            Current version is 3.0.0. You can find the latest development version in the SCST SVN in iser branch. See the download page how to setup access to it.

                            + +

                            3.0.x-iser branch in the SCST SVN is the stable post-3.0 release iSER + branch with the latest stable fixes in the iSER driver as well as other SCST components merged from the 3.0.0 branch.

                            +


                             
                            From 1238c3e88c71ab175a3310b9495ca1c44db64c03 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 17 Nov 2014 08:46:33 +0000 Subject: [PATCH 096/128] isert: Add missing copyright notice Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5876 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 35 +++++++++++++++++++ iscsi-scst/kernel/isert-scst/iser_datamover.h | 35 +++++++++++++++++++ iscsi-scst/kernel/isert-scst/iser_hdr.h | 35 +++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index c33992ef1..716237018 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -1,3 +1,38 @@ +/* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + #ifndef __ISER_H__ #define __ISER_H__ diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.h b/iscsi-scst/kernel/isert-scst/iser_datamover.h index f403ce509..8beda2cde 100644 --- a/iscsi-scst/kernel/isert-scst/iser_datamover.h +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.h @@ -1,3 +1,38 @@ +/* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + #ifndef __ISER_DATAMOVER_H__ #define __ISER_DATAMOVER_H__ diff --git a/iscsi-scst/kernel/isert-scst/iser_hdr.h b/iscsi-scst/kernel/isert-scst/iser_hdr.h index faef0e1f5..a49c13a6e 100644 --- a/iscsi-scst/kernel/isert-scst/iser_hdr.h +++ b/iscsi-scst/kernel/isert-scst/iser_hdr.h @@ -1,3 +1,38 @@ +/* +* This file is part of iser target kernel module. +* +* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. +* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + #ifndef __ISER_HDR_H__ #define __ISER_HDR_H__ From bd70a2eb27071598f0a409137d9d51284717a734 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 30 Nov 2014 08:17:02 +0000 Subject: [PATCH 097/128] isert: Fix race between disconnect handler and read by iscsi-scstd Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5896 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 28 ++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 2dcb78cf5..c170830ae 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -91,29 +91,23 @@ static void isert_del_timer(struct isert_conn_dev *dev) } } -static void release_dev(struct isert_conn_dev *dev) -{ - kref_init(&dev->kref); - - spin_lock(&isert_listen_dev.conn_lock); - dev->occupied = 0; - list_del_init(&dev->conn_list_entry); - dev->state = CS_INIT; - atomic_set(&dev->available, 1); - spin_unlock(&isert_listen_dev.conn_lock); -} - static void isert_kref_release_dev(struct kref *kref) { struct isert_conn_dev *dev = container_of(kref, struct isert_conn_dev, kref); - release_dev(dev); + kref_init(&dev->kref); + dev->occupied = 0; + list_del_init(&dev->conn_list_entry); + dev->state = CS_INIT; + atomic_set(&dev->available, 1); } static void isert_dev_release(struct isert_conn_dev *dev) { + spin_lock(&isert_listen_dev.conn_lock); kref_put(&dev->kref, isert_kref_release_dev); + spin_unlock(&isert_listen_dev.conn_lock); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) @@ -336,6 +330,7 @@ static ssize_t isert_listen_read(struct file *filp, char __user *buf, TRACE_ENTRY(); if (!have_new_connection(dev)) { +wait_for_connection: if (filp->f_flags & O_NONBLOCK) return -EAGAIN; res = wait_event_freezable(dev->waitqueue, @@ -344,9 +339,12 @@ static ssize_t isert_listen_read(struct file *filp, char __user *buf, goto out; } - sBUG_ON(list_empty(&dev->new_conn_list)); - spin_lock(&dev->conn_lock); + if (list_empty(&dev->new_conn_list)) { + /* could happen if we got disconnect */ + spin_unlock(&dev->conn_lock); + goto wait_for_connection; + } conn_dev = list_first_entry(&dev->new_conn_list, struct isert_conn_dev, conn_list_entry); list_move(&conn_dev->conn_list_entry, &dev->curr_conn_list); From a105745c4cbbcd0d438457c8105df46f09a70f96 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 30 Nov 2014 08:17:09 +0000 Subject: [PATCH 098/128] isert: Make sure we don't call dma_unmap on memory we already unmapped Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5897 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser_buf.c | 8 +++++--- iscsi-scst/kernel/isert-scst/iser_rdma.c | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser_buf.c b/iscsi-scst/kernel/isert-scst/iser_buf.c index 20a5f5bf6..39aaff733 100644 --- a/iscsi-scst/kernel/isert-scst/iser_buf.c +++ b/iscsi-scst/kernel/isert-scst/iser_buf.c @@ -294,9 +294,11 @@ void isert_wr_release(struct isert_wr *wr) struct isert_device *isert_dev = wr->isert_dev; struct ib_device *ib_dev; - ib_dev = isert_dev->ib_dev; - ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, - isert_buf->dma_dir); + if (isert_buf->sg_cnt) { + ib_dev = isert_dev->ib_dev; + ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, + isert_buf->dma_dir); + } isert_buf_release(isert_buf); } memset(wr, 0, sizeof(*wr)); diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index 344a69760..c8d2a9abb 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -359,6 +359,7 @@ static void isert_rdma_rd_completion_handler(struct isert_wr *wr) ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, isert_buf->dma_dir); + isert_buf->sg_cnt = 0; isert_data_out_ready(&wr->pdu->iscsi); } @@ -371,6 +372,7 @@ static void isert_rdma_wr_completion_handler(struct isert_wr *wr) ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, isert_buf->dma_dir); + isert_buf->sg_cnt = 0; isert_data_in_sent(&wr->pdu->iscsi); } @@ -543,9 +545,9 @@ static void isert_handle_wc_error(struct ib_wc *wc) break; case ISER_WR_RDMA_READ: if (isert_buf->sg_cnt != 0) { - isert_buf->sg_cnt = 0; ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, isert_buf->dma_dir); + isert_buf->sg_cnt = 0; } isert_pdu_err(&isert_pdu->iscsi); break; @@ -554,9 +556,9 @@ static void isert_handle_wc_error(struct ib_wc *wc) break; case ISER_WR_RDMA_WRITE: if (isert_buf->sg_cnt != 0) { - isert_buf->sg_cnt = 0; ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, isert_buf->dma_dir); + isert_buf->sg_cnt = 0; } /* RDMA-WR and SEND response of a READ task are sent together, so when receiving RDMA-WR error, From 083634586a4a5020440ef4c5cd1c238a1b7db9b8 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 30 Nov 2014 08:17:13 +0000 Subject: [PATCH 099/128] isert: Document isert_nr_devs parameter Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5898 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/README.iser | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/iscsi-scst/README.iser b/iscsi-scst/README.iser index ea5a57511..751213edc 100644 --- a/iscsi-scst/README.iser +++ b/iscsi-scst/README.iser @@ -25,9 +25,10 @@ Limitations: ------------- * Bidirectional commands are not supported * Block size over 512KB is not supported -* Maximum number of concurent login requests that can be handled is 127. +* Maximum number of concurent login requests that can be handled is 127 by default. Note that there may be more connections, but only up to 127 login requests - can be handled at the same time. + can be handled at the same time. If you wish to increase this, load isert_scst with + module parameter isert_nr_devs set to the number of login requests you need to handle. Troubleshooting: From 03548dbc243d5ea88a5c03ea1e6b705567d033e8 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 30 Nov 2014 08:17:18 +0000 Subject: [PATCH 100/128] isert: Do not fail with cards that support small number of WR per QP This fixes an issue with ConnectIB Reported-by: Eric Millbrandt Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5899 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/iser.h | 2 ++ iscsi-scst/kernel/isert-scst/iser_rdma.c | 26 +++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/iser.h b/iscsi-scst/kernel/isert-scst/iser.h index 716237018..c01e6af67 100644 --- a/iscsi-scst/kernel/isert-scst/iser.h +++ b/iscsi-scst/kernel/isert-scst/iser.h @@ -101,6 +101,8 @@ struct isert_wr { #define ISER_SQ_SIZE 128 #define ISER_MAX_WCE 2048 +#define ISER_MIN_SQ_SIZE 16 + struct isert_cmnd { struct iscsi_cmnd iscsi ____cacheline_aligned; diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c index c8d2a9abb..5571bddab 100644 --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c @@ -977,6 +977,7 @@ static int isert_conn_qp_create(struct isert_connection *isert_conn) struct ib_qp_init_attr qp_attr; int err; int cq_idx; + int max_wr = ISER_MAX_WCE; TRACE_ENTRY(); @@ -988,8 +989,6 @@ static int isert_conn_qp_create(struct isert_connection *isert_conn) qp_attr.qp_context = isert_conn; qp_attr.send_cq = isert_dev->cq_desc[cq_idx].cq; qp_attr.recv_cq = isert_dev->cq_desc[cq_idx].cq; - qp_attr.cap.max_send_wr = ISER_MAX_WCE; - qp_attr.cap.max_recv_wr = ISER_MAX_WCE; isert_conn->cq_desc = &isert_dev->cq_desc[cq_idx]; @@ -1013,11 +1012,24 @@ static int isert_conn_qp_create(struct isert_connection *isert_conn) qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; qp_attr.qp_type = IB_QPT_RC; - err = rdma_create_qp(cm_id, isert_dev->pd, &qp_attr); - if (unlikely(err)) { - pr_err("Failed to create qp, err:%d\n", err); - goto fail_create_qp; - } + do { + if (max_wr < ISER_MIN_SQ_SIZE) { + pr_err("Failed to create qp, not enough memory\n"); + goto fail_create_qp; + } + + qp_attr.cap.max_send_wr = max_wr; + qp_attr.cap.max_recv_wr = max_wr; + + err = rdma_create_qp(cm_id, isert_dev->pd, &qp_attr); + if (err && err != -ENOMEM) { + pr_err("Failed to create qp, err:%d\n", err); + goto fail_create_qp; + } + + max_wr /= 2; + } while (err == -ENOMEM); + isert_conn->qp = cm_id->qp; pr_info("iser created cm_id:%p qp:0x%X\n", cm_id, cm_id->qp->qp_num); From 5c1d9a4501d1e130a1ff29c2e40d7c37b810d805 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 30 Nov 2014 08:17:24 +0000 Subject: [PATCH 101/128] isert: Stop timer as soon as we so that in busy system we don't rely on userspace Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5900 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index c170830ae..cd06893e6 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -86,8 +86,8 @@ static struct isert_conn_dev *get_available_dev(struct isert_listener_dev *dev, static void isert_del_timer(struct isert_conn_dev *dev) { if (dev->timer_active) { - del_timer_sync(&dev->tmo_timer); dev->timer_active = 0; + del_timer_sync(&dev->tmo_timer); } } @@ -234,6 +234,7 @@ int isert_conn_alloc(struct iscsi_session *session, goto cleanup_conn; conn->rd_state = 1; + isert_del_timer(dev); isert_dev_release(dev); isert_set_priv(conn, NULL); From e6e1752b8f5cba751a1aeadbeb7736bee3f05b7f Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sun, 30 Nov 2014 08:17:29 +0000 Subject: [PATCH 102/128] isert: Do not crash kernel if userspace has a bug Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5901 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index cd06893e6..f697b4fd4 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -573,7 +573,9 @@ static ssize_t isert_read(struct file *filp, char __user *buf, size_t count, break; default: - sBUG(); + PRINT_ERROR("Invalid state in %s (%d)\n", __func__, + dev->state); + to_read = 0; } return to_read; @@ -615,7 +617,9 @@ static ssize_t isert_write(struct file *filp, const char __user *buf, break; default: - sBUG(); + PRINT_ERROR("Invalid state in %s (%d)\n", __func__, + dev->state); + to_write = 0; } return to_write; From bb7f5caef27d9f2ed2cc9b1185250a9a2103ef99 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 28 Jan 2015 12:12:32 +0000 Subject: [PATCH 103/128] Merged revisions 5875,5878-5895,5903-5905,5910,5912-5914,5928-5929,5931-5991 via svnmerge from svn+ssh://yanb123@svn.code.sf.net/p/scst/svn/trunk ........ r5875 | bvassche | 2014-11-16 19:58:07 +0200 (Sun, 16 Nov 2014) | 1 line nightly build: Update kernel versions ........ r5878 | bvassche | 2014-11-19 02:17:41 +0200 (Wed, 19 Nov 2014) | 1 line srpt/Makefile: Add double quotes around a path ........ r5879 | bvassche | 2014-11-19 02:20:20 +0200 (Wed, 19 Nov 2014) | 1 line scripts/generate-release-archive: Accept an optional list of file names ........ r5880 | bvassche | 2014-11-22 13:12:29 +0200 (Sat, 22 Nov 2014) | 1 line nightly build: Update kernel versions ........ r5881 | bvassche | 2014-11-24 19:59:14 +0200 (Mon, 24 Nov 2014) | 4 lines ib_srpt: Add support for HCA's that do not support SRQ Based on a patch provided by Parav Pandit ........ r5882 | vlnb | 2014-11-26 09:02:17 +0200 (Wed, 26 Nov 2014) | 3 lines Update for kernels 3.17.x ........ r5883 | bvassche | 2014-11-26 10:05:09 +0200 (Wed, 26 Nov 2014) | 1 line Add kernel 3.17 build infrastructure ........ r5884 | bvassche | 2014-11-26 10:07:08 +0200 (Wed, 26 Nov 2014) | 1 line nightly build: Add kernel 3.17 ........ r5885 | bvassche | 2014-11-26 10:16:44 +0200 (Wed, 26 Nov 2014) | 6 lines Fix kernel 3.17 checkpatch warnings about 'long long unsigned' Avoid that checkpatch reports the following warning: WARNING: type 'long long unsigned' should be specified in 'unsigned long long' order. ........ r5886 | bvassche | 2014-11-26 15:38:52 +0200 (Wed, 26 Nov 2014) | 1 line Build fixes for RHEL 6.6 kernel 2.6.32-504 ........ r5887 | bvassche | 2014-11-26 16:39:51 +0200 (Wed, 26 Nov 2014) | 1 line ib_srpt: Make the send queue full messages more informational ........ r5888 | bvassche | 2014-11-26 18:25:57 +0200 (Wed, 26 Nov 2014) | 1 line scripts/specialize-patch: Support blanks around numbers inside parentheses ........ r5889 | bvassche | 2014-11-26 21:42:10 +0200 (Wed, 26 Nov 2014) | 1 line scripts/specialize-patch: Reduce noise in nightly build output ........ r5890 | vlnb | 2014-11-27 06:36:33 +0200 (Thu, 27 Nov 2014) | 3 lines Cleanup ........ r5891 | bvassche | 2014-11-27 17:18:58 +0200 (Thu, 27 Nov 2014) | 1 line scst.h: Add uintptr_t ........ r5892 | bvassche | 2014-11-27 17:19:21 +0200 (Thu, 27 Nov 2014) | 1 line ib_srpt: Add support for immediate data ........ r5893 | bvassche | 2014-11-27 17:24:17 +0200 (Thu, 27 Nov 2014) | 1 line ib_srpt: Log reject reason ........ r5894 | bvassche | 2014-11-27 17:29:29 +0200 (Thu, 27 Nov 2014) | 1 line ib_srpt: Rework the max_sge computation changes from r5795 ........ r5895 | bvassche | 2014-11-28 11:16:37 +0200 (Fri, 28 Nov 2014) | 1 line scst: Add scripts/rebuild-rhel-kernel-rpm to the SCST release archive ........ r5903 | bvassche | 2014-12-03 13:50:06 +0200 (Wed, 03 Dec 2014) | 4 lines scripts/rebuild-rhel-kernel-rpm: Fix an error message Reported-by: Hiroyuki Sato ........ r5904 | bvassche | 2014-12-03 19:06:57 +0200 (Wed, 03 Dec 2014) | 1 line iscsi-scst/kernel/patches/rhel/put_page_callback-2.6.32-504.patch: Add ........ r5905 | bvassche | 2014-12-03 19:07:31 +0200 (Wed, 03 Dec 2014) | 1 line scripts/rebuild-rhel-kernel-rpm: Add support for RHEL 6.6 ........ r5910 | bvassche | 2014-12-04 13:50:58 +0200 (Thu, 04 Dec 2014) | 1 line scripts/generate-kernel-patch: Swap two filters ........ r5912 | bvassche | 2014-12-04 14:19:56 +0200 (Thu, 04 Dec 2014) | 4 lines /etc/init.d/scst: Exit with status code 0 upon 'start' if already running Reported-by: Dimitar Tanev ........ r5913 | vlnb | 2014-12-05 01:41:52 +0200 (Fri, 05 Dec 2014) | 3 lines FORMAT commands should be strictly serialized ........ r5914 | vlnb | 2014-12-05 01:43:51 +0200 (Fri, 05 Dec 2014) | 3 lines Oops, fix for the previous commit ........ r5928 | vlnb | 2014-12-06 07:02:27 +0200 (Sat, 06 Dec 2014) | 3 lines Web updates ........ r5929 | bvassche | 2014-12-09 14:33:16 +0200 (Tue, 09 Dec 2014) | 1 line rpm build: Add support for qla2x00t driver in QLogic git repository ........ r5931 | vlnb | 2014-12-11 06:27:17 +0200 (Thu, 11 Dec 2014) | 3 lines Docs update ........ r5932 | vlnb | 2014-12-11 06:34:36 +0200 (Thu, 11 Dec 2014) | 8 lines scst_vdisk: Increase virtual device name length This change makes integration with OpenStack easier since OpenStack GUIDs are 36 characters long: 32 hex characters and four dashes. Signed-off-by: Bart Van Assche ........ r5933 | vlnb | 2014-12-11 06:38:04 +0200 (Thu, 11 Dec 2014) | 11 lines vdisk_blockio: Report invalid scatterlists It is possible for a target driver to pass a scatterlist via scst_cmd_set_tgt_sg() that is valid for the vdisk_fileio handler but not for the vdisk_blockio handler. Complain loudly if an invalid scatterlist is passed to vdisk_blockio because such scatterlists cause silent data corruption with most Linux block drivers. Signed-off-by: Bart Van Assche ........ r5934 | bvassche | 2014-12-11 14:31:03 +0200 (Thu, 11 Dec 2014) | 1 line scst_vdisk: Follow-up for r5932 ........ r5935 | bvassche | 2014-12-11 14:37:02 +0200 (Thu, 11 Dec 2014) | 1 line ib_srpt: Log P_Key during login ........ r5936 | bvassche | 2014-12-12 11:29:42 +0200 (Fri, 12 Dec 2014) | 1 line scripts/generate-kernel-patch: Include scst_pg.sgml instead of sgv_cache.sgml ........ r5937 | bvassche | 2014-12-12 11:34:55 +0200 (Fri, 12 Dec 2014) | 1 line doc/scst_pg.sgml: Remove trailing whitespace ........ r5938 | bvassche | 2014-12-17 09:48:40 +0200 (Wed, 17 Dec 2014) | 1 line nightly build: Update kernel versions ........ r5939 | vlnb | 2014-12-19 05:50:58 +0200 (Fri, 19 Dec 2014) | 3 lines Fallback to the old qla driver if the git one not detected ........ r5940 | vlnb | 2014-12-19 05:55:14 +0200 (Fri, 19 Dec 2014) | 7 lines Replace in cases, where sporadic failures are possible, HARDWARE ERROR by INTERNAL TARGET FAILURE, which is retriable (some OS'es don't retry HARDWARE ERROR) Reported and suggested by Shahar Salzman ........ r5941 | vlnb | 2014-12-20 05:48:07 +0200 (Sat, 20 Dec 2014) | 7 lines scst_vdisk: Only accept NAA IDs allowed by SPC See also paragraph 7.8.6.6 NAA designator format in SPC-4. Signed-off-by: Bart Van Assche ........ r5942 | vlnb | 2014-12-20 05:49:23 +0200 (Sat, 20 Dec 2014) | 11 lines scst_vdisk: Remove superfluous llseek() calls vfs_read() and vfs_write() ignore the file offset set by llseek(). Hence remove the llseek() calls that occur just before vfs_read() and vfs_write(). See also the implementation in the Linux kernel of the pread64() and pwrite64() system calls for examples of code that uses vfs_read() and vfs_write(). Signed-off-by: Bart Van Assche ........ r5943 | bvassche | 2014-12-22 14:28:13 +0200 (Mon, 22 Dec 2014) | 1 line Source code spelling fix: Equivilant -> Equivalent ........ r5944 | bvassche | 2014-12-22 14:28:56 +0200 (Mon, 22 Dec 2014) | 1 line Source code spelling fix: accesss -> access ........ r5945 | bvassche | 2014-12-22 14:29:51 +0200 (Mon, 22 Dec 2014) | 1 line Source code spelling fix: addres -> address ........ r5946 | bvassche | 2014-12-22 14:31:08 +0200 (Mon, 22 Dec 2014) | 1 line Source code spelling fix: authentification -> authentication ........ r5947 | bvassche | 2014-12-22 14:32:30 +0200 (Mon, 22 Dec 2014) | 1 line Source code comment spelling fix: explicitely -> explicitly ........ r5948 | bvassche | 2014-12-22 14:33:06 +0200 (Mon, 22 Dec 2014) | 1 line Source code comment spelling fix: hander -> handler ........ r5949 | bvassche | 2014-12-22 14:33:37 +0200 (Mon, 22 Dec 2014) | 1 line Source code comment spelling fix: loosing -> losing ........ r5950 | bvassche | 2014-12-22 14:35:00 +0200 (Mon, 22 Dec 2014) | 1 line Spelling fix: occured -> occurred ........ r5951 | bvassche | 2014-12-22 14:35:51 +0200 (Mon, 22 Dec 2014) | 1 line Source code comment spelling fix: refering -> referring ........ r5952 | bvassche | 2014-12-22 14:36:47 +0200 (Mon, 22 Dec 2014) | 1 line Spelling fix: shrinked -> shrunk ........ r5953 | bvassche | 2014-12-22 15:08:34 +0200 (Mon, 22 Dec 2014) | 1 line Spelling fix: choosen -> chosen ........ r5954 | bvassche | 2014-12-22 15:09:20 +0200 (Mon, 22 Dec 2014) | 1 line Spelling fix: existant -> existent ........ r5955 | bvassche | 2014-12-22 15:10:41 +0200 (Mon, 22 Dec 2014) | 1 line Update for kernel 3.18 ........ r5956 | bvassche | 2014-12-22 15:15:55 +0200 (Mon, 22 Dec 2014) | 1 line Spelling fix: immediatelly -> immediately ........ r5957 | bvassche | 2014-12-24 16:28:36 +0200 (Wed, 24 Dec 2014) | 1 line nightly build: Add kernel 3.18 ........ r5958 | bvassche | 2014-12-29 14:14:52 +0200 (Mon, 29 Dec 2014) | 1 line scst_lib: Convert spaces into tabs (reported by checkpatch) ........ r5959 | bvassche | 2015-01-06 15:25:28 +0200 (Tue, 06 Jan 2015) | 1 line scst_calc_block_shift: Log block shift and sector size upon mismatch ........ r5960 | bvassche | 2015-01-07 11:20:06 +0200 (Wed, 07 Jan 2015) | 4 lines scst_local: Fix unique per session sas address Signed-off-by: Sebastian Herbszt ........ r5961 | bvassche | 2015-01-09 14:23:25 +0200 (Fri, 09 Jan 2015) | 4 lines scst_sysfs: return EINVAL on too big LUN Signed-off-by: Sebastian Herbszt ........ r5962 | bvassche | 2015-01-10 17:52:57 +0200 (Sat, 10 Jan 2015) | 1 line nightly build: Update kernel versions ........ r5963 | bvassche | 2015-01-13 10:42:28 +0200 (Tue, 13 Jan 2015) | 10 lines scst: Switch to thread context before executing a reservation command Persistent reservation commands need thread context because scst_pr_is_cmd_allowed() locks the PR mutex. Reservation commands either need BH or thread context. Hence switch from atomic to thread context before processing such commands. Reported-by: Shahar Salzman Signed-off-by: Bart Van Assche ........ r5964 | bvassche | 2015-01-13 10:51:08 +0200 (Tue, 13 Jan 2015) | 5 lines scst_parse_unmap_descriptors(): Avoid using GFP_KERNEL in atomic context Reported-by: Shahar Salzman Signed-off-by: Bart Van Assche ........ r5965 | bvassche | 2015-01-13 10:55:46 +0200 (Tue, 13 Jan 2015) | 68 lines qla2x00t: Copy entire SCST sense buffer to q2x ctio There seems to be a bug in passing sense information to QLA HBAs, where the last 2 bytes of the sense data (ASC, ASCQ) are not copied to the low level sense buffer. We encountered this in ESX, which relies on these 2 bytes to parse the MISCOMPARE sense code (0xE1, 0x1D, 0x00). Bellow is a simple test to recreate this issue, but during vMotion operations (where VMs are moved from one host to another), this may cause the operation to fail leaving the VM in an inconsistent state. The test I ran to verify that we are indeed missing the bytes is the following: 1. Create a SCST based device 2. Expose the device to 2 ESX hosts 3. Format the device as VMFS5, create a test directory 4. From both hosts, I start writing to this directory (no VMs involved, just write normal files) At this stage, both ESX hosts try to take access to the directory. The VMFS filesystem contains a per-directory lock which is managed by COMPARE AND WRITE command. Each ESX will attempt to change the VMFS lock location from unlocked to locked to create the new file. Obviously there are bound to be failures (which are equivalent to programming locking conflicts), these are reported by the MISCOMPARE sense code. Upon these MISCOMPARE errors, the host will re-try taking the lock until it succeeds, and will then proceed to perform the write operation on the directory. Due to the bug in copying the sense buffer from the SCST core to the QLA ctio, instead of the full sense code, only the key (0xE) is sent, and ESX does not know how to handle it resulting in IO error. Here are the errors as they appear on the command line: /vmfs/volumes/54a297c4-ca5af1cc-7f94-002219d20f28/ats_test # ./open_close_test-esx2.sh ./open_close_test-esx2.sh: line 8: can't create ats_fileoptest-esx2_1.txt: Input/output error ./open_close_test-esx2.sh: line 8: can't create ats_fileoptest-esx2_21.txt: Input/output error ./open_close_test-esx2.sh: line 8: can't create ats_fileoptest-esx2_110.txt: Input/output error ./open_close_test-esx2.sh: line 8: can't create ats_fileoptest-esx2_111.txt: Input/output error In the /var/log/vmkernel.log, we can see that the sense information is missing (0xE, 0x0, 0x0) instead of (0xE, 0x1D, 0x0). 2014-12-30T12:13:20.714Z cpu6:33519)ScsiDeviceIO: 2338: Cmd(0x412e84f957c0) 0x89, CmdSN 0x234d from world 519051 to dev "eui.0024f400d5020007" failed H:0x0 D:0x2 P:0x0 Valid sense data: 0xe 0x0 0x0. 2014-12-30T12:13:20.766Z cpu6:33519)ScsiDeviceIO: 2338: Cmd(0x412e84f91d00) 0x89, CmdSN 0x2350 from world 519051 to dev "eui.0024f400d5020007" failed H:0x0 D:0x2 P:0x0 Valid sense data: 0xe 0x0 0x0. 2014-12-30T12:13:20.766Z cpu6:33519)ScsiDeviceIO: 2338: Cmd(0x412e80449fc0) 0x89, CmdSN 0x234f from world 519051 to dev "eui.0024f400d5020007" failed H:0x0 D:0x2 P:0x0 Valid sense data: 0xe 0x0 0x0. This patch fixes this issue, the test will run without a problem with the fix (no IO errors, all the files are properly written to the directory). Signed-off-by: Shahar Salzman Reviewed-by: Eran Mann [bvanassche: simplified implementation] Signed-off-by: Bart Van Assche ........ r5966 | bvassche | 2015-01-13 11:38:09 +0200 (Tue, 13 Jan 2015) | 5 lines qla2x00t: Register for RSCNs in target mode The QLogic firmware and qla2xxx do not register for RSCNs in target-only mode, so do that explicitly. ........ r5967 | bvassche | 2015-01-14 10:06:12 +0200 (Wed, 14 Jan 2015) | 1 line scst_targ: Use tabs instead of spaces for indentation (detected by checkpatch) ........ r5968 | bvassche | 2015-01-15 10:58:39 +0200 (Thu, 15 Jan 2015) | 4 lines scst_targ: Avoid triggering a kernel panic if dev_user_parse() returns SCST_CMD_STATE_STOP Reported-by: Ilan Steinberg ........ r5969 | vlnb | 2015-01-16 03:21:10 +0200 (Fri, 16 Jan 2015) | 3 lines Fix READ BUFFER and WRITE BUFFER commands ........ r5970 | vlnb | 2015-01-16 05:16:26 +0200 (Fri, 16 Jan 2015) | 3 lines Follow up for r5968 ........ r5971 | vlnb | 2015-01-16 05:53:29 +0200 (Fri, 16 Jan 2015) | 5 lines Report during user devices unjam LUN NOT SUPPORTED sense Reported-By: shahar.salzman ........ r5972 | bvassche | 2015-01-16 15:01:58 +0200 (Fri, 16 Jan 2015) | 2 lines scst.spec.in: Rename variable kver into kversion ........ r5973 | bvassche | 2015-01-16 15:12:22 +0200 (Fri, 16 Jan 2015) | 2 lines scst.spec.in: Pass kernel version via RPM-variable %{kversion} instead of shell variable ${KVER} ........ r5974 | bvassche | 2015-01-16 15:16:06 +0200 (Fri, 16 Jan 2015) | 6 lines scst.spec.in: Determine version number correctly on a koji server This patch has been tested on a koji build server and also on four different RPM-based distributions (CentOS 7, Fedora 20, openSuSE 13.2 and SLES 11 SP3). ........ r5975 | bvassche | 2015-01-16 18:12:38 +0200 (Fri, 16 Jan 2015) | 1 line scst.spec.in: Leave out kernel version from RPM name ........ r5976 | bvassche | 2015-01-16 18:20:10 +0200 (Fri, 16 Jan 2015) | 1 line scst.spec.in: Add DKMS support ........ r5977 | vlnb | 2015-01-20 06:18:07 +0200 (Tue, 20 Jan 2015) | 3 lines Revert r5964 as not needed ........ r5978 | vlnb | 2015-01-20 06:20:13 +0200 (Tue, 20 Jan 2015) | 3 lines Revert r5963 as not needed ........ r5979 | bvassche | 2015-01-20 17:04:23 +0200 (Tue, 20 Jan 2015) | 13 lines scst: Rework SCSI pass-through support for kernel versions >= 2.6.30 Changes in this patch: - Rework the SCSI pass-through code such that for kernel versions >= 2.6.30 the scst_exec_req_fifo patch is no longer needed. - Modify the pass-through code such that blk_rq_append_bio() is only called for kernel version 2.6.30. For later kernel versions blk_make_request() is called instead. - Rework scst_scsi_exec_async(). - Add debug tracing of SCSI pass-through result status. - Add a lockdep_assert_held() call in scsi_end_async(). ........ r5980 | bvassche | 2015-01-20 19:13:13 +0200 (Tue, 20 Jan 2015) | 1 line nightly build: Update kernel versions ........ r5981 | vlnb | 2015-01-21 06:15:42 +0200 (Wed, 21 Jan 2015) | 3 lines Follow up for r5979 ........ r5982 | vlnb | 2015-01-21 06:20:53 +0200 (Wed, 21 Jan 2015) | 5 lines Fix returning changeable values for caching mode page Reported by Consus ........ r5983 | bvassche | 2015-01-21 15:11:56 +0200 (Wed, 21 Jan 2015) | 1 line scst.h: Fix a sparse warning for kernels 2.6.29..2.6.31 ........ r5984 | vlnb | 2015-01-22 07:03:17 +0200 (Thu, 22 Jan 2015) | 9 lines [PATCH] scst_local: Fix bidirectional command support scsi_setup_cmnd() sets sc_data_direction to DMA_TO_DEVICE for bidirectional commands. Hence test SCpnt->request->next_rq instead of sc_data_direction to figure out whether or not a command is bidirectional. Signed-off-by: Bart Van Assche ........ r5985 | vlnb | 2015-01-22 07:06:45 +0200 (Thu, 22 Jan 2015) | 12 lines [PATCH] scst_main: Suppress a checkpatch warning triggered by INIT_CACHEP{,_ALIGN} Avoid that checkpatch v3.18 reports the following warning for these two macros: WARNING: Macros with flow control statements should be avoided This patch does not change any functionality. Signed-off-by: Bart Van Assche ........ r5986 | vlnb | 2015-01-22 07:09:17 +0200 (Thu, 22 Jan 2015) | 9 lines scst_vdisk: Micro-optimize vdisk_caching_pg This patch does not change any behavior but micro-optimizes vdisk_caching_pg(). Declaring the array caching_pg[] const reduces 11 bytes from the assembler code of this function. Signed-off-by: Bart Van Assche ........ r5987 | vlnb | 2015-01-22 07:10:42 +0200 (Thu, 22 Jan 2015) | 10 lines scst: Suppress a smatch warning in vdisk_unmap_range() Avoid that the static source code analysis tool 'smatch' reports the following warning: vdisk_unmap_range() warn: should 'blocks << cmd->dev->block_shift' be a 64 bit type? Signed-off-by: Bart Van Assche ........ r5988 | vlnb | 2015-01-22 07:13:59 +0200 (Thu, 22 Jan 2015) | 27 lines scst_vdisk: Fix zero-copy read for tmpfs For some filesystems, e.g. tmpfs, address_space.readpage is NULL. Disable zero-copy reading for such filesystems. See also shmem_aops in mm/shmem.c. See also inode_init_always() and empty_aops in fs/inode.c. This patch avoids that the following call trace is triggered: BUG: unable to handle kernel NULL pointer dereference at (null) Call Trace: [] prepare_read+0x106/0x1d0 [scst_vdisk] [] fileio_alloc_data_buf+0xf0/0x330 [scst_vdisk] [] scst_prepare_space+0x9b/0x6e0 [scst] [] scst_process_active_cmd+0x545/0x840 [scst] [] scst_cmd_init_done+0x302/0x5d0 [scst] [] scst_cmd_init_stage1_done.constprop.37+0x12/0x20 [iscsi_scst] [] scsi_cmnd_start+0x25a/0x550 [iscsi_scst] [] cmnd_rx_start+0x148/0x1a0 [iscsi_scst] [] process_read_io+0x3b8/0x800 [iscsi_scst] [] scst_do_job_rd+0xc7/0x220 [iscsi_scst] [] istrd+0x16d/0x2e0 [iscsi_scst] [] kthread+0xed/0x110 [] ret_from_fork+0x7c/0xb0 Signed-off-by: Bart Van Assche ........ r5989 | vlnb | 2015-01-24 07:37:57 +0200 (Sat, 24 Jan 2015) | 5 lines scst_local: Rework data direction detection code Signed-off-by: Bart Van Assche ........ r5990 | bvassche | 2015-01-26 13:32:32 +0200 (Mon, 26 Jan 2015) | 1 line ib_srpt: Detect Mellanox OFED 2.3 correctly ........ r5991 | vlnb | 2015-01-28 07:07:46 +0200 (Wed, 28 Jan 2015) | 3 lines Cleanups ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5993 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 27 +- doc/Makefile | 2 +- doc/fig2.png | Bin 28345 -> 42859 bytes doc/fig3.png | Bin 39441 -> 0 bytes doc/fig4.png | Bin 42859 -> 0 bytes doc/scst_pg.sgml | 3469 ++++++++--------- doc/sgv_cache.sgml | 335 -- fcst/ft_sess.c | 4 +- ibmvstgt/src/Kconfig | 2 +- ibmvstgt/src/orig/2.6.35/Kconfig | 2 +- ibmvstgt/src/orig/2.6.36/Kconfig | 2 +- iscsi-scst/README | 2 +- iscsi-scst/README_in-tree | 2 +- iscsi-scst/doc/SCST_Gentoo_HOWTO.txt | 1 - iscsi-scst/doc/iscsi-scst-howto.txt | 1 - iscsi-scst/kernel/config.c | 4 +- iscsi-scst/kernel/conn.c | 10 +- iscsi-scst/kernel/iscsi.c | 4 +- iscsi-scst/kernel/iscsi.h | 2 +- iscsi-scst/kernel/nthread.c | 6 +- .../patches/put_page_callback-3.17.patch | 364 ++ .../patches/put_page_callback-3.18.patch | 387 ++ .../rhel/put_page_callback-2.6.32-504.patch | 453 +++ iscsi-scst/kernel/session.c | 6 +- mvsas_tgt/mv_tgt.c | 2 +- mvsas_tgt/mv_tgt.h | 4 +- nightly/conf/nightly.conf | 18 +- qla2x00t/doc/qla2x00t-howto.html | 7 - qla2x00t/qla2x00-target/Makefile_in-tree-3.17 | 5 + qla2x00t/qla2x00-target/Makefile_in-tree-3.18 | 5 + qla2x00t/qla2x00-target/README | 2 +- qla2x00t/qla2x00-target/qla2x00t.c | 116 +- qla2x00t/qla2x00-target/qla2x00t.h | 6 +- qla2x00t/qla_init.c | 2 +- qla2x00t/qla_iocb.c | 6 +- qla2x00t/qla_isr.c | 56 +- qla2x00t/qla_mbx.c | 9 +- qla2x00t/qla_os.c | 20 +- scripts/generate-kernel-patch | 15 +- scripts/generate-release-archive | 12 +- scripts/rebuild-rhel-kernel-rpm | 56 +- scripts/specialize-patch | 10 +- scst.spec.in | 236 +- scst/Makefile | 4 +- scst/README | 20 +- scst/README_in-tree | 2 +- scst/include/scst.h | 19 +- scst/include/scst_const.h | 4 +- .../in-tree/Kconfig.drivers.Linux-3.17.patch | 13 + .../in-tree/Kconfig.drivers.Linux-3.18.patch | 13 + .../kernel/in-tree/Makefile.dev_handlers-3.17 | 14 + .../kernel/in-tree/Makefile.dev_handlers-3.18 | 14 + .../in-tree/Makefile.drivers.Linux-3.17.patch | 12 + .../in-tree/Makefile.drivers.Linux-3.18.patch | 12 + scst/kernel/in-tree/Makefile.scst-3.17 | 13 + scst/kernel/in-tree/Makefile.scst-3.18 | 13 + .../rhel/scst_exec_req_fifo-2.6.32.patch | 529 --- .../rhel/scst_exec_req_fifo-3.10.0-121.patch | 1 - .../rhel/scst_exec_req_fifo-3.10.0-123.patch | 524 --- scst/kernel/scst_exec_req_fifo-2.6.30.patch | 529 --- scst/kernel/scst_exec_req_fifo-2.6.31.patch | 529 --- scst/kernel/scst_exec_req_fifo-2.6.32.patch | 529 --- scst/kernel/scst_exec_req_fifo-2.6.33.patch | 529 --- scst/kernel/scst_exec_req_fifo-2.6.34.patch | 530 --- scst/kernel/scst_exec_req_fifo-2.6.35.patch | 530 --- scst/kernel/scst_exec_req_fifo-2.6.36.patch | 532 --- scst/kernel/scst_exec_req_fifo-2.6.37.patch | 532 --- scst/kernel/scst_exec_req_fifo-2.6.38.patch | 532 --- scst/kernel/scst_exec_req_fifo-2.6.39.patch | 532 --- scst/kernel/scst_exec_req_fifo-3.0.patch | 532 --- scst/kernel/scst_exec_req_fifo-3.1.patch | 536 --- scst/kernel/scst_exec_req_fifo-3.10.patch | 527 --- scst/kernel/scst_exec_req_fifo-3.11.patch | 528 --- scst/kernel/scst_exec_req_fifo-3.12.patch | 528 --- scst/kernel/scst_exec_req_fifo-3.13.patch | 528 --- scst/kernel/scst_exec_req_fifo-3.14.patch | 528 --- scst/kernel/scst_exec_req_fifo-3.15.patch | 528 --- scst/kernel/scst_exec_req_fifo-3.16.patch | 524 --- scst/kernel/scst_exec_req_fifo-3.2.patch | 536 --- scst/kernel/scst_exec_req_fifo-3.3.patch | 536 --- scst/kernel/scst_exec_req_fifo-3.4.patch | 528 --- scst/kernel/scst_exec_req_fifo-3.5.patch | 527 --- scst/kernel/scst_exec_req_fifo-3.6.patch | 527 --- scst/kernel/scst_exec_req_fifo-3.7.patch | 527 --- scst/kernel/scst_exec_req_fifo-3.8.patch | 527 --- scst/kernel/scst_exec_req_fifo-3.9.patch | 527 --- scst/src/dev_handlers/scst_disk.c | 12 +- scst/src/dev_handlers/scst_tape.c | 2 +- scst/src/dev_handlers/scst_user.c | 32 +- scst/src/dev_handlers/scst_vdisk.c | 166 +- scst/src/scst_lib.c | 509 ++- scst/src/scst_main.c | 101 +- scst/src/scst_mem.c | 10 +- scst/src/scst_pres.c | 2 +- scst/src/scst_priv.h | 9 - scst/src/scst_proc.c | 2 +- scst/src/scst_sysfs.c | 5 +- scst/src/scst_targ.c | 110 +- scst_local/in-tree/Makefile-3.17 | 2 + scst_local/in-tree/Makefile-3.18 | 2 + scst_local/scst_local.c | 35 +- scstadmin/init.d/scst | 2 +- .../scst-0.8.22/lib/SCST/SCST.pm | 28 +- scstadmin/scstadmin.procfs/scstadmin | 10 +- .../scst-0.9.10/lib/SCST/SCST.pm | 2 +- srpt/Makefile | 4 +- srpt/README | 8 +- srpt/patches/kernel-3.17-pre-cflags.patch | 12 + srpt/patches/kernel-3.18-pre-cflags.patch | 12 + srpt/src/ib_srpt.c | 352 +- srpt/src/ib_srpt.h | 27 +- srpt/src/srp-ext.h | 22 + usr/fileio/common.c | 2 +- www/comparison.html | 10 +- 114 files changed, 4515 insertions(+), 18152 deletions(-) delete mode 100644 doc/fig3.png delete mode 100644 doc/fig4.png delete mode 100644 doc/sgv_cache.sgml create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.17.patch create mode 100644 iscsi-scst/kernel/patches/put_page_callback-3.18.patch create mode 100644 iscsi-scst/kernel/patches/rhel/put_page_callback-2.6.32-504.patch create mode 100644 qla2x00t/qla2x00-target/Makefile_in-tree-3.17 create mode 100644 qla2x00t/qla2x00-target/Makefile_in-tree-3.18 create mode 100644 scst/kernel/in-tree/Kconfig.drivers.Linux-3.17.patch create mode 100644 scst/kernel/in-tree/Kconfig.drivers.Linux-3.18.patch create mode 100644 scst/kernel/in-tree/Makefile.dev_handlers-3.17 create mode 100644 scst/kernel/in-tree/Makefile.dev_handlers-3.18 create mode 100644 scst/kernel/in-tree/Makefile.drivers.Linux-3.17.patch create mode 100644 scst/kernel/in-tree/Makefile.drivers.Linux-3.18.patch create mode 100644 scst/kernel/in-tree/Makefile.scst-3.17 create mode 100644 scst/kernel/in-tree/Makefile.scst-3.18 delete mode 100644 scst/kernel/rhel/scst_exec_req_fifo-2.6.32.patch delete mode 120000 scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.patch delete mode 100644 scst/kernel/rhel/scst_exec_req_fifo-3.10.0-123.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.30.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.31.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.32.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.33.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.34.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.35.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.36.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.37.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.38.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-2.6.39.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.0.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.1.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.10.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.11.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.12.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.13.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.14.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.15.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.16.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.2.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.3.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.4.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.5.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.6.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.7.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.8.patch delete mode 100644 scst/kernel/scst_exec_req_fifo-3.9.patch create mode 100644 scst_local/in-tree/Makefile-3.17 create mode 100644 scst_local/in-tree/Makefile-3.18 create mode 100644 srpt/patches/kernel-3.17-pre-cflags.patch create mode 100644 srpt/patches/kernel-3.18-pre-cflags.patch create mode 100644 srpt/src/srp-ext.h diff --git a/Makefile b/Makefile index 68f2964f9..d3f42b76c 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,7 @@ help: all: cd $(SCST_DIR) && $(MAKE) $@ # @if [ -d $(DOC_DIR) ]; then cd $(DOC_DIR) && $(MAKE) $@; fi - @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; else if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi fi # @if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi # @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi # @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @@ -152,7 +152,7 @@ all: install: cd $(SCST_DIR) && $(MAKE) $@ # @if [ -d $(DOC_DIR) ]; then cd $(DOC_DIR) && $(MAKE) $@; fi - @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; else if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi fi # @if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi # @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi # @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @@ -163,7 +163,7 @@ install: uninstall: cd $(SCST_DIR) && $(MAKE) $@ # @if [ -d $(DOC_DIR) ]; then cd $(DOC_DIR) && $(MAKE) $@; fi - @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi + @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; else if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi fi # @if [ -d $(QLA_OLD_DIR) ]; then cd $(QLA_OLD_DIR) && $(MAKE) $@; fi # @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi @if [ -d $(SRP_DIR) ]; then cd $(SRP_DIR) && $(MAKE) $@; fi @@ -228,10 +228,10 @@ docs_extraclean: scstadm: cd $(SCSTADM_DIR) && $(MAKE) all -scstadm_install: +scstadm_install: cd $(SCSTADM_DIR) && $(MAKE) install -scstadm_uninstall: +scstadm_uninstall: cd $(SCSTADM_DIR) && $(MAKE) uninstall scstadm_clean: @@ -252,7 +252,7 @@ qla_install: qla_uninstall: cd $(QLA_DIR) && $(MAKE) uninstall -qla_clean: +qla_clean: cd $(QLA_INI_DIR) && $(MAKE) clean cd $(QLA_DIR) && $(MAKE) clean @@ -284,7 +284,7 @@ iscsi_install: iscsi_uninstall: cd $(ISCSI_DIR) && $(MAKE) uninstall -iscsi_clean: +iscsi_clean: cd $(ISCSI_DIR) && $(MAKE) clean iscsi_extraclean: @@ -383,7 +383,13 @@ fcst_extraclean: scst-dist-gzip: name=scst && \ mkdir $${name}-$(VERSION) && \ - { scripts/list-source-files | \ + { if [ -h qla2x00t ] || { mount | grep "on $$PWD/qla2x00t type"; }; \ + then \ + scripts/list-source-files | grep -v ^qla2x00t/; \ + find qla2x00t/ -type f; \ + else \ + scripts/list-source-files; \ + fi | \ grep -E '^doc/|^fcst/|^iscsi-scst/|^Makefile|^qla2x00t/|^scst.spec|^scst/|^scst_local/|^srpt/'|\ tar -T- -cf- | \ tar -C $${name}-$(VERSION) -xf-; } && \ @@ -402,8 +408,9 @@ scst-rpm: cp $${name}-$(VERSION).tar.bz2 $${rpmtopdir}/SOURCES && \ sed "s/@rpm_version@/$(VERSION)/g" \ <$${name}.spec.in >$${name}.spec; \ - MAKE="$(MAKE)" \ - rpmbuild --define="%_topdir $${rpmtopdir}" -ba $${name}.spec && \ + MAKE="$(MAKE)" rpmbuild --define="%_topdir $${rpmtopdir}" \ + $(if $(KVER),--define="%kversion $(KVER)") \ + -ba $${name}.spec && \ rm -f $${name}-$(VERSION).tar.bz2 rpm: diff --git a/doc/Makefile b/doc/Makefile index 44257779d..d2b95bd76 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -8,7 +8,7 @@ RTFS = $(SRCS:.sgml=.rtf) COMMAND=linuxdoc --backend= -all: txt pdf html +all: pdf html txt: $(TXTS) diff --git a/doc/fig2.png b/doc/fig2.png index 3bdf882dc0aa5ce82b099aa72b328402e2a50ec4..667e4128971da7d71ed7bd8446e5260038acddc3 100644 GIT binary patch literal 42859 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 DM7$IA diff --git a/doc/fig3.png b/doc/fig3.png deleted file mode 100644 index e502f2dd31637f56d1646d316d2e821bdb07c1ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39441 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 HTNGRP%OM|3C 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 E2V -Generic SCSI Target Middle Level for Linux + +SCST technical description + Vladislav Bolkhovitin -Version 0.9.5 2006/12/01, actual for SCST 0.9.5 and later - - -This document describes SCSI target mid-level for Linux (SCST), its -architecture and drivers from the driver writer's point of view. - + +Version 3.0.0 for SCST 3.0.0 and later + Introduction -

                            -SCST is a SCSI target mid-level subsystem for Linux. It is designed to -provide unified, consistent interface between SCSI target drivers and -Linux kernel and simplify target drivers development as much as -possible. It has the following features: +

                            SCST is a SCSI target mid-level subsystem for Linux. It provides +unified consistent interface between SCSI target drivers, backend device +handlers and Linux kernel as well as simplifies target drivers +development as much as possible. - +It has the following features: - Very low overhead, fine-grained locks and simplest commands -processing path, which allow to reach maximum possible performance and -scalability that close to theoretical limit. + - Incoming requests can be processed in the caller's context or in -one of the internal SCST's tasklets, therefore no extra context switches -required. + Very low overhead and fine-grained locks, which allow to reach +maximum possible performance and scalability that close to theoretical +limit. Complete SMP support. - Undertakes most problems, related to execution contexts, thus -practically eliminating one of the most complicated problem in the -kernel drivers development. For example, target drivers for Marvell SAS -adapters or for InfiniBand SRP are less 3000 lines of code long. - Performs all required pre- and post- processing of incoming -requests and all necessary error recovery functionality. +requests and all necessary error recovery functionality. - Emulates necessary functionality of SCSI host adapter, because + Emulates necessary functionality of SCSI host adapters, because from a remote initiator's point of view SCST acts as a SCSI host with its own devices. Some of the emulated functions are the following: - + Generation of necessary UNIT ATTENTIONs, their storage and delivery to all connected remote initiators (sessions). - RESERVE/RELEASE functionality. + RESERVE/RELEASE functionality, including Persistent Reservations. - CA/ACA conditions. - All types of RESETs and other task management functions. - - REPORT LUNS command as well as SCSI address space management - in order to have consistent address space on all remote initiators, - since local SCSI devices could not know about each other to report - via REPORT LUNS command. Additionally, SCST responds with error on - all commands to non-existing devices and provides access control - (not implemented yet), so different remote initiators could see - different set of devices. + + REPORT LUNS command as well as SCSI address space + management in order to have consistent address space on all + remote initiators, since local SCSI devices could not know about + each other to report via REPORT LUNS command. Additionally, SCST + responds with error on all commands to non-existing devices and + provides access control, so different remote initiators could + see different set of devices. Other necessary functionality (task attributes, etc.) as specified in SAM-2, SPC-2, SAM-3, SPC-3 and other SCSI standards. - + - - Device handlers architecture provides extra reliability and -security via verifying all incoming requests and allows to make any -additional requests processing, which is completely independent from -target drivers, for example, data caching or device dependent -exceptional conditions treatment. + + Verifies all incoming requests to ensure commands execution +reliability and security. + + Device handlers architecture provides extra flexibility by +allowing to make additional requests processing, which is completely +independent from target drivers, for example, data caching or device +dependent exceptional conditions treatment. -Interoperability between SCST and local SCSI initiators (like sd, st) is -the additional issue that SCST is going to address (it is not -implemented yet). It is necessary, because local SCSI initiators can -change the state of the device, for example RESERVE the device, or some -of its parameters and that would be done behind SCST, which could lead -to various problems. Thus, RESERVE/RELEASE commands, locally generated -UNIT ATTENTIONs, etc. should be intercepted and processed as if local -SCSI initiators act as remote SCSI initiators connected to SCST. This -feature requires some the kernel modification. Since in the current -version it is not implemented, SCST and the target drivers are able to -work with any unpatched 2.4 kernel version. - -Interface between SCST and the target drivers is based on work, done by -University of New Hampshire Interoperability Labs (UNH IOL). - -All described below data structures and function could be found in -. - Terms and Definitions -

                            +SCST Architecture +SCST Core Architecture -

                            +

                            SCST accepts commands and passes them to SCSI mid-level at the same way as SCSI high-level drivers (sg, sd, st) do. Figure 1 shows interaction between SCST, its drivers and Linux SCSI subsystem. @@ -171,42 +135,61 @@ interaction between SCST, its drivers and Linux SCSI subsystem. -Target driver registration + Target drivers + +struct scst_tgt_template

                            To work with SCST a target driver must register its template in SCST by -calling scst_register_target_template(). The template lets SCST know the +calling Structure scst_tgt_template - -

                            -struct scst_tgt_template +struct scst_tgt_template { int sg_tablesize; - const char name[15]; + const char name[SCST_MAX_NAME]; unsigned unchecked_isa_dma:1; unsigned use_clustering:1; + unsigned no_clustering:1; - unsigned xmit_response_atomic:1; + unsigned xmit_response_atomic:1; unsigned rdy_to_xfer_atomic:1; - unsigned report_aen_atomic:1; - int (* detect) (struct scst_tgt_template *tgt_template); - int (* release)(struct scst_tgt *tgt); + unsigned no_proc_entry:1; - int (* xmit_response)(struct scst_cmd *cmd); + int max_hw_pending_time; + + int threads_num; + + int (*detect) (struct scst_tgt_template *tgt_template); + int (*release)(struct scst_tgt *tgt); + + int (*xmit_response)(struct scst_cmd *cmd); int (* rdy_to_xfer)(struct scst_cmd *cmd); - + + void (*on_hw_pending_cmd_timeout) (struct scst_cmd *cmd); + void (*on_free_cmd) (struct scst_cmd *cmd); - void (* task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd); - void (* report_aen)(int mgmt_fn, const uint8_t *lun, int lun_len); - - int (*proc_info) (char *buffer, char **start, off_t offset, - int length, int *eof, struct scst_tgt *tgt, int inout); + int (*alloc_data_buf) (struct scst_cmd *cmd); + + void (*preprocessing_done) (struct scst_cmd *cmd); + + int (*pre_exec) (struct scst_cmd *cmd); + + void (*task_mgmt_affected_cmds_done) (struct scst_mgmt_cmd *mgmt_cmd); + void (*task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd); + + int (*report_aen) (struct scst_aen *aen); + + int (*read_proc) (struct seq_file *seq, struct scst_tgt *tgt); + int (*write_proc) (char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_tgt *tgt); + + int (*get_initiator_port_transport_id) (struct scst_session *sess, + uint8_t **transport_id); } @@ -225,97 +208,163 @@ the template. Must be defined. unchecked DMA onto an ISA bus. = 0 to signify -the number of detected target adapters. A negative value should be -returned whenever there is an error. Must be defined. += 0 to +signify the number of detected target adapters. A negative value should +be returned whenever there is an error. Must be defined. - +0 if the regular SCST allocation should be done. In case of returning +successfully, scst_cmd->tgt_data_buf_alloced will be set by SCST. It is +possible that both target driver and dev handler request own memory +allocation. If allocation in atomic context, i.e. scst_cmd_atomic() is +true, and < 0 is returned, this function will be recalled in thread +context. Note that the driver will have to handle itself all relevant +details such as scatterlist setup, highmem, freeing the allocated +memory, etc. + + - - + Functions @@ -333,7 +382,7 @@ can return the following error codes: -More about More about xmit_response() -

                            -As already written above, function +As already written above, function xmit_response() should transmit +the response data and the status from the cmd parameter. - - - - -If Target driver registration functions scst_register_target_template() @@ -416,13 +442,13 @@ Where: Returns 0 on success or appropriate error code otherwise. -scst_register() +scst_register_target()

                            -Function -struct scst_tgt *scst_register( +struct scst_tgt *scst_register_target( struct scst_tgt_template *vtt) @@ -434,20 +460,20 @@ Where: Returns target structure based on template vtt or NULL in case of error. -Target driver unregistration +Target driver unregistration functions

                            In order to unregister itself target driver should at first call -scst_unregister() +scst_unregister_target()

                            -Function -void scst_unregister( +void scst_unregister_target( struct scst_tgt *tgt) @@ -457,7 +483,7 @@ Where: -scst_unregister_target_template() +scst_unregister_target_template()

                            Function -SCST session registration +Device specific drivers (backend device handlers) -

                            -When target driver determines that it needs to create new SCST session -(for example, by receiving new TCP connection), it should call - -struct scst_session *scst_register_session( - struct scst_tgt *tgt, - int atomic, - const char *initiator_name, - void *data, - void (*result_fn) ( - struct scst_session *sess, - void *data, - int result)); - - -Where: - - - - - - - - - -A session creation and initialization is a complex task, which requires -sleeping state, so it can't be fully done in interrupt context. -Therefore the "bottom half" of it, if - - - - Session registration when - - -

                            - - - - Session registration when -
                            - -SCST session unregistration - -

                            -SCST session unregistration basically is the same, except that instead of -atomic parameter there is -void scst_unregister_session( - struct scst_session *sess, - int wait, - void (* unreg_done_fn)( - struct scst_session *sess)) - - -Where: - - - - - - - - - -All outstanding commands will be finished regularly. After -The commands processing and interaction between SCST and its drivers - -

                            -The commands processing by SCST started when target driver calls - - -If the command required no data transfer, it will be passed to -SCSI mid-level directly or via device handler's If the command is a If the command is a - -When the command is finished by SCSI mid-level, device handler's - - - - - The commands processing flow - - - -Additionally, before calling - - Expected transfer length and direction via - - -The commands processing functions - -scst_rx_cmd() - -

                            -Function -struct scst_cmd *scst_rx_cmd( - struct scst_session *sess, - const uint8_t *lun, - int lun_len, - const uint8_t *cdb, - int cdb_len, - int atomic) - - -Where: - - - - - -scst_cmd_init_done() - -

                            -Function -void scst_cmd_init_done( - struct scst_cmd *cmd, - int pref_context) - - -Where: - - - - - -scst_rx_data() - -

                            -Function -void scst_rx_data( - struct scst_cmd *cmd, - int status, - int pref_context) - - -Where: - - - - - -Parameter - - - -scst_tgt_cmd_done() - -

                            -Function -void scst_tgt_cmd_done( - struct scst_cmd *cmd) - - -Where: - - - -The commands processing context - -

                            -Execution context often is a major problem in the kernel drivers -development, because many contexts, like IRQ one, greatly limit -available functionality, therefore require additional complex code in -order to pass processing to more simple context. SCST does its best to -undertake most of the context handling. - -On the initialization time SCST creates for internal command processing -as many threads as there are processors in the system or specified by -user via -Directly, i.e. in the caller's context, without limitations -Directly atomically, i.e. with sleeping forbidden -In the SCST's internal per processor or per session thread -In the SCST's per processor tasklet - - -The target driver sets this context as pref_context parameter for -Preferred context constants - -

                            -There are the following preferred context constants: - - - - - -Task management functions - -

                            -There are the following task management functions supported: - - - - - -scst_rx_mgmt_fn_tag() - -

                            -Function -int scst_rx_mgmt_fn_tag( - struct scst_session *sess, - int fn, - uint32_t tag, - int atomic, - void *tgt_specific) - - -Where: - - - - - -Returns 0 if the command was successfully created and scheduled for -execution, error code otherwise. On success, the completion status of -the command will be reported asynchronously via scst_rx_mgmt_fn_lun() - -

                            -Function -int scst_rx_mgmt_fn_lun( - struct scst_session *sess, - int fn, - const uint8_t *lun, - int lun_len, - int atomic, - void *tgt_specific); - - -Where: - - - - - -Returns 0 if the command was successfully created and scheduled for -execution, error code otherwise. On success, the completion status of -the command will be reported asynchronously via Device specific drivers (device handlers) - -

                            -Device specific drivers are plugins for SCST, which help SCST to analyze -incoming requests and determine parameters, specific to various types -of devices. Device handlers are intended for the following: +

                            Device specific drivers are add-ons for SCST, which help SCST to +analyze incoming requests and determine parameters, specific to various +types of devices as well as actually execute specified SCSI commands. +Device handlers are intended for the following: @@ -1013,41 +513,237 @@ current device's configuration exactly as an end-target SCSI device does. This serves two purposes: - + Improves security and reliability by not trusting the data supplied by remote initiator via SCSI low-level protocol. - + Some low-level SCSI protocols don't provide data transfer length and direction, so that information can be get only directly from CDB and current device's configuration. For example, for tape devices to get data transfer size it might be necessary to know block size setting. - + + Execute commands + To process some exceptional conditions, like ILI on tape devices. To initialize incoming commands with some device-specific parameters, like timeout value. -To allow some additional device-specific commands pre-, post- +To allow some additional device-specific commands pre-, post- processing or alternative execution, like copying data from system cache, and do that completely independently from target drivers. -Device handlers performs very lightweight processing and therefore -should not considerably affect performance or CPU load. They are -considered to be part of SCST, so they could directly access any fields -in SCST's structures as well as use the corresponding functions. +Device handlers considered to be part of SCST, so they could directly +access any fields in SCST's structures as well as use the corresponding +functions. Without appropriate device handler SCST hides devices of this type from remote initiators and returns Device specific driver registration +Structure scst_register_dev_driver() +

                            +Structure +struct scst_dev_type +{ + char name[]; + int type; + + unsigned parse_atomic:1; + unsigned alloc_data_buf_atomic:1; + unsigned dev_done_atomic:1; + + unsigned no_proc:1; + + unsigned exec_sync:1; + + unsigned pr_cmds_notifications:1; + + int threads_num; + enum scst_dev_type_threads_pool_type threads_pool_type; + + int (*attach) (struct scst_device *dev); + void (*detach) (struct scst_device *dev); + + int (*attach_tgt) (struct scst_tgt_device *tgt_dev); + void (*detach_tgt) (struct scst_tgt_device *tgt_dev); + + int (*parse) (struct scst_cmd *cmd); + int (*alloc_data_buf) (struct scst_cmd *cmd); + int (*exec) (struct scst_cmd *cmd); + int (*dev_done) (struct scst_cmd *cmd); + int (*on_free_cmd) (struct scst_cmd *cmd); + + int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, + struct scst_tgt_dev *tgt_dev); + + int (*read_proc) (struct seq_file *seq, struct scst_dev_type *dev_type); + int (*write_proc) (char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_dev_type *dev_type); +} + + +Where: + + + + 0. Possible values: + + + + + +bufflen/ and data_direction/ (both - REQUIRED). Returns the +command's scst_cmd_done()/ callback. + +Returns: + + + + +If this function provides sync execution, you should set +exec_sync flag and consider to setup dedicated threads by +setting 0. + +Optional, if not set, the commands will be sent directly to SCSI +device. + + + + + + + +Device specific drivers registration + + scst_register_dev_driver()

                            To work with SCST a device specific driver must register itself in SCST by @@ -1066,256 +762,57 @@ Where: The function returns 0 on success or appropriate error code otherwise. -Structure scst_register_virtual_device()

                            -Structure -struct scst_dev_type -{ - char name[15]; - int type; - - unsigned parse_atomic:1; - unsigned exec_atomic:1; - unsigned dev_done_atomic:1; - - int (*init) (struct scst_dev_type *dev_type); - void (*release) (struct scst_dev_type *dev_type); - - int (*attach) (struct scst_device *dev); - void (*detach) (struct scst_device *dev); - - int (*attach_tgt) (struct scst_tgt_device *tgt_dev); - void (*detach_tgt) (struct scst_tgt_device *tgt_dev); - - int (*parse) (struct scst_cmd *cmd); - int (*exec) (struct scst_cmd *cmd, - void (*scst_cmd_done)(struct scsi_cmnd *cmd, int next_state)); - int (*dev_done) (struct scst_cmd *cmd); - int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, - struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd_to_abort); - int (*on_free_cmd) (struct scst_cmd *cmd); - - int (*proc_info) (char *buffer, char **start, off_t offset, - int length, int *eof, struct scst_dev_type *dev_type, - int inout) - - struct module *module; -} +int scst_register_virtual_device( + struct scst_dev_type *dev_handler, + const char *dev_name) Where: -bufflen/ and data_direction/ (see below - - - - - - - - - - - - -If the driver needs to create additional files in its /proc -subdirectory, it can use -Structure Device specific drivers unregistration + + scst_unregister_virtual_device() + +

                            +Virtual devices unregistered by calling + -struct scst_info_cdb -{ - enum scst_cdb_flags flags; - scst_data_direction direction; - unsigned int transfer_len; - unsigned short cdb_len; - const char *op_name; -} +void scst_unregister_virtual_device( + int id) Where: - - - - - - -Field data_direction/, set by - - - -Device specific driver unregistration + scst_unregister_dev_driver()

                            Device specific driver is unregistered by calling @@ -1332,968 +829,1350 @@ Where: -SCST commands' states +SCST sessions -

                            -There are the following states, which a SCST command passes through -during execution and which could be returned by device handler's -SCST sessions registration + +

                            +When target driver determines that it needs to create new SCST session +(for example, by receiving new TCP connection), it should call + +struct scst_session *scst_register_session( + struct scst_tgt *tgt, + int atomic, + const char *initiator_name, + void *tgt_priv, + void *result_fn_data, + void (*result_fn) ( + struct scst_session *sess, + void *data, + int result)) + + +Where: -tgt_dev/ + + + + + + +A session creation and initialization is a complex task, which requires +sleeping state, so it can't be fully done in interrupt context. +Therefore the "bottom half" of it, if scst_register_session() is +called from atomic context, will be done in SCST thread context. In this +case scst_register_session() will return not completely initialized +session, but the target driver can supply commands to this session via +scst_rx_cmd(). Those commands processing will be delayed inside +SCST until the session initialization is finished, then their processing +will be restarted. The target driver will be notified about finish of +the session initialization by function SCST sessions unregistration + +

                            +SCST session unregistration basically is the same, except that instead of +atomic parameter there is +void scst_unregister_session( + struct scst_session *sess, + int wait, + void (*unreg_done_fn)( + struct scst_session *sess)) + + +Where: + + + + + + + + + +All outstanding commands will be finished regularly. After +scst_unregister_session() returned no new commands must be sent to SCST +via scst_rx_cmd(). Also, the caller must ensure that no scst_rx_cmd() or +scst_rx_mgmt_fn_*() is called in parallel with +scst_unregister_session(). + +Function scst_unregister_session()/ can be called before result_fn() of +scst_register_session() called, i.e. during the session +registration/initialization. + + +Commands processing and interaction between SCST core and its drivers + +

                            +Consider simplified commands processing example. It assumes that target +driver doesn't need own memory allocation, i.e. not defined +alloc_data_buf() callback. Example of such target driver is qla2x00t. + +The commands processing by SCST started when target driver calls + + +If the command required no data transfer, it will be passed to +SCSI mid-level directly or via device handler's If the command is a If the command is a + +When the command is finished by SCSI mid-level, device handler's + + + + + The commands processing flow + + + +The commands processing functions + +scst_rx_cmd() + +

                            +Function +struct scst_cmd *scst_rx_cmd( + struct scst_session *sess, + const uint8_t *lun, + int lun_len, + const uint8_t *cdb, + int cdb_len, + int atomic) + + +Where: + + + + + +scst_cmd_init_done() + +

                            +Function +void scst_cmd_init_done( + struct scst_cmd *cmd, + enum scst_exec_context pref_context) + + +Where: + + + + + +scst_rx_data() + +

                            +Function +void scst_rx_data( + struct scst_cmd *cmd, + int status, + enum scst_exec_context pref_context) + + +Where: + + + + + +Parameter + + + +scst_tgt_cmd_done() + +

                            +Function +void scst_tgt_cmd_done( + struct scst_cmd *cmd, + enum scst_exec_context pref_context) + + +Where: + + + + +The commands processing context + +

                            +Execution context often is a major problem in the kernel drivers +development, because many contexts, like IRQ context, greatly limit +available functionality, therefore require additional complex code in +order to pass processing to more simple context. SCST does its best to +undertake most of the context handling. + +On the initialization time SCST creates for internal command processing +as many threads as there are processors in the system or specified by +user via +Directly, i.e. in the caller's context, without limitations +Directly atomically, i.e. with sleeping forbidden +In the SCST's internal threads +In the SCST's per processor tasklets + + +The target driver sets this context as pref_context parameter for SCST +functions. Additionally, target's template's Preferred context constants + +

                            +There are the following preferred context constants: + + + + + +SCST commands' processing states + +

                            +There are the following processing states, which a SCST command passes +through during execution and which could be returned by device handler's + + +tgt_dev/ assignment) state - -SCST's structures manipulation functions + +Task management functions

                            -Target drivers must not directly access any fields in SCST's structures, -they must use only described below functions. - -SCST target driver manipulation functions - -scst_tgt_get_tgt_specific() and scst_tgt_set_tgt_specific() - -

                            -Function -void *scst_tgt_get_tgt_specific( - struct scst_tgt *tgt) - - -Function -void scst_tgt_set_tgt_specific( - struct scst_tgt *tgt, - void *val) - - -Where: +There are the following task management functions supported: - -SCST session manipulation functions +All task management functions return completion status via +scst_sess_get_tgt_specific() and scst_sess_set_tgt_specific() +scst_rx_mgmt_fn_tag()

                            -Function -void *scst_sess_get_tgt_specific( - struct scst_session *sess) - - -Function -void scst_sess_set_tgt_specific( +int scst_rx_mgmt_fn_tag( struct scst_session *sess, - void *val) + int fn, + uint32_t tag, + int atomic, + void *tgt_priv) Where: - -SCST command manipulation functions +Returns 0 if the command was successfully created and scheduled for +execution, error code otherwise. On success, the completion status of +the command will be reported asynchronously via task_mgmt_fn_done() +driver's callback. -scst_cmd_atomic() +scst_rx_mgmt_fn_lun()

                            -Function -int scst_cmd_atomic( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_session() - -

                            -Function -struct scst_session *scst_cmd_get_session( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_resp_data_len() - -

                            -Function -unsigned int scst_cmd_get_resp_data_len( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_tgt_resp_flags() - -

                            -Function -int scst_cmd_get_tgt_resp_flags( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_buffer() - -

                            -Function -void *scst_cmd_get_buffer( - struct scst_cmd *cmd) - - -Where: - - - - -It is recommended to use scst_cmd_get_bufflen() - -

                            -Function -unsigned int scst_cmd_get_bufflen( - struct scst_cmd *cmd) - - -Where: - - - - -It is recommended to use scst_cmd_get_use_sg() - -

                            -Function -unsigned short scst_cmd_get_use_sg( - struct scst_cmd *cmd) - - -Where: - - - - -It is recommended to use scst_cmd_get_data_direction() - -

                            -Function -scst_data_direction scst_cmd_get_data_direction( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_status() - -

                            -Functions -uint8_t scst_cmd_get_status( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_masked_status() - -

                            -Functions -uint8_t scst_cmd_get_masked_status( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_msg_status() - -

                            -Functions -uint8_t scst_cmd_get_msg_status( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_host_status() - -

                            -Functions -uint16_t scst_cmd_get_host_status( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_driver_status() - -

                            -Functions -uint16_t scst_cmd_get_driver_status( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_sense_buffer() - -

                            -Functions -uint8_t *scst_cmd_get_sense_buffer( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_sense_buffer_len() - -

                            -Functions -int scst_cmd_get_sense_buffer_len( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_get_tag() and scst_cmd_set_tag() - -

                            Function -uint32_t scst_cmd_get_tag( - struct scst_cmd *cmd) - - -Function -void scst_cmd_set_tag( - struct scst_cmd *cmd, - uint32_t tag) - - -Where: - - - - -scst_cmd_get_tgt_specific() and scst_cmd_get_tgt_specific_lock() - -

                            -Functions -void *scst_cmd_get_tgt_specific( - struct scst_cmd *cmd) - - - -void *scst_cmd_get_tgt_specific_lock( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_set_tgt_specific() and scst_cmd_set_tgt_specific_lock() - -

                            -Functions -void *scst_cmd_set_tgt_specific( - struct scst_cmd *cmd, - void *val) - - - -void *scst_cmd_set_tgt_specific_lock( - struct scst_cmd *cmd, - void *val) - - -Where: - - - - -scst_cmd_get_data_buff_alloced() and scst_cmd_set_data_buff_alloced() - -

                            -Function -int scst_cmd_get_data_buff_alloced( - struct scst_cmd *cmd) - - -Function -void scst_cmd_set_data_buff_alloced( - struct scst_cmd *cmd) - - -Where: - - - - -scst_cmd_set_expected(), scst_cmd_is_expected_set(), -scst_cmd_get_expected_data_direction() and -scst_cmd_get_expected_transfer_len() - -

                            -Function -void scst_cmd_set_expected( - struct scst_cmd *cmd, - scst_data_direction expected_data_direction, - unsigned int expected_transfer_len) - - -Function -int scst_cmd_is_expected_set( - struct scst_cmd *cmd) - - -Function -scst_data_direction scst_cmd_get_expected_data_direction( - struct scst_cmd *cmd) - - -Function -unsigned int scst_cmd_get_expected_transfer_len( - struct scst_cmd *cmd) - - -Where: - - - - -scst_get_buf_first(), scst_get_buf_next(), -scst_put_buf() and scst_get_buf_count() - -

                            -These functions are designed to simplify and unify access to the -commands data (SG vector or plain data buffer) in all possible -conditions, including HIGHMEM environment, and should be used instead of -direct access. - -Function -int scst_get_buf_first( - struct scst_cmd *cmd, - uint8_t **buf) - - -Where: - - - - -Returns the length of the chunk of data for success, 0 for the end of -data, negative error code otherwise. - -Function -int scst_get_buf_next( - struct scst_cmd *cmd, - uint8_t **buf) - - -Where: - - - - -Returns the length of the chunk of data for success, 0 for the end of -data, negative error code otherwise. - -Function -void scst_put_buf( - struct scst_cmd *cmd, - uint8_t *buf) - - -Where: - - - - -Function -int scst_get_buf_count( - struct scst_cmd *cmd) - - -Where: - - - - -SCST task management commands manipulation functions - -scst_mgmt_cmd_get_tgt_specific() - -

                            -Function -void *scst_mgmt_cmd_get_tgt_specific( - struct scst_mgmt_cmd *mcmd) - - -Where: - - - - -scst_mgmt_cmd_get_status() - -

                            -Functions -void *scst_mgmt_cmd_get_status( - struct scst_mgmt_cmd *mcmd) - - -Where: - - - - -The following status values are possible: - - - - SCST_MGMT_STATUS_SUCCESS - the task management command completed -successfully - - SCST_MGMT_STATUS_FAILED - the task management command failed. - - - -Miscellaneous functions - -scst_find_cmd_by_tag() - -

                            -Function -struct scst_cmd *scst_find_cmd_by_tag( - struct scst_session *sess, - uint32_t tag) +int scst_rx_mgmt_fn_lun( + struct scst_session *sess, + int fn, + const uint8_t *lun, + int lun_len, + int atomic, + void *tgt_priv); Where: - -Returns found command or NULL otherwise. +Returns 0 if the command was successfully created and scheduled for +execution, error code otherwise. On success, the completion status of +the command will be reported asynchronously via task_mgmt_fn_done() +driver's callback. -scst_find_cmd() +Possible status constants which can be returned by + + + + +SGV cache

                            -Function -struct scst_cmd *scst_find_cmd( - struct scst_session *sess, - void *data, - int (*cmp_fn)(struct scst_cmd *cmd, void *data)) - - -Where: +SCST SGV cache is a memory management subsystem in SCST. One can call it +a "memory pool", but Linux kernel already have a mempool interface, +which serves different purposes. SGV cache provides to SCST core, target +drivers and backend dev handlers facilities to allocate, build and cache +SG vectors for data buffers. The main advantage of it is the caching +facility, when it doesn't free to the system each vector, which is not +used anymore, but keeps it for a while (possibly indefinitely) to let it +be reused by the next consecutive command. This allows to: - Reduce commands processing latencies and, hence, improve performance; - - + Make commands processing latencies predictable, which is essential + for RT applications. -Returns found command or NULL otherwise. +The freed SG vectors are kept by the SGV cache either for some (possibly +indefinite) time, or, optionally, until the system needs more memory and +asks to free some using the set_shrinker() interface. Also the SGV cache +allows to: - -SCST is designed in a such way that any command is always processed only -by one thread at any time, so no locking is necessary. But there is one -exception from that rule, it is Cluster pages together. "Cluster" means merging adjacent pages in a +single SG entry. It allows to have less SG entries in the resulting SG +vector, hence improve performance handling it as well as allow to +work with bigger buffers on hardware with limited SG capabilities. -scst_get_cdb_info() + Set custom page allocator functions. For instance, scst_user device +handler uses this facility to eliminate unneeded mapping/unmapping of +user space pages and avoid unneeded IOCTL calls for buffers allocations. +In fileio_tgt application, which uses a regular malloc() function to +allocate data buffers, this facility allows ~30% less CPU load and +considerable performance increase. + + Prevent each initiator or all initiators altogether to allocate too +much memory and DoS the target. Consider 10 initiators, which can have +access to 10 devices each. Any of them can queue up to 64 commands, each +can transfer up to 1MB of data. So, all of them in a peak can allocate +up to 10*10*64 = ~6.5GB of memory for data buffers. This amount must be +limited somehow and the SGV cache performs this function. + + + + Implementation

                            -Function -int scst_get_cdb_info( - const uint8_t *cdb_p, - int dev_type, - struct scst_info_cdb *info_p) - + -Where: + With fixed size buffers. - + With a set of power 2 size buffers. In this mode each SGV cache +(struct sgv_pool) has SGV_POOL_ELEMENTS (11 currently) of kmem caches. +Each of those kmem caches keeps SGV cache objects (struct sgv_pool_obj) +corresponding to SG vectors with size of order X pages. For instance, +request to allocate 4 pages will be served from kmem cache[2&rsqb, since the +order of the of number of requested pages is 2. If later request to +allocate 11KB comes, the same SG vector with 4 pages will be reused (see +below). This mode is in average allows less memory overhead comparing +with the fixed size buffers mode. - - +In both modes, if size of a request exceeds the maximum allowed for +caching buffer size, the requested buffer will be allocated, but not +cached. -Returns 0 on success, -1 otherwise. +Freed cached sgv_pool_obj objects are actually freed to the system +either by the purge work, which is scheduled once in 60 seconds, or in +sgv_shrink() called by system, when it's asking for memory. -scst_to_dma_dir() + Interface + + sgv_pool *sgv_pool_create()

                            -Function -int scst_to_dma_dir( - int scst_dir) +struct sgv_pool *sgv_pool_create( + const char *name, + enum sgv_clustering_types clustered, int single_alloc_pages, + bool shared, int purge_interval) -Where: +This function creates and initializes an SGV cache. It has the following +arguments: - + + + + 0, then the SGV cache will work in the + fixed size buffers mode. In this case single_alloc_pages sets the + size of each buffer in pages. + + -Returns the corresponding scst_is_cmd_local() + void sgv_pool_del()

                            -Function -int scst_is_cmd_local( - struct scst_cmd *cmd) +void sgv_pool_del( + struct sgv_pool *pool) -Where: +This function deletes the corresponding SGV cache. If the cache is +shared, it will decrease its reference counter. If the reference counter +reaches 0, the cache will be destroyed. - - - -Returns 1, if the command's CDB is locally handled by SCST or 0 -otherwise - -scst_register_virtual_device() and scst_unregister_virtual_device() + void sgv_pool_flush()

                            -These functions provide a way for device handlers to register a virtual -(emulated) device, which will be visible only by remote initiators. For -example, FILEIO device handler uses files on file system to makes from -them virtual remotely available SCSI disks. - -Function -int scst_register_virtual_device( - struct scst_dev_type *dev_handler) +void sgv_pool_flush( + struct sgv_pool *pool) -Where: +This function flushes, i.e. frees, all the cached entries in the SGV +cache. - - - -Returns assigned to the device ID on success, or negative value otherwise. - -Function -void scst_unregister_virtual_device( - int id) - - -Where: - - - - -scst_add_threads() and scst_del_threads() + void sgv_pool_set_allocator()

                            -These functions allows to add or delete some SCST threads. For example, -if -int scst_add_threads( - int num) +void sgv_pool_set_allocator( + struct sgv_pool *pool, + struct page *(*alloc_pages_fn)(struct scatterlist *sg, gfp_t gfp, void *priv), + void (*free_pages_fn)(struct scatterlist *sg, int sg_count, void *priv)); -Where: +This function allows to set for the SGV cache a custom pages allocator. For +instance, scst_user uses such function to supply to the cache mapped from +user space pages. + + - -Returns 0 on success, error code otherwise. +This function should return the allocated page or NULL, if no page was +allocated. -Function -void scst_del_threads( - int num) - - -Where: + - -scst_proc_get_tgt_root() + struct scatterlist *sgv_pool_alloc()

                            -Function -struct proc_dir_entry *scst_proc_get_tgt_root( - struct scst_tgt_template *vtt) +struct scatterlist *sgv_pool_alloc( + struct sgv_pool *pool, + unsigned int size, + gfp_t gfp_mask, + int flags, + int *count, + struct sgv_pool_obj **sgv, + struct scst_mem_lim *mem_lim, + void *priv) -Where: +This function allocates an SG vector from the SGV cache. It has the +following parameters: - + + + + -Returns proc_dir_entry on success, NULL otherwise. +This function returns pointer to the resulting SG vector or NULL in case +of any error. -scst_proc_get_dev_type_root() + void sgv_pool_free()

                            -Function -struct proc_dir_entry *scst_proc_get_dev_type_root( - struct scst_dev_type *dtt) +void sgv_pool_free( + struct sgv_pool_obj *sgv, + struct scst_mem_lim *mem_lim) -Where: +This function frees previously allocated SG vector, referenced by SGV +cache object sgv. + + void *sgv_get_priv(struct sgv_pool_obj *sgv) + +

                            + +void *sgv_get_priv( + struct sgv_pool_obj *sgv) + + +This function allows to get the allocation private data for this SGV +cache object sgv. The private data are set by sgv_pool_alloc(). + + void scst_init_mem_lim() + +

                            + +void scst_init_mem_lim( + struct scst_mem_lim *mem_lim) + + +This function initializes memory limits structure mem_lim according to +the current system configuration. This structure should be latter used +to track and limit allocated by one or more SGV caches memory. + + + Runtime information and statistics. + +

                            + SGV cache runtime information and statistics is available in +/proc/scsi_tgt/sgv. + + + Target driver qla2x00t + +

                            +Target driver qla2x00t allows to use QLogic 2xxx based adapters in +the target (server) mode. + +It consists from two parts: - -Returns proc_dir_entry on success, NULL otherwise. +The initiator driver qla2xxx was changed to: + + + + To provide support for the target mode add-on via a set of +exported callbacks + + To provide extra info and management interface in the driver's +sysfs interface (attributes target_mode_enabled, ports_database, etc.) + + To fix some problems uncovered during target mode development and +usage. + + + +The changes are relatively small (few thousands lines big patch) and local. + +The changed qla2xxx is still capable to work as initiator only. Mode, +when a host acts as initiator and target simultaneously, is supported as +well. + +Since firmware interface for 24xx+ chips is fundamentally different from +earlier versions, qla2x00t generally contains 2 separate drivers sharing +some common processing. + + Driver initialization + +

                            +On initialization, qla2x00tgt registers its SCST template tgt2x_template +in the SCST core. Then during template registration SCST core calls +detect() callback which is function q2t_target_detect(). + +In this function qla2x00tgt registers its callbacks in qla2xxx by +calling qla2xxx_tgt_register_driver(). Qla2xxx_tgt_register_driver() +stores pointer to the being registered callbacks in variable qla_target. + +Then q2t_target_detect() calls qla2xxx_add_targets(), which calls for +each known local FC port (HBA instance) qla_target.tgt_host_action() +callback with ADD_TARGET action. Then q2t_host_action() calls +q2t_add_target() which registers SCST target for this FC port. + +If later a new FC port is hot added, qla2x00_probe_one() will also call +for all new local ports qla_target.tgt_host_action() with ADD_TARGET +action. + + + Driver unload + +

                            +When a local FC port is being removed, the Linux kernel calls +qla2x00_remove_one(), which then qla_target.tgt_host_action() with +REMOVE_TARGET action. + +Then q2t_host_action() calls q2t_remove_target(), which unregisters the +corresponding SCST target in SCST. During unregistration SCST core calls +release() callback of tgt2x_template, which is q2t_target_release(). + +Then q2t_target_release() calls q2t_target_stop(). Then +q2t_target_stop() marks this target as stopped by setting flag tgt_stop. +When this flag is set, all incoming from initiators commands are +refused. + +Then q2t_target_stop() schedules deletion of all sessions of the target. + +Then q2t_target_stop() waits until all outstanding commands finished and +sessions deleted. + +Then q2t_target_stop(), if necessary, calls qla2x00_disable_tgt_mode() +to disables target mode, which disables target mode of the corresponding +HBA and resets it. Then qla2x00_disable_tgt_mode() waits until reset +finished. + +Then q2t_target_stop() returns and then q2t_target_release() frees the +target. + + +If module qla2x00tgt is being unloaded, q2t_exit() at first takes +q2t_unreg_rwsem on writing. Taking it is necessary to make sure that +q2t_host_action() will not be active during qla2x00tgt unload. + +Then q2t_exit() calls scst_unregister_target_template() for +tgt2x_template, which then in a loop will unregister all QLA SCST targets +from SCST as described above. + + + Enabling target mode + +

                            +When command to enable target mode received, +qla_target.tgt_host_action() with action ENABLE_TARGET_MODE called. Then +q2t_host_action() goes over all discovered remote of the being enabled +target and adds SCST sessions for all them. + +Then it calls qla2x00_enable_tgt_mode(), which enables target mode of +the corresponding HBA and resets it. Then qla2x00_enable_tgt_mode() +waits until reset finished. + +During reset firmware initialization functions detect that target mode +is enables and initialize the firmware accordingly. + + + Disabling target mode + +

                            +When command to disable target mode received, +qla_target.tgt_host_action() with action DISABLE_TARGET_MODE called. Then +q2t_host_action() calls q2t_target_stop(), which processes as describe above. + + + SCST sessions management + +

                            +As required by SCSI and FC standards, each remote initiator FC port +has the corresponding SCST session. + +Since qla2xxx is not intended to strictly maintain database of remote +initiator FC ports as it is needed for target mode, qla2x00t uses mixed +approach for SCST sessions management, when both qla2xxx and QLogic +firmware generate events and information about currently active remote +FC ports. + +Remote FC ports management also has to handle changing FC and loop IDs +after fabric events, so it needs to constantly monitor FC and loop IDs +of the registered FC ports. This is implemented by checks in +q2t_create_sess() that being registered FC port already has SCST session +and q2t_check_fcport_exist() in q2t_del_sess_work_fn(). See below for +more info. + +Interaction with qla2xxx is implemented using tgt_fc_port_added() and +tgt_fc_port_deleted() qla_target's callbacks. + +Callback tgt_fc_port_added() called by qla2xxx when the target driver +detects new remote FC port. Assigned to it q2t_fc_port_added() checks if +an SCST session already exists for this remote FC port and, if not, +creates it. + +Callback tgt_fc_port_deleted() called by qla2xxx when it deletes a +remote FC port from its database. Assigned to it q2t_fc_port_deleted() +checks if an SCST session already exists for this remote FC port and, if +yes, schedules it for deletion. + +Driver qla2x00tgt has 2 types of SCST sessions: local and not local. +Sessions created by q2t_fc_port_added() are not local. Local sessions +created if qla2x00tgt receives a command from remote initiator for which +there is no know remote FC port and, hence, SCST session. Local sessions +are created in tgt->sess_work (q2t_sess_work_fn()) by calling +q2t_make_local_sess(). All received from remote initiators commands for +local sessions are delayed until the sessions are created. + +To minimize affecting initiators by FC fabric events, qla2x00tgt doesn't +immediately delete SCST sessions scheduled for deletion, but instead +delay them for some time. If during this time a command from an unknown +remote initiator received, q2t_make_local_sess()/q2t_create_sess() at +first check if a session for this initiator already exists and, if yes, +undelete then reuse it after updating its s_id and loop_id to new values. + +If a session not reused during the delete delay time, then +q2t_del_sess_work_fn() asks the firmware internal database if it knows +the corresponding remote FC port. If yes, then this session is undeleted +and its s_id and loop_id updated to new values. If no, the session is +deleted. + + + Handling stuck commands + +

                            +Driver qla2x00tgt defines in tgt2x_template callback +on_hw_pending_cmd_timeout for handling stuck commands in +q2t_on_hw_pending_cmd_timeout() function, with max_hw_pending_time +timeout set Q2T_MAX_HW_PENDING_TIME (60 seconds). If the firmware +doesn't return reply for one or more IOCBs for the corresponding SCST +command, SCST core calls this callback. + +In this callback all the stuck commands are forcibly finished. + + + + Debugging and troubleshooting + +

                            +SCST core and its drivers provide excessive debugging and logging +facilities suitable to catch and analyze problems of virtually any level +of complexity. + +Depending from amount debugging and logging facilities available, there +are 3 types of builds: + + + + + +Switch between build modes is done by calling "make x2y", where "x" - +current build mode and "y" - desired build mode. For instance, to switch +from release to debug mode you should run "make release2debug". + + Logging levels management + +

                            +Logging levels management is done using "trace_level" file located in the +driver's proc interface subdirectory. Each SCST driver has it, except in +the perf build mode. For instance, for SCST core it's located in +/proc/scsi_tgt/. For qla2x00t it's located in /proc/scsi_tgt/qla2x00tgt/. + +Reading from it you can find currently enabled logging levels. + +You can change them by writing in this file, like: + +# echo "add scsi" >/proc/scsi_tgt/trace_level + +The following commands are available: + + + + + +The following trace levels are common for all drivers: + + + + + +The following trace levels are additionally available for SCST core: + + + + + + Preparing a debug kernel + +

                            +SCST logging can produce huge amount of logging, which default kernel +configuration can't cope with, so it needs some extra adjustments. + +For that you should change in lib/Kconfig.debug or init/Kconfig +depending from your kernel version LOG_BUF_SHIFT from "12 21" to "12 25". + +Then you should in your .config set CONFIG_LOG_BUF_SHIFT to 25. + +Also, Linux kernel has a lot of helpful debug facilities, like lockdep, +which allows to catch various deadlocks, or memory allocation debugging. +It is recommended to enable them during SCST debugging. + +The following options are recommended to be enabled (available depending +from your kernel version): CONFIG_SLUB_DEBUG, CONFIG_PRINTK_TIME, +CONFIG_MAGIC_SYSRQ, CONFIG_DEBUG_FS, CONFIG_DEBUG_KERNEL, +CONFIG_DEBUG_SHIRQ, CONFIG_DETECT_SOFTLOCKUP, CONFIG_DETECT_HUNG_TASK, +CONFIG_SLUB_DEBUG_ON, CONFIG_SLUB_STATS, CONFIG_DEBUG_PREEMPT, +CONFIG_DEBUG_RT_MUTEXES, CONFIG_DEBUG_PI_LIST, CONFIG_DEBUG_SPINLOCK, +CONFIG_DEBUG_MUTEXES, CONFIG_DEBUG_LOCK_ALLOC, CONFIG_PROVE_LOCKING, +CONFIG_LOCKDEP, CONFIG_LOCK_STAT, CONFIG_DEBUG_SPINLOCK_SLEEP, +CONFIG_STACKTRACE, CONFIG_DEBUG_BUGVERBOSE, CONFIG_DEBUG_VM, +CONFIG_DEBUG_VIRTUAL, CONFIG_DEBUG_WRITECOUNT, CONFIG_DEBUG_MEMORY_INIT, +CONFIG_DEBUG_LIST, CONFIG_DEBUG_SG, CONFIG_DEBUG_NOTIFIERS, +CONFIG_FRAME_POINTER, CONFIG_FAULT_INJECTION, CONFIG_FAILSLAB, +CONFIG_FAIL_PAGE_ALLOC, CONFIG_FAIL_MAKE_REQUEST, +CONFIG_FAIL_IO_TIMEOUT, CONFIG_FAULT_INJECTION_DEBUG_FS, +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER. + + Preparing logging subsystem + +

                            +It is recommended that you system logger daemon on the target configured: + + + + To store kernel logs in separate files on the fastest disk you +have. It will be better if this disk is dedicated for logging or, at +least, doesn't contain your LUNs data. + + To write the kernel logs to the disk in asynchronous manner, i.e. +without calling fsync() after each written message. Usually, you can +achieve it, if you add a '-' sign before the corresponding file path in +your syslog daemon conf file, like: + +kern.* -/var/log/kern.log + + + + Decoding OOPS messages + +

                            +You can decode an OOPS message to the corresponding line in C file +using gdb "l" command. For example, an OOPS message has a line: + + +[<ffffffff88646174>&rsqb :iscsi_scst:iscsi_extracheck_is_rd_thread+0x94/0xb0 + + +You can decode it by: + + +$ gdb iscsi-scst.ko +(gdb) l *iscsi_scst:iscsi_extracheck_is_rd_thread+0x94 + + +For that the corresponding module (iscsi-scst.ko) should be build with +debug info. But modules not always have debug info built-in. To +workaround it you can add "-g" flag in the corresponding Makefile +(without changing anything else!) or enable in .config using "make +menuconfig" building kernel with debug info. Then rebuild only the .o +file you need. + +For instance, to decode OOPS in mm/filemap.c in the kernel you need +enable in .config building kernel with debug info and then run: + + +$ make mm/filemap.o +... +$ gdb mm/filemap.o + diff --git a/doc/sgv_cache.sgml b/doc/sgv_cache.sgml deleted file mode 100644 index 4d6240031..000000000 --- a/doc/sgv_cache.sgml +++ /dev/null @@ -1,335 +0,0 @@ - - -

                            - - -SCST SGV cache description - - - - Vladislav Bolkhovitin - - -Version 2.1.0 - - - -Introduction - -

                            -SCST SGV cache is a memory management subsystem in SCST. One can call it -a "memory pool", but Linux kernel already have a mempool interface, -which serves different purposes. SGV cache provides to SCST core, target -drivers and backend dev handlers facilities to allocate, build and cache -SG vectors for data buffers. The main advantage of it is the caching -facility, when it doesn't free to the system each vector, which is not -used anymore, but keeps it for a while (possibly indefinitely) to let it -be reused by the next consecutive command. This allows to: - - - - Reduce commands processing latencies and, hence, improve performance; - - Make commands processing latencies predictable, which is essential - for RT applications. - - - -The freed SG vectors are kept by the SGV cache either for some (possibly -indefinite) time, or, optionally, until the system needs more memory and -asks to free some using the set_shrinker() interface. Also the SGV cache -allows to: - - - - Cluster pages together. "Cluster" means merging adjacent pages in a -single SG entry. It allows to have less SG entries in the resulting SG -vector, hence improve performance handling it as well as allow to -work with bigger buffers on hardware with limited SG capabilities. - - Set custom page allocator functions. For instance, scst_user device -handler uses this facility to eliminate unneeded mapping/unmapping of -user space pages and avoid unneeded IOCTL calls for buffers allocations. -In fileio_tgt application, which uses a regular malloc() function to -allocate data buffers, this facility allows ~30% less CPU load and -considerable performance increase. - - Prevent each initiator or all initiators altogether to allocate too -much memory and DoS the target. Consider 10 initiators, which can have -access to 10 devices each. Any of them can queue up to 64 commands, each -can transfer up to 1MB of data. So, all of them in a peak can allocate -up to 10*10*64 = ~6.5GB of memory for data buffers. This amount must be -limited somehow and the SGV cache performs this function. - - - - Implementation - -

                            -From implementation POV the SGV cache is a simple extension of the kmem -cache. It can work in 2 modes: - - - - With fixed size buffers. - - With a set of power 2 size buffers. In this mode each SGV cache -(struct sgv_pool) has SGV_POOL_ELEMENTS (11 currently) of kmem caches. -Each of those kmem caches keeps SGV cache objects (struct sgv_pool_obj) -corresponding to SG vectors with size of order X pages. For instance, -request to allocate 4 pages will be served from kmem cache[2&rsqb, since the -order of the of number of requested pages is 2. If later request to -allocate 11KB comes, the same SG vector with 4 pages will be reused (see -below). This mode is in average allows less memory overhead comparing -with the fixed size buffers mode. - - - -Consider how the SGV cache works in the set of buffers mode. When a -request to allocate new SG vector comes, sgv_pool_alloc() via -sgv_get_obj() checks if there is already a cached vector with that -order. If yes, then that vector will be reused and its length, if -necessary, will be modified to match the requested size. In the above -example request for 11KB buffer, 4 pages vector will be reused and -modified using trans_tbl to contain 3 pages and the last entry will be -modified to contain the requested length - 2*PAGE_SIZE. If there is no -cached object, then a new sgv_pool_obj will be allocated from the -corresponding kmem cache, chosen by the order of number of requested -pages. Then that vector will be filled by pages and returned. - -In the fixed size buffers mode the SGV cache works similarly, except -that it always allocate buffer with the predefined fixed size. I.e. -even for 4K request the whole buffer with predefined size, say, 1MB, -will be used. - -In both modes, if size of a request exceeds the maximum allowed for -caching buffer size, the requested buffer will be allocated, but not -cached. - -Freed cached sgv_pool_obj objects are actually freed to the system -either by the purge work, which is scheduled once in 60 seconds, or in -sgv_shrink() called by system, when it's asking for memory. - - Interface - - sgv_pool *sgv_pool_create() - -

                            - -struct sgv_pool *sgv_pool_create( - const char *name, - enum sgv_clustering_types clustered, int single_alloc_pages, - bool shared, int purge_interval) - - -This function creates and initializes an SGV cache. It has the following -arguments: - - - - - - - - 0, then the SGV cache will work in the - fixed size buffers mode. In this case single_alloc_pages sets the - size of each buffer in pages. - - - -Returns the resulting SGV cache or NULL in case of any error. - - void sgv_pool_del() - -

                            - -void sgv_pool_del( - struct sgv_pool *pool) - - -This function deletes the corresponding SGV cache. If the cache is -shared, it will decrease its reference counter. If the reference counter -reaches 0, the cache will be destroyed. - - void sgv_pool_flush() - -

                            - -void sgv_pool_flush( - struct sgv_pool *pool) - - -This function flushes, i.e. frees, all the cached entries in the SGV -cache. - - void sgv_pool_set_allocator() - -

                            - -void sgv_pool_set_allocator( - struct sgv_pool *pool, - struct page *(*alloc_pages_fn)(struct scatterlist *sg, gfp_t gfp, void *priv), - void (*free_pages_fn)(struct scatterlist *sg, int sg_count, void *priv)); - - -This function allows to set for the SGV cache a custom pages allocator. For -instance, scst_user uses such function to supply to the cache mapped from -user space pages. - - - - - -This function should return the allocated page or NULL, if no page was -allocated. - - - - - - - struct scatterlist *sgv_pool_alloc() - -

                            - -struct scatterlist *sgv_pool_alloc( - struct sgv_pool *pool, - unsigned int size, - gfp_t gfp_mask, - int flags, - int *count, - struct sgv_pool_obj **sgv, - struct scst_mem_lim *mem_lim, - void *priv) - - -This function allocates an SG vector from the SGV cache. It has the -following parameters: - - - - - - - - - -This function returns pointer to the resulting SG vector or NULL in case -of any error. - - void sgv_pool_free() - -

                            - -void sgv_pool_free( - struct sgv_pool_obj *sgv, - struct scst_mem_lim *mem_lim) - - -This function frees previously allocated SG vector, referenced by SGV -cache object sgv. - - void *sgv_get_priv(struct sgv_pool_obj *sgv) - -

                            - -void *sgv_get_priv( - struct sgv_pool_obj *sgv) - - -This function allows to get the allocation private data for this SGV -cache object sgv. The private data are set by sgv_pool_alloc(). - - void scst_init_mem_lim() - -

                            - -void scst_init_mem_lim( - struct scst_mem_lim *mem_lim) - - -This function initializes memory limits structure mem_lim according to -the current system configuration. This structure should be latter used -to track and limit allocated by one or more SGV caches memory. - - - Runtime information and statistics. - -

                            -Runtime information and statistics is available in /sys/kernel/scst_tgt/sgv. - -

                            diff --git a/fcst/ft_sess.c b/fcst/ft_sess.c index 1690a87d1..81caf8bae 100644 --- a/fcst/ft_sess.c +++ b/fcst/ft_sess.c @@ -181,7 +181,9 @@ static u32 ft_sess_hash(u32 port_id) 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) + !defined(CONFIG_SUSE_KERNEL) && \ + (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 || \ + (RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 6)) /* * 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). diff --git a/ibmvstgt/src/Kconfig b/ibmvstgt/src/Kconfig index bbf91aec6..f3f71155e 100644 --- a/ibmvstgt/src/Kconfig +++ b/ibmvstgt/src/Kconfig @@ -206,7 +206,7 @@ config SCSI_MULTI_LUN mobile phone in mass storage mode. This option forces the kernel to probe for all LUNs by default. This setting can be overriden by max_luns boot/module parameter. Note that this option does not affect - devices conforming to SCSI-3 or higher as they can explicitely report + devices conforming to SCSI-3 or higher as they can explicitly report their number of LUNs. It is safe to say Y here unless you have one of those rare devices which reacts in an unexpected way when probed for multiple LUNs. diff --git a/ibmvstgt/src/orig/2.6.35/Kconfig b/ibmvstgt/src/orig/2.6.35/Kconfig index 75f233680..d07f508d1 100644 --- a/ibmvstgt/src/orig/2.6.35/Kconfig +++ b/ibmvstgt/src/orig/2.6.35/Kconfig @@ -206,7 +206,7 @@ config SCSI_MULTI_LUN mobile phone in mass storage mode. This option forces the kernel to probe for all LUNs by default. This setting can be overriden by max_luns boot/module parameter. Note that this option does not affect - devices conforming to SCSI-3 or higher as they can explicitely report + devices conforming to SCSI-3 or higher as they can explicitly report their number of LUNs. It is safe to say Y here unless you have one of those rare devices which reacts in an unexpected way when probed for multiple LUNs. diff --git a/ibmvstgt/src/orig/2.6.36/Kconfig b/ibmvstgt/src/orig/2.6.36/Kconfig index bbf91aec6..f3f71155e 100644 --- a/ibmvstgt/src/orig/2.6.36/Kconfig +++ b/ibmvstgt/src/orig/2.6.36/Kconfig @@ -206,7 +206,7 @@ config SCSI_MULTI_LUN mobile phone in mass storage mode. This option forces the kernel to probe for all LUNs by default. This setting can be overriden by max_luns boot/module parameter. Note that this option does not affect - devices conforming to SCSI-3 or higher as they can explicitely report + devices conforming to SCSI-3 or higher as they can explicitly report their number of LUNs. It is safe to say Y here unless you have one of those rare devices which reacts in an unexpected way when probed for multiple LUNs. diff --git a/iscsi-scst/README b/iscsi-scst/README index ad1ff5bdc..db82baa7e 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -413,7 +413,7 @@ echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled Below is an advanced sample script, which configures more virtual devices of various types, including virtual CDROM and 2 targets, one with all default parameters, another one with some not default -parameters, incoming and outgoing user names for CHAP authentification, +parameters, incoming and outgoing user names for CHAP authentication, and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520, which will see another set of devices. Also this sample configures CHAP authentication for discovery sessions and iSNS server with access diff --git a/iscsi-scst/README_in-tree b/iscsi-scst/README_in-tree index e76287e91..db41e2db6 100644 --- a/iscsi-scst/README_in-tree +++ b/iscsi-scst/README_in-tree @@ -242,7 +242,7 @@ echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled Below is an advanced sample script, which configures more virtual devices of various types, including virtual CDROM and 2 targets, one with all default parameters, another one with some not default -parameters, incoming and outgoing user names for CHAP authentification, +parameters, incoming and outgoing user names for CHAP authentication, and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520, which will see another set of devices. Also this sample configures CHAP authentication for discovery sessions and iSNS server with access diff --git a/iscsi-scst/doc/SCST_Gentoo_HOWTO.txt b/iscsi-scst/doc/SCST_Gentoo_HOWTO.txt index cee8e5026..a2549013a 100644 --- a/iscsi-scst/doc/SCST_Gentoo_HOWTO.txt +++ b/iscsi-scst/doc/SCST_Gentoo_HOWTO.txt @@ -40,7 +40,6 @@ work. cd /usr/src/linux-2.6.39-gentoo-r3 patch -p1 < /root/scst/iscsi-scst/kernel/patches/put_page_callback-2.6.39.patch - patch -p1 < /root/scst/scst/kernel/scst_exec_req_fifo-2.6.39.patch make clean diff --git a/iscsi-scst/doc/iscsi-scst-howto.txt b/iscsi-scst/doc/iscsi-scst-howto.txt index 97271f74f..7b9b5fffc 100644 --- a/iscsi-scst/doc/iscsi-scst-howto.txt +++ b/iscsi-scst/doc/iscsi-scst-howto.txt @@ -21,7 +21,6 @@ the example below): cd /usr/src/kernels/linux-2.6.38.8 patch -p1 < $HOME/scst/iscsi-scst/kernel/patches/put_page_callback-2.6.38.patch - patch -p1 < $HOME/scst/scst/kernel/scst_exec_req_fifo-2.6.38.patch make clean Next, build and install the kernel: diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index b51a43293..3ae4e0567 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -339,7 +339,7 @@ static int add_conn(void __user *ptr) session = session_lookup(target, info.sid); if (!session) { PRINT_ERROR("Session %lld not found", - (long long unsigned int)info.tid); + (unsigned long long int)info.tid); err = -ENOENT; goto out_unlock; } @@ -383,7 +383,7 @@ static int del_conn(void __user *ptr) session = session_lookup(target, info.sid); if (!session) { PRINT_ERROR("Session %llx not found", - (long long unsigned int)info.sid); + (unsigned long long int)info.sid); err = -ENOENT; goto out_unlock; } diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 5b1865981..b35e03c30 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -533,7 +533,7 @@ static void conn_rsp_timer_fn(unsigned long arg) "%s (SID %llx), closing connection", iscsi_get_timeout(cmnd)/HZ, conn->session->initiator_name, - (long long unsigned int) + (unsigned long long int) conn->session->sid); /* * We must call mark_conn_closed() outside of @@ -764,13 +764,13 @@ static int conn_setup_sock(struct iscsi_conn *conn) mm_segment_t oldfs; struct iscsi_session *session = conn->session; - TRACE_DBG("%llx", (long long unsigned int)session->sid); + TRACE_DBG("%llx", (unsigned long long int)session->sid); conn->sock = SOCKET_I(conn->file->f_dentry->d_inode); if (conn->sock->ops->sendpage == NULL) { PRINT_ERROR("Socket for sid %llx doesn't support sendpage()", - (long long unsigned int)session->sid); + (unsigned long long int)session->sid); res = -EINVAL; goto out; } @@ -809,7 +809,7 @@ void conn_free(struct iscsi_conn *conn) TRACE_ENTRY(); TRACE_MGMT_DBG("Freeing conn %p (sess=%p, %#Lx %u)", conn, - session, (long long unsigned int)session->sid, conn->cid); + session, (unsigned long long int)session->sid, conn->cid); lockdep_assert_held(&conn->target->target_mutex); @@ -925,7 +925,7 @@ int iscsi_conn_alloc(struct iscsi_session *session, } TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn, - (long long unsigned int)session->sid, info->cid); + (unsigned long long int)session->sid, info->cid); conn->transport = t; diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 7e13fffbd..4a8115526 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -2374,8 +2374,8 @@ static int cmnd_abort_pre_checks(struct iscsi_cmnd *req, int *status) if (req_hdr->lun != hdr->lun) { PRINT_ERROR("ABORT TASK: LUN mismatch: req LUN " "%llx, cmd LUN %llx, rtt %u", - (long long unsigned)be64_to_cpu(req_hdr->lun), - (long long unsigned)be64_to_cpu(hdr->lun), + (unsigned long long)be64_to_cpu(req_hdr->lun), + (unsigned long long)be64_to_cpu(hdr->lun), req_hdr->rtt); *status = ISCSI_RESPONSE_FUNCTION_REJECTED; goto out_put; diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 50bbdee44..c89880f85 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -426,7 +426,7 @@ struct iscsi_cmnd { /* * Used only to abort not yet sent responses. Usage in * cmnd_done() is only a side effect to have a lockless - * accesss to this list from always only a single thread + * access to this list from always only a single thread * at any time. So, all responses live in the parent * until it has the last reference put. */ diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 2b6c03e86..1da884f36 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -1336,7 +1336,7 @@ retry: count, &off); set_fs(oldfs); TRACE_WRITE("sid %#Lx, cid %u, res %d, iov_len %zd", - (long long unsigned int)conn->session->sid, + (unsigned long long int)conn->session->sid, conn->cid, res, iop->iov_len); if (unlikely(res <= 0)) { if (res == -EAGAIN) { @@ -1467,7 +1467,7 @@ retry2: "index %lu, offset %u, size %u, cmd %p, " "page %p)", (sendpage != sock_no_sendpage) ? "sendpage" : "sock_no_sendpage", - (long long unsigned int)conn->session->sid, + (unsigned long long int)conn->session->sid, conn->cid, res, page->index, offset, size, write_cmnd, page); if (unlikely(res <= 0)) { @@ -1553,7 +1553,7 @@ out_err: { #endif PRINT_ERROR("error %d at sid:cid %#Lx:%u, cmnd %p", res, - (long long unsigned int)conn->session->sid, + (unsigned long long int)conn->session->sid, conn->cid, conn->write_cmnd); } if (ref_cmd_to_parent && diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.17.patch b/iscsi-scst/kernel/patches/put_page_callback-3.17.patch new file mode 100644 index 000000000..b049270ea --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.17.patch @@ -0,0 +1,364 @@ +=== modified file 'drivers/block/drbd/drbd_receiver.c' +--- old/drivers/block/drbd/drbd_receiver.c 2014-11-21 03:17:49 +0000 ++++ new/drivers/block/drbd/drbd_receiver.c 2014-11-21 03:51:00 +0000 +@@ -132,7 +132,7 @@ static int page_chain_free(struct page * + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; + +=== modified file 'include/linux/mm_types.h' +--- old/include/linux/mm_types.h 2014-11-21 03:17:49 +0000 ++++ new/include/linux/mm_types.h 2014-11-21 03:51:00 +0000 +@@ -196,6 +196,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops + +=== modified file 'include/linux/net.h' +--- old/include/linux/net.h 2014-11-21 03:17:49 +0000 ++++ new/include/linux/net.h 2014-11-21 03:51:00 +0000 +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -285,6 +286,45 @@ int kernel_sendpage(struct socket *sock, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + + +=== modified file 'include/linux/skbuff.h' +--- old/include/linux/skbuff.h 2014-11-21 03:17:49 +0000 ++++ new/include/linux/skbuff.h 2014-11-21 03:51:00 +0000 +@@ -2145,7 +2145,7 @@ static inline struct page *skb_frag_page + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -2168,7 +2168,7 @@ static inline void skb_frag_ref(struct s + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** + +=== modified file 'net/Kconfig' +--- old/net/Kconfig 2014-11-21 03:17:49 +0000 ++++ new/net/Kconfig 2014-11-21 03:51:00 +0000 +@@ -75,6 +75,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + +=== modified file 'net/ceph/pagevec.c' +--- old/net/ceph/pagevec.c 2014-11-21 03:17:49 +0000 ++++ new/net/ceph/pagevec.c 2014-11-21 03:51:00 +0000 +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page ** + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + if (is_vmalloc_addr(pages)) + vfree(pages); + +=== modified file 'net/core/skbuff.c' +--- old/net/core/skbuff.c 2014-11-21 03:17:49 +0000 ++++ new/net/core/skbuff.c 2014-11-21 03:51:00 +0000 +@@ -426,7 +426,7 @@ struct sk_buff *__netdev_alloc_skb(struc + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -484,7 +484,7 @@ static void skb_clone_fraglist(struct sk + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -808,7 +808,7 @@ int skb_copy_ubufs(struct sk_buff *skb, + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1655,7 +1655,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1708,7 +1708,7 @@ static bool spd_fill_page(struct splice_ + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2167,7 +2167,7 @@ skb_zerocopy(struct sk_buff *to, struct + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } +@@ -2821,7 +2821,7 @@ int skb_append_datato_frags(struct sock + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); + +=== modified file 'net/core/sock.c' +--- old/net/core/sock.c 2014-11-21 03:17:49 +0000 ++++ new/net/core/sock.c 2014-11-21 03:51:00 +0000 +@@ -1881,7 +1881,7 @@ bool skb_page_frag_refill(unsigned int s + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + pfrag->offset = 0; +@@ -2645,7 +2645,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + + +=== modified file 'net/ipv4/Makefile' +--- old/net/ipv4/Makefile 2014-11-21 03:17:49 +0000 ++++ new/net/ipv4/Makefile 2014-11-21 03:51:00 +0000 +@@ -54,6 +54,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah. + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o xfrm4_protocol.o + +=== modified file 'net/ipv4/ip_output.c' +--- old/net/ipv4/ip_output.c 2014-11-21 03:17:49 +0000 ++++ new/net/ipv4/ip_output.c 2014-11-21 03:51:00 +0000 +@@ -1051,7 +1051,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1276,7 +1276,7 @@ ssize_t ip_append_page(struct sock *sk, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; + +=== modified file 'net/ipv4/tcp.c' +--- old/net/ipv4/tcp.c 2014-11-21 03:17:49 +0000 ++++ new/net/ipv4/tcp.c 2014-11-21 03:51:00 +0000 +@@ -950,7 +950,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1251,7 +1251,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } + +=== added file 'net/ipv4/tcp_zero_copy.c' +--- old/net/ipv4/tcp_zero_copy.c 1970-01-01 00:00:00 +0000 ++++ new/net/ipv4/tcp_zero_copy.c 2014-11-21 03:51:00 +0000 +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); + +=== modified file 'net/ipv6/ip6_output.c' +--- old/net/ipv6/ip6_output.c 2014-11-21 03:17:49 +0000 ++++ new/net/ipv6/ip6_output.c 2014-11-21 03:51:00 +0000 +@@ -1477,7 +1477,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, + diff --git a/iscsi-scst/kernel/patches/put_page_callback-3.18.patch b/iscsi-scst/kernel/patches/put_page_callback-3.18.patch new file mode 100644 index 000000000..7f85a7496 --- /dev/null +++ b/iscsi-scst/kernel/patches/put_page_callback-3.18.patch @@ -0,0 +1,387 @@ +Subject: [PATCH] put_page_callback + +--- + drivers/block/drbd/drbd_receiver.c | 2 +- + include/linux/mm_types.h | 11 +++++++++ + include/linux/net.h | 40 ++++++++++++++++++++++++++++++ + include/linux/skbuff.h | 4 +-- + net/Kconfig | 12 +++++++++ + net/ceph/pagevec.c | 2 +- + net/core/skbuff.c | 14 +++++------ + net/core/sock.c | 4 +-- + net/ipv4/Makefile | 1 + + net/ipv4/ip_output.c | 4 +-- + net/ipv4/tcp.c | 4 +-- + net/ipv4/tcp_zero_copy.c | 50 ++++++++++++++++++++++++++++++++++++++ + net/ipv6/ip6_output.c | 2 +- + 13 files changed, 132 insertions(+), 18 deletions(-) + create mode 100644 net/ipv4/tcp_zero_copy.c + +diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c +index 6960fb0..8fa4016 100644 +--- a/drivers/block/drbd/drbd_receiver.c ++++ b/drivers/block/drbd/drbd_receiver.c +@@ -132,7 +132,7 @@ static int page_chain_free(struct page *page) + struct page *tmp; + int i = 0; + page_chain_for_each_safe(page, tmp) { +- put_page(page); ++ net_put_page(page); + ++i; + } + return i; +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 6e0b286..5706a4d 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -196,6 +196,17 @@ struct page { + #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS + int _last_cpupid; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif + } + /* + * The struct page can be forced to be double word aligned so that atomic ops +diff --git a/include/linux/net.h b/include/linux/net.h +index 17d8339..f784384 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -19,6 +19,7 @@ + #define _LINUX_NET_H + + #include ++#include + #include + #include + #include /* For O_CLOEXEC and O_NONBLOCK */ +@@ -285,6 +286,45 @@ int kernel_sendpage(struct socket *sock, struct page *page, int offset, + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); + int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #define MODULE_ALIAS_NETPROTO(proto) \ + MODULE_ALIAS("net-pf-" __stringify(proto)) + +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index 6c8b6f6..edf6195 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2250,7 +2250,7 @@ static inline struct page *skb_frag_page(const skb_frag_t *frag) + */ + static inline void __skb_frag_ref(skb_frag_t *frag) + { +- get_page(skb_frag_page(frag)); ++ net_get_page(skb_frag_page(frag)); + } + + /** +@@ -2273,7 +2273,7 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f) + */ + static inline void __skb_frag_unref(skb_frag_t *frag) + { +- put_page(skb_frag_page(frag)); ++ net_put_page(skb_frag_page(frag)); + } + + /** +diff --git a/net/Kconfig b/net/Kconfig +index 99815b5..ac45213 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -76,6 +76,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" +diff --git a/net/ceph/pagevec.c b/net/ceph/pagevec.c +index 5550130..993f710 100644 +--- a/net/ceph/pagevec.c ++++ b/net/ceph/pagevec.c +@@ -51,7 +51,7 @@ void ceph_put_page_vector(struct page **pages, int num_pages, bool dirty) + for (i = 0; i < num_pages; i++) { + if (dirty) + set_page_dirty_lock(pages[i]); +- put_page(pages[i]); ++ net_put_page(pages[i]); + } + if (is_vmalloc_addr(pages)) + vfree(pages); +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 32e31c2..6eb3a9e 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -437,7 +437,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, + if (likely(data)) { + skb = build_skb(data, fragsz); + if (unlikely(!skb)) +- put_page(virt_to_head_page(data)); ++ net_put_page(virt_to_head_page(data)); + } + } else { + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, +@@ -495,7 +495,7 @@ static void skb_clone_fraglist(struct sk_buff *skb) + static void skb_free_head(struct sk_buff *skb) + { + if (skb->head_frag) +- put_page(virt_to_head_page(skb->head)); ++ net_put_page(virt_to_head_page(skb->head)); + else + kfree(skb->head); + } +@@ -822,7 +822,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) + if (!page) { + while (head) { + struct page *next = (struct page *)page_private(head); +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -1669,7 +1669,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1722,7 +1722,7 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, + spd->partial[spd->nr_pages - 1].len += *len; + return false; + } +- get_page(page); ++ net_get_page(page); + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; + spd->partial[spd->nr_pages].offset = offset; +@@ -2181,7 +2181,7 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) + page = virt_to_head_page(from->head); + offset = from->data - (unsigned char *)page_address(page); + __skb_fill_page_desc(to, 0, page, offset, plen); +- get_page(page); ++ net_get_page(page); + j = 1; + len -= plen; + } +@@ -2835,7 +2835,7 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, + copy); + frg_cnt++; + pfrag->offset += copy; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); +diff --git a/net/core/sock.c b/net/core/sock.c +index 15e0c67..e8ea0df 100644 +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -1830,7 +1830,7 @@ bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t gfp) + } + if (pfrag->offset + sz <= pfrag->size) + return true; +- put_page(pfrag->page); ++ net_put_page(pfrag->page); + } + + pfrag->offset = 0; +@@ -2581,7 +2581,7 @@ void sk_common_release(struct sock *sk) + sk_refcnt_debug_release(sk); + + if (sk->sk_frag.page) { +- put_page(sk->sk_frag.page); ++ net_put_page(sk->sk_frag.page); + sk->sk_frag.page = NULL; + } + +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index 518c04e..4072a87 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -57,6 +57,7 @@ obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o + obj-$(CONFIG_GENEVE) += geneve.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o xfrm4_protocol.o +diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +index bc6471d..ab9e262 100644 +--- a/net/ipv4/ip_output.c ++++ b/net/ipv4/ip_output.c +@@ -1051,7 +1051,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +@@ -1276,7 +1276,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i-1], len); + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 38c2bcb..f089a7a 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -949,7 +949,7 @@ new_segment: + if (can_coalesce) { + skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy); + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; +@@ -1250,7 +1250,7 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, copy); +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + pfrag->offset += copy; + } +diff --git a/net/ipv4/tcp_zero_copy.c b/net/ipv4/tcp_zero_copy.c +new file mode 100644 +index 0000000..430147e +--- /dev/null ++++ b/net/ipv4/tcp_zero_copy.c +@@ -0,0 +1,50 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL_GPL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL_GPL(net_set_get_put_page_callbacks); +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 8e950c2..8cb4760 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -1472,7 +1472,7 @@ alloc_new_skb: + __skb_fill_page_desc(skb, i, pfrag->page, + pfrag->offset, 0); + skb_shinfo(skb)->nr_frags = ++i; +- get_page(pfrag->page); ++ net_get_page(pfrag->page); + } + copy = min_t(int, copy, pfrag->size - pfrag->offset); + if (getfrag(from, +-- +2.1.2 + diff --git a/iscsi-scst/kernel/patches/rhel/put_page_callback-2.6.32-504.patch b/iscsi-scst/kernel/patches/rhel/put_page_callback-2.6.32-504.patch new file mode 100644 index 000000000..5159dd59f --- /dev/null +++ b/iscsi-scst/kernel/patches/rhel/put_page_callback-2.6.32-504.patch @@ -0,0 +1,453 @@ +[PATCH] put_page_callback-2.6.32-504 + +--- + include/linux/Kbuild | 1 + + include/linux/mm_types.h | 12 +++++++++++ + include/linux/net.h | 40 +++++++++++++++++++++++++++++++++++++ + net/Kconfig | 12 +++++++++++ + net/core/dev.c | 2 +- + net/core/skbuff.c | 34 ++++++++++++++++---------------- + net/ipv4/Makefile | 1 + + net/ipv4/ip_output.c | 4 +- + net/ipv4/tcp.c | 8 +++--- + net/ipv4/tcp_output.c | 2 +- + net/ipv4/tcp_zero_copy.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ + net/ipv6/ip6_output.c | 2 +- + 12 files changed, 141 insertions(+), 26 deletions(-) + create mode 100644 net/ipv4/tcp_zero_copy.c + +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +index 9301043..2870f1a 100644 +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -113,6 +113,7 @@ header-y += map_to_7segment.h + header-y += matroxfb.h + header-y += meye.h + header-y += minix_fs.h ++header-y += mm.h + header-y += mmtimer.h + header-y += mqueue.h + header-y += mtio.h +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index 645f205..7b6de1f 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -106,6 +106,18 @@ struct page { + */ + void *shadow; + #endif ++ ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++ /* ++ * Used to implement support for notification on zero-copy TCP transfer ++ * completion. It might look as not good to have this field here and ++ * it's better to have it in struct sk_buff, but it would make the code ++ * much more complicated and fragile, since all skb then would have to ++ * contain only pages with the same value in this field. ++ */ ++ void *net_priv; ++#endif ++ + }; + + /* +diff --git a/include/linux/net.h b/include/linux/net.h +index 58ada6b..b0adbdc 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -20,6 +20,7 @@ + + #include + #include ++#include + + #define NPROTO AF_MAX + +@@ -389,5 +390,44 @@ static const struct proto_ops name##_ops = { \ + extern struct ratelimit_state net_ratelimit_state; + #endif + ++#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) ++/* Support for notification on zero-copy TCP transfer completion */ ++typedef void (*net_get_page_callback_t)(struct page *page); ++typedef void (*net_put_page_callback_t)(struct page *page); ++ ++extern net_get_page_callback_t net_get_page_callback; ++extern net_put_page_callback_t net_put_page_callback; ++ ++extern int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback); ++ ++/* ++ * See comment for net_set_get_put_page_callbacks() why those functions ++ * don't need any protection. ++ */ ++static inline void net_get_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_get_page_callback(page); ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ if (page->net_priv != 0) ++ net_put_page_callback(page); ++ put_page(page); ++} ++#else ++static inline void net_get_page(struct page *page) ++{ ++ get_page(page); ++} ++static inline void net_put_page(struct page *page) ++{ ++ put_page(page); ++} ++#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */ ++ + #endif /* __KERNEL__ */ + #endif /* _LINUX_NET_H */ +diff --git a/net/Kconfig b/net/Kconfig +index 1d9b405..eedbed6 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -72,6 +72,18 @@ config INET + + Short answer: say Y. + ++config TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION ++ bool "TCP/IP zero-copy transfer completion notification" ++ depends on INET ++ default SCST_ISCSI ++ ---help--- ++ Adds support for sending a notification upon completion of a ++ zero-copy TCP/IP transfer. This can speed up certain TCP/IP ++ software. Currently this is only used by the iSCSI target driver ++ iSCSI-SCST. ++ ++ If unsure, say N. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" +diff --git a/net/core/dev.c b/net/core/dev.c +index 61dce2f..25d0826 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -3655,7 +3655,7 @@ pull: + skb_shinfo(skb)->frags[0].size -= grow; + + if (unlikely(!skb_shinfo(skb)->frags[0].size)) { +- put_page(skb_shinfo(skb)->frags[0].page); ++ net_put_page(skb_shinfo(skb)->frags[0].page); + memmove(skb_shinfo(skb)->frags, + skb_shinfo(skb)->frags + 1, + --skb_shinfo(skb)->nr_frags); +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 157dc11..74ac749 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -78,13 +78,13 @@ static struct kmem_cache *skbuff_fclone_cache __read_mostly; + static void sock_pipe_buf_release(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { +- put_page(buf->page); ++ net_put_page(buf->page); + } + + static void sock_pipe_buf_get(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { +- get_page(buf->page); ++ net_get_page(buf->page); + } + + static int sock_pipe_buf_steal(struct pipe_inode_info *pipe, +@@ -396,7 +396,7 @@ static void skb_release_data(struct sk_buff *skb) + if (skb_shinfo(skb)->nr_frags) { + int i; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + } + + /* +@@ -705,7 +705,7 @@ static int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) + if (!page) { + while (head) { + struct page *next = (struct page *)head->private; +- put_page(head); ++ net_put_page(head); + head = next; + } + return -ENOMEM; +@@ -720,7 +720,7 @@ static int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) + + /* skb frags release userspace buffers */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + + uarg->callback(uarg); + +@@ -886,7 +886,7 @@ struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask) + } + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; +- get_page(skb_shinfo(n)->frags[i].page); ++ net_get_page(skb_shinfo(n)->frags[i].page); + } + skb_shinfo(n)->nr_frags = i; + } +@@ -967,7 +967,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, + skb_tx(skb)->dev_zerocopy = 0; + } + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) +- get_page(skb_shinfo(skb)->frags[i].page); ++ net_get_page(skb_shinfo(skb)->frags[i].page); + + if (skb_has_frag_list(skb)) + skb_clone_fraglist(skb); +@@ -1246,7 +1246,7 @@ drop_pages: + skb_shinfo(skb)->nr_frags = i; + + for (; i < nfrags; i++) +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + + if (skb_has_frag_list(skb)) + skb_drop_fraglist(skb); +@@ -1415,7 +1415,7 @@ pull_pages: + k = 0; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + if (skb_shinfo(skb)->frags[i].size <= eat) { +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + eat -= skb_shinfo(skb)->frags[i].size; + } else { + skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; +@@ -1517,7 +1517,7 @@ EXPORT_SYMBOL(skb_copy_bits); + */ + static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) + { +- put_page(spd->pages[i]); ++ net_put_page(spd->pages[i]); + } + + static inline struct page *linear_to_page(struct page *page, unsigned int *len, +@@ -1541,7 +1541,7 @@ new_page: + off = sk->sk_sndmsg_off; + mlen = PAGE_SIZE - off; + if (mlen < 64 && mlen < *len) { +- put_page(p); ++ net_put_page(p); + goto new_page; + } + +@@ -1551,7 +1551,7 @@ new_page: + memcpy(page_address(p) + off, page_address(page) + *offset, *len); + sk->sk_sndmsg_off += *len; + *offset = off; +- get_page(p); ++ net_get_page(p); + + return p; + } +@@ -1572,7 +1572,7 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page, + if (!page) + return 1; + } else +- get_page(page); ++ net_get_page(page); + + spd->pages[spd->nr_pages] = page; + spd->partial[spd->nr_pages].len = *len; +@@ -2202,7 +2202,7 @@ static inline void skb_split_no_header(struct sk_buff *skb, + * where splitting is expensive. + * 2. Split is accurately. We make this. + */ +- get_page(skb_shinfo(skb)->frags[i].page); ++ net_get_page(skb_shinfo(skb)->frags[i].page); + skb_shinfo(skb1)->frags[0].page_offset += len - pos; + skb_shinfo(skb1)->frags[0].size -= len - pos; + skb_shinfo(skb)->frags[i].size = len - pos; +@@ -2325,7 +2325,7 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) + to++; + + } else { +- get_page(fragfrom->page); ++ net_get_page(fragfrom->page); + fragto->page = fragfrom->page; + fragto->page_offset = fragfrom->page_offset; + fragto->size = todo; +@@ -2347,7 +2347,7 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) + fragto = &skb_shinfo(tgt)->frags[merge]; + + fragto->size += fragfrom->size; +- put_page(fragfrom->page); ++ net_put_page(fragfrom->page); + } + + /* Reposition in the original skb */ +@@ -2760,7 +2760,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features) + + while (pos < offset + len && i < nfrags) { + *frag = skb_shinfo(skb)->frags[i]; +- get_page(frag->page); ++ net_get_page(frag->page); + size = frag->size; + + if (pos < offset) { +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index e18daba..65f5371 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -51,6 +51,7 @@ obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o + obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o + obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o + obj-$(CONFIG_NETLABEL) += cipso_ipv4.o ++obj-$(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION) += tcp_zero_copy.o + + obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ + xfrm4_output.o +diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +index 7ac7cfa..8cda5cc 100644 +--- a/net/ipv4/ip_output.c ++++ b/net/ipv4/ip_output.c +@@ -1000,7 +1000,7 @@ alloc_new_skb: + err = -EMSGSIZE; + goto error; + } +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, off, 0); + frag = &skb_shinfo(skb)->frags[i]; + } +@@ -1239,7 +1239,7 @@ ssize_t ip_append_page(struct sock *sk, struct page *page, + if (skb_can_coalesce(skb, i, page, offset)) { + skb_shinfo(skb)->frags[i-1].size += len; + } else if (i < MAX_SKB_FRAGS) { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, len); + } else { + err = -EMSGSIZE; +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 18d22cf..b5c12fa 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -821,7 +821,7 @@ new_segment: + if (can_coalesce) { + skb_shinfo(skb)->frags[i - 1].size += copy; + } else { +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, offset, copy); + } + skb_tx(skb)->shared_frag = 1; +@@ -1030,7 +1030,7 @@ new_segment: + goto new_segment; + } else if (page) { + if (off == PAGE_SIZE) { +- put_page(page); ++ net_put_page(page); + TCP_PAGE(sk) = page = NULL; + off = 0; + } +@@ -1071,9 +1071,9 @@ new_segment: + } else { + skb_fill_page_desc(skb, i, page, off, copy); + if (TCP_PAGE(sk)) { +- get_page(page); ++ net_get_page(page); + } else if (off + copy < PAGE_SIZE) { +- get_page(page); ++ net_get_page(page); + TCP_PAGE(sk) = page; + } + } +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index 255e6e3..5c48819 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -1071,7 +1071,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) + k = 0; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + if (skb_shinfo(skb)->frags[i].size <= eat) { +- put_page(skb_shinfo(skb)->frags[i].page); ++ net_put_page(skb_shinfo(skb)->frags[i].page); + eat -= skb_shinfo(skb)->frags[i].size; + } else { + skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; +diff --git a/net/ipv4/tcp_zero_copy.c b/net/ipv4/tcp_zero_copy.c +new file mode 100644 +index 0000000..9cd990c +--- /dev/null ++++ b/net/ipv4/tcp_zero_copy.c +@@ -0,0 +1,49 @@ ++/* ++ * Support routines for TCP zero copy transmit ++ * ++ * Created by Vladislav Bolkhovitin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++ ++net_get_page_callback_t net_get_page_callback __read_mostly; ++EXPORT_SYMBOL(net_get_page_callback); ++ ++net_put_page_callback_t net_put_page_callback __read_mostly; ++EXPORT_SYMBOL(net_put_page_callback); ++ ++/* ++ * Caller of this function must ensure that at the moment when it's called ++ * there are no pages in the system with net_priv field set to non-zero ++ * value. Hence, this function, as well as net_get_page() and net_put_page(), ++ * don't need any protection. ++ */ ++int net_set_get_put_page_callbacks( ++ net_get_page_callback_t get_callback, ++ net_put_page_callback_t put_callback) ++{ ++ int res = 0; ++ ++ if ((net_get_page_callback != NULL) && (get_callback != NULL) && ++ (net_get_page_callback != get_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ if ((net_put_page_callback != NULL) && (put_callback != NULL) && ++ (net_put_page_callback != put_callback)) { ++ res = -EBUSY; ++ goto out; ++ } ++ ++ net_get_page_callback = get_callback; ++ net_put_page_callback = put_callback; ++ ++out: ++ return res; ++} ++EXPORT_SYMBOL(net_set_get_put_page_callbacks); +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 0e985f7..22b4529 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -1375,7 +1375,7 @@ alloc_new_skb: + err = -EMSGSIZE; + goto error; + } +- get_page(page); ++ net_get_page(page); + skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0); + frag = &skb_shinfo(skb)->frags[i]; + } +-- +1.7.1 + diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index e75fc8618..121389aab 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -374,7 +374,7 @@ int __del_session(struct iscsi_target *target, u64 sid) if (!list_empty(&session->conn_list)) { PRINT_ERROR("%llx still have connections", - (long long unsigned int)session->sid); + (unsigned long long int)session->sid); return -EBUSY; } @@ -391,7 +391,7 @@ void iscsi_sess_force_close(struct iscsi_session *sess) lockdep_assert_held(&sess->target->target_mutex); PRINT_INFO("Deleting session %llx with initiator %s (%p)", - (long long unsigned int)sess->sid, sess->initiator_name, sess); + (unsigned long long int)sess->sid, sess->initiator_name, sess); list_for_each_entry(conn, &sess->conn_list, conn_list_entry) { TRACE_MGMT_DBG("Deleting connection with initiator %p", conn); @@ -415,7 +415,7 @@ static void iscsi_session_info_show(struct seq_file *seq, list_for_each_entry(session, &target->session_list, session_list_entry) { seq_printf(seq, "\tsid:%llx initiator:%s (reinstating %s)\n", - (long long unsigned int)session->sid, + (unsigned long long int)session->sid, session->initiator_name, session->sess_reinstating ? "yes" : "no"); conn_info_show(seq, session); diff --git a/mvsas_tgt/mv_tgt.c b/mvsas_tgt/mv_tgt.c index 86c5a49fa..962c88b13 100644 --- a/mvsas_tgt/mv_tgt.c +++ b/mvsas_tgt/mv_tgt.c @@ -1642,7 +1642,7 @@ static int mvst_handle_task_mgmt(struct mvs_info *mvi, sess = mvst_find_sess_by_lid(tgt, initiator_sas_addr); if (sess == NULL) { TRACE(TRACE_MGMT, "mvsttgt(%ld): task mgmt fn 0x%x for " - "non-existant session", mvi->instance, + "non-existent session", mvi->instance, cmd->save_task_iu.task_fun); res = -EFAULT; goto out; diff --git a/mvsas_tgt/mv_tgt.h b/mvsas_tgt/mv_tgt.h index 0058854f7..6598d1f29 100644 --- a/mvsas_tgt/mv_tgt.h +++ b/mvsas_tgt/mv_tgt.h @@ -478,7 +478,7 @@ struct mvs_tgt_initiator { }; /* - * Equivilant to IT Nexus (Initiator-Target) + * Equivalent to IT Nexus (Initiator-Target) */ struct mvst_sess { struct list_head sess_entry; @@ -558,7 +558,7 @@ struct mvst_tgt { unsigned int tgt_enable_64bit_addr:1; wait_queue_head_t waitQ; int notify_ack_expected; - /* Count of sessions refering q2t_tgt, protected by hardware_lock */ + /* Count of sessions referring q2t_tgt, protected by hardware_lock */ int sess_count; struct list_head sess_list; }; diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index b13e569f3..def42025c 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,21 +3,23 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.16.7 \ +3.18.3 \ +3.17.8-nc \ +3.16.7-nc \ 3.15.10-nc \ -3.14.23-nc \ +3.14.29-nc \ 3.13.11-nc \ -3.12.31-nc \ +3.12.36-nc \ 3.11.10-nc \ -3.10.59-nc \ +3.10.65-nc \ 3.9.11-nc \ 3.8.13-nc \ 3.7.10-nc \ 3.6.11-nc \ 3.5.7-nc \ -3.4.103-nc \ +3.4.105-nc \ 3.3.8-nc \ -3.2.61-nc \ +3.2.66-nc \ 3.1.10-nc \ 3.0.101-nc \ 2.6.39.4-nc \ @@ -26,9 +28,9 @@ ABT_KERNELS=" \ 2.6.36.4-nc \ 2.6.35.14 \ 2.6.35.14-u-nc \ -2.6.34.14-nc \ +2.6.34.15-nc \ 2.6.33.20-nc \ -2.6.32.62-nc \ +2.6.32.65-nc \ 2.6.31.14-nc \ 2.6.30.10-nc \ 2.6.29.6-nc \ diff --git a/qla2x00t/doc/qla2x00t-howto.html b/qla2x00t/doc/qla2x00t-howto.html index 66b418723..4ed582fd4 100644 --- a/qla2x00t/doc/qla2x00t-howto.html +++ b/qla2x00t/doc/qla2x00t-howto.html @@ -80,13 +80,6 @@ Instructions for obtaining a distribution-specific kernel source tree vary. An e [root@proj src ]# tar xjf linux-source-`uname -r`.tar.bz2 -
                          • - Patch the kernel that has just been downloaded: -
                            [root@proj src ]# ln -s linux-3.11 linux
                            -[root@proj src ]# cd linux
                            -[root@proj linux ]# patch -p1 < /root/scst/scst/kernel/scst_exec_req_fifo-3.11.patch
                            -
                          • -
                          • The next step is to configure the kernel:
                            [root@proj linux ]# pwd
                             /usr/src/linux
                            diff --git a/qla2x00t/qla2x00-target/Makefile_in-tree-3.17 b/qla2x00t/qla2x00-target/Makefile_in-tree-3.17
                            new file mode 100644
                            index 000000000..9657aee84
                            --- /dev/null
                            +++ b/qla2x00t/qla2x00-target/Makefile_in-tree-3.17
                            @@ -0,0 +1,5 @@
                            +ccflags-y += -Idrivers/scsi/qla2xxx
                            +
                            +qla2x00tgt-y := qla2x00t.o
                            +
                            +obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o
                            diff --git a/qla2x00t/qla2x00-target/Makefile_in-tree-3.18 b/qla2x00t/qla2x00-target/Makefile_in-tree-3.18
                            new file mode 100644
                            index 000000000..9657aee84
                            --- /dev/null
                            +++ b/qla2x00t/qla2x00-target/Makefile_in-tree-3.18
                            @@ -0,0 +1,5 @@
                            +ccflags-y += -Idrivers/scsi/qla2xxx
                            +
                            +qla2x00tgt-y := qla2x00t.o
                            +
                            +obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o
                            diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README
                            index d06a7757a..7cf3c3096 100644
                            --- a/qla2x00t/qla2x00-target/README
                            +++ b/qla2x00t/qla2x00-target/README
                            @@ -565,7 +565,7 @@ Thanks to:
                             initiator driver.
                             
                              * Mark Buechler  for the original
                            -WWN-based authentification, a lot of useful suggestions, bug reports and
                            +WWN-based authentication, a lot of useful suggestions, bug reports and
                             help in debugging.
                             
                              * Ming Zhang  for fixes.
                            diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c
                            index 3741f88fa..a66eb3ada 100644
                            --- a/qla2x00t/qla2x00-target/qla2x00t.c
                            +++ b/qla2x00t/qla2x00-target/qla2x00t.c
                            @@ -1547,6 +1547,8 @@ static int q2t_target_release(struct scst_tgt *scst_tgt)
                             
                             	q2t_target_stop(scst_tgt);
                             
                            +	cancel_work_sync(&tgt->rscn_reg_work);
                            +
                             	ha->q2t_tgt = NULL;
                             	scst_tgt_set_tgt_priv(scst_tgt, NULL);
                             
                            @@ -2409,8 +2411,8 @@ static void q2t_load_cont_data_segments(struct q2t_prm *prm)
                             			*dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
                             
                             			TRACE_SG("S/G Segment Cont. phys_addr=%llx:%llx, len=%d",
                            -			      (long long unsigned int)pci_dma_hi32(dma_addr),
                            -			      (long long unsigned int)pci_dma_lo32(dma_addr),
                            +			      (unsigned long long int)pci_dma_hi32(dma_addr),
                            +			      (unsigned long long int)pci_dma_lo32(dma_addr),
                             			      (int)sg_dma_len(prm->sg));
                             
                             			prm->sg = __sg_next_inline(prm->sg);
                            @@ -2470,8 +2472,8 @@ static void q2x_load_data_segments(struct q2t_prm *prm)
                             		*dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
                             
                             		TRACE_SG("S/G Segment phys_addr=%llx:%llx, len=%d",
                            -		      (long long unsigned int)pci_dma_hi32(dma_addr),
                            -		      (long long unsigned int)pci_dma_lo32(dma_addr),
                            +		      (unsigned long long int)pci_dma_hi32(dma_addr),
                            +		      (unsigned long long int)pci_dma_lo32(dma_addr),
                             		      (int)sg_dma_len(prm->sg));
                             
                             		prm->sg = __sg_next_inline(prm->sg);
                            @@ -2532,8 +2534,8 @@ static void q24_load_data_segments(struct q2t_prm *prm)
                             		*dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg));
                             
                             		TRACE_SG("S/G Segment phys_addr=%llx:%llx, len=%d",
                            -		      (long long unsigned int)pci_dma_hi32(dma_addr),
                            -		      (long long unsigned int)pci_dma_lo32(dma_addr),
                            +		      (unsigned long long int)pci_dma_hi32(dma_addr),
                            +		      (unsigned long long int)pci_dma_lo32(dma_addr),
                             		      (int)sg_dma_len(prm->sg));
                             
                             		prm->sg = __sg_next_inline(prm->sg);
                            @@ -2679,6 +2681,31 @@ out_unlock_free_unmap:
                             	goto out;
                             }
                             
                            +/*
                            + * Convert sense buffer (byte array) to little endian format as required by
                            + * qla24xx firmware.
                            + */
                            +static void q24_copy_sense_buffer_to_ctio(ctio7_status1_entry_t *ctio,
                            +	uint8_t *sense_buf, unsigned int sense_buf_len)
                            +{
                            +	uint32_t *src = (void *)sense_buf;
                            +	uint32_t *end = (void *)sense_buf + sense_buf_len;
                            +	uint8_t *p;
                            +	__be32 *dst = (void *)ctio->sense_data;
                            +
                            +	/*
                            +	 * The sense buffer allocated by scst_alloc_sense() is zero-filled and
                            +	 * has a length that is a multiple of four. This means that it is safe
                            +	 * to access the bytes after the end of the sense buffer up to a
                            +	 * boundary that is a multiple of four.
                            +	 */
                            +	for (p = (uint8_t *)end; ((uintptr_t)p & 3) != 0; p++)
                            +		WARN_ONCE(*p != 0, "sense_buf[%zd] = %d\n", p - sense_buf, *p);
                            +
                            +	for ( ; src < end; src++)
                            +		*dst++ = cpu_to_be32(*src);
                            +}
                            +
                             static inline int q2t_need_explicit_conf(scsi_qla_host_t *ha,
                             	struct q2t_cmd *cmd, int sending_sense)
                             {
                            @@ -2914,7 +2941,6 @@ static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio,
                             	ctio->residual = cpu_to_le32(prm->residual);
                             	ctio->scsi_status = cpu_to_le16(prm->rq_result);
                             	if (scst_sense_valid(prm->sense_buffer)) {
                            -		int i;
                             		ctio1 = (ctio7_status1_entry_t *)ctio;
                             		if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) {
                             			ctio1->flags |= cpu_to_le16(
                            @@ -2925,20 +2951,8 @@ static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio,
                             		ctio1->flags |= cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
                             		ctio1->scsi_status |= cpu_to_le16(SS_SENSE_LEN_VALID);
                             		ctio1->sense_length = cpu_to_le16(prm->sense_buffer_len);
                            -		for (i = 0; i < prm->sense_buffer_len/4; i++)
                            -			((uint32_t *)ctio1->sense_data)[i] =
                            -				cpu_to_be32(((uint32_t *)prm->sense_buffer)[i]);
                            -#if 0
                            -		if (unlikely((prm->sense_buffer_len % 4) != 0)) {
                            -			static int q;
                            -			if (q < 10) {
                            -				PRINT_INFO("qla2x00t(%ld): %d bytes of sense "
                            -					"lost", prm->tgt->ha->instance,
                            -					prm->sense_buffer_len % 4);
                            -				q++;
                            -			}
                            -		}
                            -#endif
                            +		q24_copy_sense_buffer_to_ctio(ctio1, prm->sense_buffer,
                            +					      prm->sense_buffer_len);
                             	} else {
                             		ctio1 = (ctio7_status1_entry_t *)ctio;
                             		ctio1->flags &= ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
                            @@ -4074,7 +4088,7 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb)
                             
                             	if (sess == NULL) {
                             		TRACE_MGMT_DBG("qla2x00t(%ld): task mgmt fn 0x%x for "
                            -			"non-existant session", ha->instance, fn);
                            +			"non-existent session", ha->instance, fn);
                             		res = q2t_sched_sess_work(tgt, Q2T_SESS_WORK_TM, iocb,
                             			IS_FWI2_CAPABLE(ha) ? sizeof(atio7_entry_t) :
                             					      sizeof(notify_entry_t));
                            @@ -4161,11 +4175,39 @@ out:
                             	return res;
                             }
                             
                            +static void q2t_rscn_reg_work(struct work_struct *work)
                            +{
                            +	struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, rscn_reg_work);
                            +	scsi_qla_host_t *ha = tgt->ha;
                            +	int ret;
                            +
                            +	TRACE_ENTRY();
                            +
                            +	if ((ha->host->active_mode & MODE_INITIATOR) == 0) {
                            +		/*
                            +		 * The QLogic firmware and qla2xxx do not register for RSCNs in
                            +		 * target-only mode, so do that explicitly.
                            +		 */
                            +		ret = qla2x00_send_change_request(ha, 0x3, ha->vp_idx);
                            +		if (ret != QLA_SUCCESS)
                            +			PRINT_INFO("qla2x00t(%ld): RSCN registration failed: "
                            +				"%#x (OK for non-fabric setups)",
                            +				ha->host_no, ret);
                            +		else
                            +			TRACE_MGMT_DBG("qla2x00t(%ld): RSCN registration succeeded",
                            +				ha->host_no);
                            +	}
                            +
                            +	TRACE_EXIT();
                            +	return;
                            +}
                            +
                             /*
                              * pha->hardware_lock supposed to be held on entry. Might drop it, then reacquire
                              */
                             static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb)
                             {
                            +	struct q2t_tgt *tgt = ha->tgt;
                             	int res = 1; /* send notify ack */
                             	struct q2t_sess *sess;
                             	int loop_id;
                            @@ -4177,6 +4219,13 @@ static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb)
                             
                             	switch (iocb->status_subcode) {
                             	case ELS_PLOGI:
                            +		/*
                            +		 * HACK. Let's do it on PLOGI, because seems there is no other
                            +		 * simple place, from where it can be called. In the worst
                            +		 * case, we will just reinstall RSCNs once again, it's harmless.
                            +		 */
                            +		schedule_work(&tgt->rscn_reg_work);
                            +		break;
                             	case ELS_FLOGI:
                             	case ELS_PRLI:
                             		break;
                            @@ -5297,7 +5346,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
                             					 * command was sent between the abort request
                             					 * was received and processed. Unfortunately,
                             					 * the firmware has a silly requirement that
                            -					 * all aborted exchanges must be explicitely
                            +					 * all aborted exchanges must be explicitly
                             					 * terminated, otherwise it refuses to send
                             					 * responses for the abort requests. So, we
                             					 * have to (re)terminate the exchange and
                            @@ -5405,11 +5454,11 @@ static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha,
                             	case MBA_RSP_TRANSFER_ERR:	/* Response Transfer Error */
                             	case MBA_ATIO_TRANSFER_ERR:	/* ATIO Queue Transfer Error */
                             		TRACE(TRACE_MGMT, "qla2x00t(%ld): System error async event %#x "
                            -			"occured", ha->instance, code);
                            +			"occurred", ha->instance, code);
                             		break;
                             
                             	case MBA_LOOP_UP:
                            -		TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop up occured",
                            +		TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop up occurred",
                             			ha->instance);
                             		if (tgt->link_reinit_iocb_pending) {
                             			q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0);
                            @@ -5418,28 +5467,28 @@ static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha,
                             		break;
                             
                             	case MBA_LIP_OCCURRED:
                            -		TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP occured", ha->instance);
                            +		TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP occurred", ha->instance);
                             		break;
                             
                             	case MBA_LOOP_DOWN:
                            -		TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop down occured",
                            +		TRACE(TRACE_MGMT, "qla2x00t(%ld): Loop down occurred",
                             			ha->instance);
                             		break;
                             
                             	case MBA_LIP_RESET:
                            -		TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP reset occured",
                            +		TRACE(TRACE_MGMT, "qla2x00t(%ld): LIP reset occurred",
                             			ha->instance);
                             		break;
                             
                             	case MBA_PORT_UPDATE:
                             	case MBA_RSCN_UPDATE:
                             		TRACE_MGMT_DBG("qla2x00t(%ld): Port update async event %#x "
                            -			"occured", ha->instance, code);
                            +			"occurred", ha->instance, code);
                             		/* .mark_all_devices_lost() is handled by the initiator driver */
                             		break;
                             
                             	default:
                            -		TRACE(TRACE_MGMT, "qla2x00t(%ld): Async event %#x occured: "
                            +		TRACE(TRACE_MGMT, "qla2x00t(%ld): Async event %#x occurred: "
                             			"ignoring (m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)",
                             			ha->instance, code,
                             			le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]),
                            @@ -5823,8 +5872,10 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd)
                             
                             		q2t_cleanup_hw_pending_cmd(ha, cmd);
                             
                            -		scst_rx_data(scst_cmd, SCST_RX_STATUS_ERROR_FATAL,
                            -				SCST_CONTEXT_THREAD);
                            +		/* It might be sporadic, hence retriable */
                            +		scst_set_cmd_error(scst_cmd,
                            +				SCST_LOAD_SENSE(scst_sense_internal_failure));
                            +		scst_rx_data(scst_cmd, SCST_RX_STATUS_ERROR_SENSE_SET, SCST_CONTEXT_THREAD);
                             		goto out_unlock;
                             	} else if (cmd->state == Q2T_STATE_ABORTED) {
                             		TRACE_MGMT_DBG("Force finishing aborted cmd %p (tag %d)",
                            @@ -5872,6 +5923,7 @@ static int q2t_add_target(scsi_qla_host_t *ha)
                             
                             	tgt->ha = ha;
                             	init_waitqueue_head(&tgt->waitQ);
                            +	INIT_WORK(&tgt->rscn_reg_work, q2t_rscn_reg_work);
                             	INIT_LIST_HEAD(&tgt->sess_list);
                             	INIT_LIST_HEAD(&tgt->del_sess_list);
                             	INIT_DELAYED_WORK(&tgt->sess_del_work, q2t_del_sess_work_fn);
                            diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h
                            index d3d976478..57517046a 100644
                            --- a/qla2x00t/qla2x00-target/qla2x00t.h
                            +++ b/qla2x00t/qla2x00-target/qla2x00t.h
                            @@ -141,7 +141,9 @@ struct q2t_tgt {
                             	 */
                             	unsigned long tgt_stop; /* the driver is being stopped */
                             
                            -	/* Count of sessions refering q2t_tgt. Protected by hardware_lock. */
                            +	struct work_struct rscn_reg_work;
                            +
                            +	/* Count of sessions referring q2t_tgt. Protected by hardware_lock. */
                             	int sess_count;
                             
                             	/*
                            @@ -177,7 +179,7 @@ struct q2t_tgt {
                             };
                             
                             /*
                            - * Equivilant to IT Nexus (Initiator-Target)
                            + * Equivalent to IT Nexus (Initiator-Target)
                              */
                             struct q2t_sess {
                             	uint16_t loop_id;
                            diff --git a/qla2x00t/qla_init.c b/qla2x00t/qla_init.c
                            index b4b745fa8..865c21623 100644
                            --- a/qla2x00t/qla_init.c
                            +++ b/qla2x00t/qla_init.c
                            @@ -2147,7 +2147,7 @@ qla2x00_configure_loop(scsi_qla_host_t *ha)
                             		DEBUG3(printk("%s: exiting normally\n", __func__));
                             	}
                             
                            -	/* Restore state if a resync event occured during processing */
                            +	/* Restore state if a resync event occurred during processing */
                             	if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
                             		if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
                             			set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
                            diff --git a/qla2x00t/qla_iocb.c b/qla2x00t/qla_iocb.c
                            index 432a3600e..1d9ea02af 100644
                            --- a/qla2x00t/qla_iocb.c
                            +++ b/qla2x00t/qla_iocb.c
                            @@ -261,7 +261,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
                              * qla2x00_start_scsi() - Send a SCSI command to the ISP
                              * @sp: command to send to the ISP
                              *
                            - * Returns non-zero if a failure occured, else zero.
                            + * Returns non-zero if a failure occurred, else zero.
                              */
                             int
                             qla2x00_start_scsi(srb_t *sp)
                            @@ -407,7 +407,7 @@ queuing_error:
                              *
                              * Can be called from both normal and interrupt context.
                              *
                            - * Returns non-zero if a failure occured, else zero.
                            + * Returns non-zero if a failure occurred, else zero.
                              *
                              * Hardware lock must be held on entrance. Might release it, then reacquire.
                              */
                            @@ -671,7 +671,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
                              * qla24xx_start_scsi() - Send a SCSI command to the ISP
                              * @sp: command to send to the ISP
                              *
                            - * Returns non-zero if a failure occured, else zero.
                            + * Returns non-zero if a failure occurred, else zero.
                              */
                             int
                             qla24xx_start_scsi(srb_t *sp)
                            diff --git a/qla2x00t/qla_isr.c b/qla2x00t/qla_isr.c
                            index 8395186d8..233eded41 100644
                            --- a/qla2x00t/qla_isr.c
                            +++ b/qla2x00t/qla_isr.c
                            @@ -419,9 +419,9 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                             		break;
                             
                             	case MBA_LIP_OCCURRED:		/* Loop Initialization Procedure */
                            -		DEBUG2(printk("scsi(%ld): LIP occured (%x).\n", ha->host_no,
                            +		DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", ha->host_no,
                             		    mb[1]));
                            -		qla_printk(KERN_INFO, ha, "LIP occured (%x).\n", mb[1]);
                            +		qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]);
                             
                             		if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
                             			atomic_set(&ha->loop_state, LOOP_DOWN);
                            @@ -488,7 +488,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                             		DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n",
                             		    ha->host_no, mb[1]));
                             		qla_printk(KERN_INFO, ha,
                            -		    "LIP reset occured (%x).\n", mb[1]);
                            +		    "LIP reset occurred (%x).\n", mb[1]);
                             
                             		if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
                             			atomic_set(&ha->loop_state, LOOP_DOWN);
                            @@ -781,8 +781,8 @@ qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
                             	fcport->last_ramp_up = jiffies;
                             
                             	DEBUG2(qla_printk(KERN_INFO, fcport->ha,
                            -	    "scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n",
                            -	    fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
                            +	    "scsi(%ld:%d:%d:%lld): Queue depth adjusted-up to %d.\n",
                            +	    fcport->ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
                             	    sdev->queue_depth));
                             }
                             
                            @@ -795,8 +795,8 @@ qla2x00_adjust_sdev_qdepth_down(struct scsi_device *sdev, void *data)
                             		return;
                             
                             	DEBUG2(qla_printk(KERN_INFO, fcport->ha,
                            -	    "scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n",
                            -	    fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
                            +	    "scsi(%ld:%d:%d:%lld): Queue depth adjusted-down to %d.\n",
                            +	    fcport->ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
                             	    sdev->queue_depth));
                             }
                             
                            @@ -1140,11 +1140,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             		if (IS_FWI2_CAPABLE(ha))
                             			sense_data += rsp_info_len;
                             		if (rsp_info_len > 3 && rsp_info[3]) {
                            -			DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol "
                            +			DEBUG2(printk("scsi(%ld:%d:%d:%lld) FCP I/O protocol "
                             			    "failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..."
                             			    "retrying command\n", ha->host_no,
                             			    cp->device->channel, cp->device->id,
                            -			    cp->device->lun, rsp_info_len, rsp_info[0],
                            +			    (unsigned long long)cp->device->lun, rsp_info_len, rsp_info[0],
                             			    rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4],
                             			    rsp_info[5], rsp_info[6], rsp_info[7]));
                             
                            @@ -1178,11 +1178,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             			    ((unsigned)(scsi_bufflen(cp) - resid) <
                             			     cp->underflow)) {
                             				qla_printk(KERN_INFO, ha,
                            -					   "scsi(%ld:%d:%d:%d): Mid-layer underflow "
                            +					   "scsi(%ld:%d:%d:%lld): Mid-layer underflow "
                             					   "detected (%x of %x bytes)...returning "
                             					   "error status.\n", ha->host_no,
                             					   cp->device->channel, cp->device->id,
                            -					   cp->device->lun, resid,
                            +					   (unsigned long long)cp->device->lun, resid,
                             					   scsi_bufflen(cp));
                             
                             				cp->result = DID_ERROR << 16;
                            @@ -1230,10 +1230,10 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             			CMD_RESID_LEN(cp) = resid;
                             		} else {
                             			DEBUG2(printk(KERN_INFO
                            -			    "scsi(%ld:%d:%d) UNDERRUN status detected "
                            +			    "scsi(%ld:%d:%lld) UNDERRUN status detected "
                             			    "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
                             			    "os_underflow=0x%x\n", ha->host_no,
                            -			    cp->device->id, cp->device->lun, comp_status,
                            +			    cp->device->id, (unsigned long long)cp->device->lun, comp_status,
                             			    scsi_status, resid_len, resid, cp->cmnd[0],
                             			    cp->underflow));
                             
                            @@ -1277,11 +1277,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             			 * layer to retry it by reporting a bus busy.
                             			 */
                             			if (!(scsi_status & SS_RESIDUAL_UNDER)) {
                            -				DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
                            +				DEBUG2(printk("scsi(%ld:%d:%d:%lld) Dropped "
                             					      "frame(s) detected (%x of %x bytes)..."
                             					      "retrying command.\n", ha->host_no,
                             					      cp->device->channel, cp->device->id,
                            -					      cp->device->lun, resid,
                            +					      (unsigned long long)cp->device->lun, resid,
                             					      scsi_bufflen(cp)));
                             
                             				cp->result = DID_BUS_BUSY << 16;
                            @@ -1292,11 +1292,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             			if ((unsigned)(scsi_bufflen(cp) - resid) <
                             			    cp->underflow) {
                             				qla_printk(KERN_INFO, ha,
                            -					   "scsi(%ld:%d:%d:%d): Mid-layer underflow "
                            +					   "scsi(%ld:%d:%d:%lld): Mid-layer underflow "
                             					   "detected (%x of %x bytes)...returning "
                             					   "error status.\n", ha->host_no,
                             					   cp->device->channel, cp->device->id,
                            -					   cp->device->lun, resid,
                            +					   (unsigned long long)cp->device->lun, resid,
                             					   scsi_bufflen(cp));
                             
                             				cp->result = DID_ERROR << 16;
                            @@ -1310,9 +1310,9 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             
                             	case CS_DATA_OVERRUN:
                             		DEBUG2(printk(KERN_INFO
                            -		    "scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n",
                            -		    ha->host_no, cp->device->id, cp->device->lun, comp_status,
                            -		    scsi_status));
                            +		    "scsi(%ld:%d:%lld): OVERRUN status detected 0x%x-0x%x\n",
                            +		    ha->host_no, cp->device->id, (unsigned long long)cp->device->lun,
                            +		    comp_status, scsi_status));
                             		DEBUG2(printk(KERN_INFO
                             		    "CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
                             		    cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3],
                            @@ -1335,9 +1335,9 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             		 * Target with DID_NO_CONNECT ELSE Queue the IOs in the
                             		 * retry_queue.
                             		 */
                            -		DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down "
                            +		DEBUG2(printk("scsi(%ld:%d:%lld): status_entry: Port Down "
                             		    "pid=%ld, compl status=0x%x, port state=0x%x\n",
                            -		    ha->host_no, cp->device->id, cp->device->lun,
                            +		    ha->host_no, cp->device->id, (unsigned long long)cp->device->lun,
                             		    cp->serial_number, comp_status,
                             		    atomic_read(&fcport->state)));
                             
                            @@ -1373,17 +1373,17 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
                             
                             		if (IS_FWI2_CAPABLE(ha)) {
                             			DEBUG2(printk(KERN_INFO
                            -			    "scsi(%ld:%d:%d:%d): TIMEOUT status detected "
                            +			    "scsi(%ld:%d:%d:%lld): TIMEOUT status detected "
                             			    "0x%x-0x%x\n", ha->host_no, cp->device->channel,
                            -			    cp->device->id, cp->device->lun, comp_status,
                            -			    scsi_status));
                            +			    cp->device->id, (unsigned long long)cp->device->lun,
                            +			    comp_status, scsi_status));
                             			break;
                             		}
                             		DEBUG2(printk(KERN_INFO
                            -		    "scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x "
                            +		    "scsi(%ld:%d:%d:%lld): TIMEOUT status detected 0x%x-0x%x "
                             		    "sflags=%x.\n", ha->host_no, cp->device->channel,
                            -		    cp->device->id, cp->device->lun, comp_status, scsi_status,
                            -		    le16_to_cpu(sts->status_flags)));
                            +		    cp->device->id, (unsigned long long)cp->device->lun, comp_status,
                            +		    scsi_status, le16_to_cpu(sts->status_flags)));
                             
                             		/* Check to see if logout occurred. */
                             		if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT))
                            diff --git a/qla2x00t/qla_mbx.c b/qla2x00t/qla_mbx.c
                            index f87985433..a7c7015ba 100644
                            --- a/qla2x00t/qla_mbx.c
                            +++ b/qla2x00t/qla_mbx.c
                            @@ -232,7 +232,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
                             			DEBUG2_3_11(printk("%s(%ld): timeout schedule "
                             			    "isp_abort_needed.\n", __func__, ha->host_no));
                             			qla_printk(KERN_WARNING, ha,
                            -			    "Mailbox command timeout occured. Scheduling ISP "
                            +			    "Mailbox command timeout occurred. Scheduling ISP "
                             			    "abort.\n");
                             			set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
                             			qla2xxx_wake_dpc(ha);
                            @@ -243,7 +243,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
                             			DEBUG2_3_11(printk("%s(%ld): timeout calling "
                             			    "abort_isp\n", __func__, ha->host_no));
                             			qla_printk(KERN_WARNING, ha,
                            -			    "Mailbox command timeout occured. Issuing ISP "
                            +			    "Mailbox command timeout occurred. Issuing ISP "
                             			    "abort.\n");
                             
                             			set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
                            @@ -3035,9 +3035,9 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
                             
                             	/*
                             	 * This command is implicitly executed by firmware during login for the
                            -	 * physical hosts
                            +	 * physical hosts if initiator mode is enabled.
                             	 */
                            -	if (vp_idx == 0)
                            +	if (vp_idx == 0 && (ha->host->active_mode & MODE_INITIATOR))
                             		return QLA_FUNCTION_FAILED;
                             
                             	mcp->mb[0] = MBC_SEND_CHANGE_REQUEST;
                            @@ -3058,6 +3058,7 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
                             
                             	return rval;
                             }
                            +EXPORT_SYMBOL(qla2x00_send_change_request);
                             
                             int
                             qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
                            diff --git a/qla2x00t/qla_os.c b/qla2x00t/qla_os.c
                            index b2b105615..5c7018eaf 100644
                            --- a/qla2x00t/qla_os.c
                            +++ b/qla2x00t/qla_os.c
                            @@ -909,8 +909,8 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
                             	if (!fcport)
                             		return FAILED;
                             
                            -	qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
                            -	    ha->host_no, cmd->device->id, cmd->device->lun, name);
                            +	qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET ISSUED.\n",
                            +	    ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name);
                             
                             	err = 0;
                             	if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
                            @@ -926,14 +926,14 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
                             	    cmd->device->lun, type) != QLA_SUCCESS)
                             		goto eh_reset_failed;
                             
                            -	qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
                            -	    ha->host_no, cmd->device->id, cmd->device->lun, name);
                            +	qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET SUCCEEDED.\n",
                            +	    ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name);
                             
                             	return SUCCESS;
                             
                              eh_reset_failed:
                            -	qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n",
                            -	    ha->host_no, cmd->device->id, cmd->device->lun, name,
                            +	qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%lld): %s RESET FAILED: %s.\n",
                            +	    ha->host_no, cmd->device->id, (unsigned long long)cmd->device->lun, name,
                             	    reset_errors[err]);
                             	return FAILED;
                             }
                            @@ -1228,8 +1228,8 @@ static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth)
                             		return;
                             
                             	DEBUG2(qla_printk(KERN_INFO, ha,
                            -		"scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n",
                            -		ha->host_no, sdev->channel, sdev->id, sdev->lun,
                            +		"scsi(%ld:%d:%d:%lld): Queue depth adjusted-down to %d.\n",
                            +		ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
                             		sdev->queue_depth));
                             }
                             
                            @@ -1246,8 +1246,8 @@ static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth)
                             		scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth);
                             
                             	DEBUG2(qla_printk(KERN_INFO, ha,
                            -	       "scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n",
                            -	       ha->host_no, sdev->channel, sdev->id, sdev->lun,
                            +	       "scsi(%ld:%d:%d:%lld): Queue depth adjusted-up to %d.\n",
                            +	       ha->host_no, sdev->channel, sdev->id, (unsigned long long)sdev->lun,
                             	       sdev->queue_depth));
                             }
                             
                            diff --git a/scripts/generate-kernel-patch b/scripts/generate-kernel-patch
                            index d41d188e9..dbcf623fe 100755
                            --- a/scripts/generate-kernel-patch
                            +++ b/scripts/generate-kernel-patch
                            @@ -107,16 +107,16 @@ EOF
                             # passed via stdin and send the specialized patch to stdout.
                             function specialize_patch {
                               if [ "${enable_specialize}" = "true" ]; then
                            +    if [ "${generating_upstream_patch}" = "true" ]; then
                            +      scripts/filter-trace-entry-exit
                            +    else
                            +      cat
                            +    fi |
                                 "$(dirname $0)/specialize-patch" \
                                      ${specialize_patch_options} \
                                      -v kernel_version="${kver3}" \
                                      -v SCSI_EXEC_REQ_FIFO_DEFINED="${scsi_exec_req_fifo_defined}" \
                            -         -v SCST_IO_CONTEXT="${scst_io_context}" \
                            -    | if [ "${generating_upstream_patch}" = "true" ]; then
                            -        scripts/filter-trace-entry-exit
                            -      else
                            -        cat
                            -      fi
                            +         -v SCST_IO_CONTEXT="${scst_io_context}"
                               else
                                 cat
                               fi
                            @@ -282,6 +282,7 @@ for p in scst/kernel/*-${kver}.patch \
                             		echo iscsi-scst/kernel/patches/*-${kver}.patch;
                             	   fi)
                             do
                            +  [ -e "$p" ] || continue
                               # Exclude the put_page_callback patch when command-line option -u has been
                               # specified since the current approach is not considered acceptable for
                               # upstream kernel inclusion. See also http://lkml.org/lkml/2008/12/11/213.
                            @@ -309,7 +310,7 @@ scst_07_pres="scst/src/scst_pres.h scst/src/scst_pres.c"
                             scst_08_sysfs="scst/src/scst_sysfs.c"
                             scst_09_debug="scst/include/scst_debug.h scst/src/scst_debug.c"
                             scst_proc="scst/src/scst_proc.c"
                            -scst_10_sgv="scst/include/scst_sgv.h scst/src/scst_mem.h scst/src/scst_mem.c doc/sgv_cache.sgml"
                            +scst_10_sgv="scst/include/scst_sgv.h scst/src/scst_mem.h scst/src/scst_mem.c doc/scst_pg.sgml"
                             scst_user="scst/include/scst_user.h scst/src/dev_handlers/scst_user.c"
                             scst_13_vdisk="scst/src/dev_handlers/scst_vdisk.c"
                             scst_14_tg="scst/src/scst_tg.c"
                            diff --git a/scripts/generate-release-archive b/scripts/generate-release-archive
                            index 1c4fe2d75..3980ac7dd 100755
                            --- a/scripts/generate-release-archive
                            +++ b/scripts/generate-release-archive
                            @@ -4,15 +4,19 @@ usage() {
                                 echo "Usage: $(basename $0) name version"
                             }
                             
                            -if [ $# != 2 ]; then
                            +if [ $# -lt 2 ]; then
                                 usage
                                 exit 1
                             fi
                             
                             scriptdir="$(dirname "$0")"
                            -name="$1"
                            -version="$2"
                            +name="$1"; shift
                            +version="$1"; shift
                            +files="$*"
                            +if [ -z "$files" ]; then
                            +    files=$($scriptdir/list-source-files)
                            +fi
                             
                             tar --owner=root --group=root --transform="s|^|$name-$version/|" \
                            -  -cjf $name-$version.tar.bz2 $($scriptdir/list-source-files) &&
                            +  -cjf $name-$version.tar.bz2 $files &&
                             ls -l $name-$version.tar.bz2
                            diff --git a/scripts/rebuild-rhel-kernel-rpm b/scripts/rebuild-rhel-kernel-rpm
                            index e4a96c1d7..5e92901df 100755
                            --- a/scripts/rebuild-rhel-kernel-rpm
                            +++ b/scripts/rebuild-rhel-kernel-rpm
                            @@ -220,14 +220,10 @@ cd SPECS
                             log "Copying SCST patches to the SOURCES directory"
                             
                             cd ${rpmbuild_dir}/SOURCES
                            -copy_best_matching_patch $scst_dir/scst/kernel/rhel/scst_exec_req_fifo scst_exec_req_fifo.patch ||
                            -{
                            -    echo "No matching scst_exec_req_fifo patch found for kernel version $kver";
                            -    exit 1;
                            -}
                            +copy_best_matching_patch $scst_dir/scst/kernel/rhel/scst_exec_req_fifo scst_exec_req_fifo.patch
                             copy_best_matching_patch $scst_dir/iscsi-scst/kernel/patches/rhel/put_page_callback put_page_callback.patch ||
                             {
                            -    echo "No matching scst_exec_req_fifo patch found for kernel version $kver";
                            +    echo "No matching put_page_callback patch found for kernel version $kver";
                                 exit 1;
                             }
                             
                            @@ -300,7 +296,7 @@ diff -u SPECS/kernel.spec{.orig,}
                              Source82: config-s390x-debug
                              Source83: config-s390x-debug-rhel
                              
                            -+Patch200: scst_exec_req_fifo.patch
                            ++#Patch200: scst_exec_req_fifo.patch
                             +Patch201: put_page_callback.patch
                             +
                              # empty final patch file to facilitate testing of kernel patches
                            @@ -310,7 +306,7 @@ diff -u SPECS/kernel.spec{.orig,}
                              # Dynamically generate kernel .config files from config-* files
                              make -f %{SOURCE20} VERSION=%{version} configs
                              
                            -+ApplyPatch scst_exec_req_fifo.patch
                            ++#ApplyPatch scst_exec_req_fifo.patch
                             +ApplyPatch put_page_callback.patch
                             +
                              ApplyOptionalPatch linux-kernel-test.patch
                            @@ -339,7 +335,7 @@ diff -u SPECS/kernel.spec{.orig,}
                              Source82: config-generic
                              Source83: config-x86_64-debug-rhel
                              
                            -+Patch200: scst_exec_req_fifo.patch
                            ++#Patch200: scst_exec_req_fifo.patch
                             +Patch201: put_page_callback.patch
                             +
                              # empty final patch file to facilitate testing of kernel patches
                            @@ -349,7 +345,7 @@ diff -u SPECS/kernel.spec{.orig,}
                              # Dynamically generate kernel .config files from config-* files
                              make -f %{SOURCE20} VERSION=%{version} configs
                              
                            -+ApplyPatch scst_exec_req_fifo.patch
                            ++#ApplyPatch scst_exec_req_fifo.patch
                             +ApplyPatch put_page_callback.patch
                             +
                              ApplyOptionalPatch linux-kernel-test.patch
                            @@ -365,6 +361,42 @@ diff -u SPECS/kernel.spec{.orig,}
                                make ARCH=$Arch %{oldconfig_target} > /dev/null
                                echo "# $Arch" > configs/$i
                             EOF
                            +elif [ ${kver#2.6.32-504.} != $kver ]; then
                            +# RHEL/CentOS/SL 6.6
                            +patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
                            +diff -u SPECS/kernel.spec{.orig,}
                            +--- kernel.spec.orig	2014-12-03 16:20:14.764118318 +0100
                            ++++ kernel.spec	2014-12-03 16:21:36.606089530 +0100
                            +@@ -610,6 +610,9 @@
                            + Source85: config-powerpc64-debug-rhel
                            + Source86: config-s390x-debug-rhel
                            + 
                            ++#Patch200: scst_exec_req_fifo.patch
                            ++Patch201: put_page_callback.patch
                            ++
                            + # empty final patch file to facilitate testing of kernel patches
                            + Patch999999: linux-kernel-test.patch
                            + 
                            +@@ -932,6 +935,9 @@
                            + # Dynamically generate kernel .config files from config-* files
                            + make -f %{SOURCE20} VERSION=%{version} configs
                            + 
                            ++#ApplyPatch scst_exec_req_fifo.patch
                            ++ApplyPatch put_page_callback.patch
                            ++
                            + ApplyOptionalPatch linux-kernel-test.patch
                            + 
                            + # Any further pre-build tree manipulations happen here.
                            +@@ -958,6 +964,8 @@
                            + for i in *.config
                            + do
                            +   mv $i .config
                            ++  echo "CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION=y" >> .config
                            ++  sed -i.tmp -e 's/^CONFIG_SCSI_QLA_FC=.*/CONFIG_SCSI_QLA_FC=n/' .config
                            +   Arch=`head -1 .config | cut -b 3-`
                            +   make ARCH=$Arch %{oldconfig_target} > /dev/null
                            +   echo "# $Arch" > configs/$i
                            +EOF
                             elif [ ${kver#3.10.0-12[13]} != $kver ]; then
                             # RHEL/CentOS/SL 7.0
                             patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
                            @@ -382,7 +414,7 @@ patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
                              Source2000: cpupower.service
                              Source2001: cpupower.config
                              
                            -+Patch200: scst_exec_req_fifo.patch
                            ++#Patch200: scst_exec_req_fifo.patch
                             +Patch201: put_page_callback.patch
                             +
                              # empty final patch to facilitate testing of kernel patches
                            @@ -392,7 +424,7 @@ patch -p1 ${rpmbuild_dir}/SPECS/kernel.spec <<'EOF' || exit $?
                              # Drop some necessary files from the source dir into the buildroot
                              cp $RPM_SOURCE_DIR/kernel-%{version}-*.config .
                              
                            -+ApplyPatch scst_exec_req_fifo.patch
                            ++#ApplyPatch scst_exec_req_fifo.patch
                             +ApplyPatch put_page_callback.patch
                             +
                              ApplyOptionalPatch linux-kernel-test.patch
                            diff --git a/scripts/specialize-patch b/scripts/specialize-patch
                            index ede8b0a19..dd0c842f2 100755
                            --- a/scripts/specialize-patch
                            +++ b/scripts/specialize-patch
                            @@ -251,7 +251,7 @@ function evaluate(stmnt, pattern, arg, op, result) {
                                   sub(pattern, op[1] == 0 ? op[2] : op[1], stmnt)
                                 }
                               
                            -    pattern="\\((-*[0-9]+)\\)"
                            +    pattern="\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)"
                                 while (match(stmnt, pattern, op) != 0)
                                 {
                                   sub(pattern, op[1], stmnt)
                            @@ -545,11 +545,17 @@ BEGIN {
                                 reset_hunk_state_variables()
                                 match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$", h)
                               }
                            -  else if (delete_next_blank_line && match($0, "^+$"))
                            +  else if (delete_next_blank_line && $0 == "+")
                               {
                                 discard = 1
                                 delete_next_blank_line = 0
                               }
                            +  else if (lines >= 2 && !match(line[lines-2], ":$") &&
                            +           line[lines-1] == "+\x9return;" && $0 == "+}")
                            +  {
                            +    line[lines-1] = $0
                            +    lines_less_added++
                            +  }
                               else
                               {
                                 delete_next_blank_line = 0
                            diff --git a/scst.spec.in b/scst.spec.in
                            index 7eebbc592..1e5786cf0 100644
                            --- a/scst.spec.in
                            +++ b/scst.spec.in
                            @@ -1,25 +1,66 @@
                             %define kmod_name scst
                            -%define kver %{expand:%%(echo ${KVER:-$(uname -r)})}
                            -%define kernel_rpm %{expand:%%(						\
                            -	  krpm="$(rpm -qf /boot/vmlinuz-%{kver} 2>/dev/null |		\
                            -		grep -v 'is not owned by any package' | head -n 1)";	\
                            -	  if [ -n "$krpm" ]; then					\
                            -	    echo "/boot/vmlinuz-%{kver}";				\
                            -	  else								\
                            -	    echo "%{nil}";					 	\
                            -	  fi								\
                            +# kversion: Kernel version as it appears under /lib/modules.
                            +# The algorithm for setting the variable kversion is as follows:
                            +# - If the variable kversion has been set, use its value.
                            +# - If an RPM with the name kernel-headers exists (RHEL / CentOS), use the
                            +#   version number of the kernel that package is based on. This provides the
                            +#   version number when building on a koji build server.
                            +# - Otherwise use the version number of the running kernel.
                            +%{!?kversion:%define kversion %{expand:%%(
                            +	    if rpm --quiet -q kernel-headers; then
                            +		rpm -q --qf '%%%%{version}-%%%%{release}.%%%%{arch}' \\
                            +		    kernel-headers;
                            +	    else
                            +		uname -r;
                            +	    fi
                            +	)}}
                            +%{echo:kversion=%{kversion}
                            +}
                            +# kernel_rpm: Name of the kernel RPM if the kernel is available as an RPM.
                            +%if %{expand:%%(rpm --quiet -q kernel-headers ||
                            +	        rpm --quiet -qf /lib/modules/%{kversion}/kernel/arch 2>/dev/null;
                            +		echo $((1-$?)))}
                            +%define kernel_rpm %{expand:%%(
                            +	    if rpm --quiet -q kernel-headers; then
                            +		echo kernel;
                            +	    else
                            +		rpm -q --qf '%%%%{name}\\n' \\
                            +		    "$(rpm -qf /lib/modules/%%{kversion}/kernel/arch | head -n1)";
                            +	    fi
                             	)}
                            +%endif
                            +# krpmver: Version of the kernel RPM. Not necessarily identical to %{kversion}.
                            +%{?kernel_rpm:%define krpmver %{expand:%%(
                            +	    if rpm --quiet -q %%{kernel_rpm}; then
                            +		rpm -q --qf '%%%%{version}-%%%%{release}\\n' "%%{kernel_rpm}";
                            +	    else
                            +		rpm -q --qf '%%%%{version}-%%%%{release}\\n' kernel-headers;
                            +	    fi |
                            +	    head -n1
                            +	)}}
                            +%{echo:krpmver=%{krpmver}
                            +}
                            +# kernel_devel_rpm: Name of the kernel development RPM.
                            +%{?kernel_rpm:%define kernel_devel_rpm %{kernel_rpm}-devel}
                            +# Version of the RPM that is being built.
                             %define rpm_version @rpm_version@
                            +# Make command with or without flags.
                             %define make %{expand:%%(echo ${MAKE:-make})}
                            +%define pkgrel 1
                            +%define dkms_version %{rpm_version}-%{pkgrel}%{?dist}
                             
                            -Name:		%{kmod_name}-%{kver}
                            -Version:        %{rpm_version}
                            -Release:        1
                            -Summary:	SCST mid-layer kernel driver
                            +Name:		%{kmod_name}
                            +Version:	%{rpm_version}
                            +Release:	%{pkgrel}%{?dist}
                            +Summary:	SCST mid-layer kernel drivers
                             Group:		System/Kernel
                             License:	GPLv2
                             Vendor:		http://scst.sourceforge.net/
                             URL:		http://scst.sourceforge.net/
                            +# Unfortunately the Red Hat / CentOS kernel-debug-devel RPM provides
                            +# kernel-devel so a workaround is needed to match the kernel-devel RPM.
                            +%{?kernel_rpm:Requires:	%{kernel_rpm} = %{krpmver}}
                            +BuildRequires:	%{?kernel_rpm:%{kernel_rpm} = %{krpmver} %{kernel_devel_rpm} = %{krpmver}} gcc make
                             
                             Source:		%{kmod_name}-%{version}.tar.bz2
                             BuildRoot:	%{_tmppath}/%{name}-%{version}-build
                            @@ -40,10 +81,26 @@ Authors:
                             --------
                                 Vladislav Bolkhovitin, Bart Van Assche and others
                             
                            +%package userspace
                            +Summary:	SCST mid-layer user space software
                            +Group:		Development/Kernel
                            +Requires:	scst-dkms
                            +
                            +%description userspace
                            +A generic SCSI target subsystem for Linux that allows to convert any Linux
                            +server into a sophisticated storage server. The three layers in SCST are the
                            +target driver layer; the SCSI target core and the device handler layer. SCST
                            +target drivers realize communication with an initiator and implement a storage
                            +protocol like iSCSI, FC or SRP. SCST device handlers implement a SCSI
                            +interface on top of local storage. Examples of such local storage are SCSI
                            +RAID controller (dev_disk handler), block device (vdisk_blockio handler), file
                            +(vdisk_fileio handler) or custom block device behavior implemented in user
                            +space (scst_user).
                            +
                             %package devel
                             Summary:	SCST mid-layer kernel driver development package
                             Group:		Development/Kernel
                            -AutoReqProv:	no
                            +BuildArch:	noarch
                             
                             %description devel
                             A generic SCSI target subsystem for Linux (SCST) that allows to convert
                            @@ -53,6 +110,25 @@ provide access to a local SCSI RAID controller (dev_disk), block device
                             (vdisk_blockio), file (vdisk_fileio) or custom block device behavior
                             implemented in user space (scst_user).
                             
                            +Authors:
                            +--------
                            +    Vladislav Bolkhovitin, Bart Van Assche and others
                            +
                            +%package dkms
                            +Summary:	DKMS-enabled SCST source code package
                            +Group:		System/Kernel
                            +BuildArch:	noarch
                            +Requires(pre):	dkms gcc make
                            +Requires(post):	dkms
                            +
                            +%description dkms
                            +A generic SCSI target subsystem for Linux (SCST) that allows to convert
                            +any Linux server into a sophisticated storage server. SCST target drivers
                            +implement protocols like iSCSI, FC or SRP. SCST device handlers either
                            +provide access to a local SCSI RAID controller (dev_disk), block device
                            +(vdisk_blockio), file (vdisk_fileio) or custom block device behavior
                            +implemented in user space (scst_user).
                            +
                             Authors:
                             --------
                                 Vladislav Bolkhovitin, Bart Van Assche and others
                            @@ -62,14 +138,14 @@ Authors:
                             %setup -q -n %{kmod_name}-%{version}
                             
                             %build
                            -export KVER=%{kver} PREFIX=%{_prefix}
                            +export KVER=%{kversion} PREFIX=%{_prefix}
                             export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y
                             for d in scst fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do
                                 %{make} -C $d
                             done
                             
                             %install
                            -export KVER=%{kver} PREFIX=%{_prefix} MANDIR=%{_mandir}
                            +export KVER=%{kversion} PREFIX=%{_prefix} MANDIR=%{_mandir}
                             export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y
                             for d in scst; do
                                 DESTDIR=%{buildroot} %{make} -C $d install
                            @@ -77,12 +153,72 @@ done
                             for d in fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do
                                 DESTDIR=%{buildroot} INSTALL_MOD_PATH=%{buildroot} %{make} -C $d install
                             done
                            -rm -f %{buildroot}/lib/modules/%{kver}/[Mm]odule*
                            +rm -f %{buildroot}/lib/modules/%{kversion}/[Mm]odule*
                            +
                            +install -d -m 755 %{buildroot}/usr/src/%{kmod_name}-%{dkms_version}
                            +(
                            +    cd %{buildroot}/usr/src/%{kmod_name}-%{dkms_version} &&
                            +    tar --strip-components=1 -xaf %{SOURCE0}
                            +)
                            +cat >%{buildroot}/usr/src/%{kmod_name}-%{dkms_version}/dkms.conf <<"EOF"
                            +PACKAGE_VERSION="%{dkms_version}"
                            +PACKAGE_NAME="%{kmod_name}"
                            +AUTOINSTALL=yes
                            +MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t/qla2x00-target && make -sC scst_local && make -sC srpt && cp */*.ko */*/*.ko */*/*/*.ko ."
                            +CLEAN="make clean"
                            +# Remove any existing ib_srpt.ko kernel modules
                            +PRE_INSTALL="find /lib/modules/${kernelver} -name ib_srpt.ko -exec rm {} \;"
                            +BUILT_MODULE_NAME[ 0]="fcst"
                            +BUILT_MODULE_NAME[ 1]="ib_srpt"
                            +BUILT_MODULE_NAME[ 2]="iscsi-scst"
                            +BUILT_MODULE_NAME[ 3]="qla2x00tgt"
                            +BUILT_MODULE_NAME[ 4]="qla2xxx_scst"
                            +BUILT_MODULE_NAME[ 5]="scst"
                            +BUILT_MODULE_NAME[ 6]="scst_local"
                            +BUILT_MODULE_NAME[ 7]="scst_cdrom"
                            +BUILT_MODULE_NAME[ 8]="scst_changer"
                            +BUILT_MODULE_NAME[ 9]="scst_disk"
                            +BUILT_MODULE_NAME[10]="scst_modisk"
                            +BUILT_MODULE_NAME[11]="scst_processor"
                            +BUILT_MODULE_NAME[12]="scst_raid"
                            +BUILT_MODULE_NAME[13]="scst_tape"
                            +BUILT_MODULE_NAME[14]="scst_user"
                            +BUILT_MODULE_NAME[15]="scst_vdisk"
                            +BUILT_MODULE_NAME[16]="isert-scst"
                            +DEST_MODULE_LOCATION[ 0]="/extra"
                            +DEST_MODULE_LOCATION[ 1]="/extra"
                            +DEST_MODULE_LOCATION[ 2]="/extra"
                            +DEST_MODULE_LOCATION[ 3]="/extra"
                            +DEST_MODULE_LOCATION[ 4]="/extra"
                            +DEST_MODULE_LOCATION[ 5]="/extra"
                            +DEST_MODULE_LOCATION[ 6]="/extra"
                            +DEST_MODULE_LOCATION[ 7]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[ 8]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[ 9]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[10]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[11]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[12]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[13]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[14]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[15]="/extra/dev_handlers"
                            +DEST_MODULE_LOCATION[16]="/extra"
                            +EOF
                             
                             %clean
                             rm -rf %{buildroot}
                             
                             %pre
                            +# Remove any existing ib_srpt.ko kernel modules
                            +find /lib/modules/%{kversion} -name ib_srpt.ko -exec rm {} \;
                            +# Remove files installed by "make install"
                            +rm -f /usr/local/man/man5/iscsi-scstd.conf.5
                            +rm -f /usr/local/man/man8/iscsi-scst-adm.8
                            +rm -f /usr/local/man/man8/iscsi-scstd.8
                            +rm -f /usr/local/sbin/iscsi-scst-adm
                            +rm -f /usr/local/sbin/iscsi-scstd
                            +rm -rf /usr/local/include/scst
                            +
                            +%pre userspace
                             # Remove files installed by "make install"
                             rm -f /usr/local/man/man5/iscsi-scstd.conf.5
                             rm -f /usr/local/man/man8/iscsi-scst-adm.8
                            @@ -90,33 +226,49 @@ rm -f /usr/local/man/man8/iscsi-scstd.8
                             rm -f /usr/local/sbin/iscsi-scst-adm
                             rm -f /usr/local/sbin/iscsi-scstd
                             rm -rf /usr/local/include/scst
                            -# Remove existing ib_srpt.ko kernel modules
                            -find /lib/modules/%{kver} -name ib_srpt.ko -exec rm {} \;
                             
                             %post
                            -/sbin/depmod -a %{kver}
                            +/sbin/depmod -a %{kversion}
                            +
                            +%post dkms
                            +dkms add -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade
                            +dkms build -m %{kmod_name} -v %{dkms_version}
                            +dkms install -m %{kmod_name} -v %{dkms_version}
                            +
                            +%preun dkms
                            +dkms remove -m %{kmod_name} -v %{dkms_version} --rpm_safe_upgrade --all
                            +true
                             
                             %files
                             %defattr(-,root,root)
                            -%dir /lib/modules/%{kver}/extra
                            -/lib/modules/%{kver}/extra/fcst.ko
                            -/lib/modules/%{kver}/extra/ib_srpt.ko
                            -/lib/modules/%{kver}/extra/iscsi-scst.ko
                            -/lib/modules/%{kver}/extra/isert-scst.ko
                            -/lib/modules/%{kver}/extra/qla2x00tgt.ko
                            -/lib/modules/%{kver}/extra/qla2xxx_scst.ko
                            -/lib/modules/%{kver}/extra/scst.ko
                            -/lib/modules/%{kver}/extra/scst_local.ko
                            -%dir /lib/modules/%{kver}/extra/dev_handlers
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_cdrom.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_changer.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_disk.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_modisk.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_processor.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_raid.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_tape.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_user.ko
                            -/lib/modules/%{kver}/extra/dev_handlers/scst_vdisk.ko
                            +%dir /lib/modules/%{kversion}/extra
                            +/lib/modules/%{kversion}/extra/fcst.ko
                            +/lib/modules/%{kversion}/extra/ib_srpt.ko
                            +/lib/modules/%{kversion}/extra/iscsi-scst.ko
                            +/lib/modules/%{kversion}/extra/isert-scst.ko
                            +/lib/modules/%{kversion}/extra/qla2x00tgt.ko
                            +/lib/modules/%{kversion}/extra/qla2xxx_scst.ko
                            +/lib/modules/%{kversion}/extra/scst.ko
                            +/lib/modules/%{kversion}/extra/scst_local.ko
                            +%dir /lib/modules/%{kversion}/extra/dev_handlers
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_cdrom.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_changer.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_disk.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_modisk.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_processor.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_raid.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_tape.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_user.ko
                            +/lib/modules/%{kversion}/extra/dev_handlers/scst_vdisk.ko
                            +%{_mandir}/man5/iscsi-scstd.conf.5.gz
                            +%{_mandir}/man8/iscsi-scst-adm.8.gz
                            +%{_mandir}/man8/iscsi-scstd.8.gz
                            +%{_sbindir}/iscsi-scst-adm
                            +%{_sbindir}/iscsi-scstd
                            +%dir /var/lib/scst/pr
                            +%dir /var/lib/scst/vdev_mode_pages
                            +
                            +%files userspace
                             %{_mandir}/man5/iscsi-scstd.conf.5.gz
                             %{_mandir}/man8/iscsi-scst-adm.8.gz
                             %{_mandir}/man8/iscsi-scstd.8.gz
                            @@ -136,6 +288,12 @@ find /lib/modules/%{kver} -name ib_srpt.ko -exec rm {} \;
                             /usr/include/scst/scst_sgv.h
                             /usr/include/scst/scst_user.h
                             
                            +%files dkms
                            +%defattr(-,root,root)
                            +/usr/src/%{kmod_name}-%{dkms_version}/
                            +
                             %changelog
                            +* Fri Jan 16 2015 Bart Van Assche 
                            +- Added DKMS support.
                             * Fri Nov 22 2013 Bart Van Assche 
                             - Initial spec file.
                            diff --git a/scst/Makefile b/scst/Makefile
                            index bcd0770e4..44cdd1cdd 100644
                            --- a/scst/Makefile
                            +++ b/scst/Makefile
                            @@ -54,7 +54,9 @@ enable_proc:
                             	cd $(SCST_DIR) && $(MAKE) $@
                             
                             release-archive:
                            -	../scripts/generate-release-archive scst "$$(sed -n 's/^#define[[:blank:]]SCST_VERSION_NAME[[:blank:]]*\"\([^\"]*\)\".*/\1/p' include/scst_const.h)"
                            +	../scripts/generate-release-archive scst \
                            +	  "$$(sed -n 's/^#define[[:blank:]]SCST_VERSION_NAME[[:blank:]]*\"\([^\"]*\)\".*/\1/p' include/scst_const.h)" \
                            +	  $(shell list-source-files) ../scripts/rebuild-rhel-kernel-rpm
                             
                             help:
                             	@echo "		all (the default) : make all"
                            diff --git a/scst/README b/scst/README
                            index c2d6555c5..b7eff48e0 100644
                            --- a/scst/README
                            +++ b/scst/README
                            @@ -70,27 +70,13 @@ following patches for the kernel in the "kernel" subdirectory. All of
                             them are optional, so, if you don't need the corresponding
                             functionality, you may not apply them.
                             
                            -1. scst_exec_req_fifo-2.6.X.patch. This patch is necessary for
                            -pass-through dev handlers, because in the mainstream kernels
                            -scsi_do_req()/scsi_execute_async() work in LIFO order, instead of
                            -expected and required FIFO. So SCST needs new functions
                            -scsi_do_req_fifo() or scsi_execute_async_fifo() to be added in the
                            -kernel. This patch does that. You may not patch the kernel if you don't
                            -need the pass-through support. Alternatively, you can define
                            -CONFIG_SCST_STRICT_SERIALIZING compile option during the compilation
                            -(see description below). Unfortunately, the CONFIG_SCST_STRICT_SERIALIZING
                            -trick doesn't work on kernels starting from 2.6.30, because those
                            -kernels don't have the required functionality (scsi_execute_async())
                            -anymore. So, on them to have pass-through working you have to apply
                            -scst_exec_req_fifo-2.6.X.patch.
                            -
                            -2. readahead-2.6.X.patch. This patch fixes problem in Linux readahead
                            +1. readahead-2.6.X.patch. This patch fixes problem in Linux readahead
                             subsystem and greatly improves performance for software RAIDs. See
                             http://sourceforge.net/mailarchive/forum.php?thread_name=a0272b440906030714g67eabc5k8f847fb1e538cc62%40mail.gmail.com&forum_name=scst-devel
                             thread for more details. It is included in the mainstream kernels 2.6.33
                             and 2.6.32.11.
                             
                            -3. readahead-context-2.6.X.patch. This is backported from 2.6.31 version
                            +2. readahead-context-2.6.X.patch. This is backported from 2.6.31 version
                             of the context readahead patch http://lkml.org/lkml/2009/4/12/9, big
                             thanks to Wu Fengguang. This is a performance improvement patch. It is
                             included in the mainstream kernel 2.6.31.
                            @@ -1088,7 +1074,7 @@ Each vdisk_fileio's device has the following attributes in
                                reported in the SCSI device identification VPD page. If eui64_id has
                                been set the value of this attribute is reported as the EUI-64 ID. The
                                first three bytes of an EUI-64 ID are a so-called organizationally
                            -   unique identifier (OUI). The remaining bytes may be choosen by the
                            +   unique identifier (OUI). The remaining bytes may be chosen by the
                                organization that owns the OUI. For more information about OUIs, see
                                also http://standards.ieee.org/develop/regauth/oui/public.html.
                             
                            diff --git a/scst/README_in-tree b/scst/README_in-tree
                            index 67210ed60..553fac3e6 100644
                            --- a/scst/README_in-tree
                            +++ b/scst/README_in-tree
                            @@ -935,7 +935,7 @@ Each vdisk_fileio's device has the following attributes in
                                reported in the SCSI device identification VPD page. If eui64_id has
                                been set the value of this attribute is reported as the EUI-64 ID. The
                                first three bytes of an EUI-64 ID are a so-called organizationally
                            -   unique identifier (OUI). The remaining bytes may be choosen by the
                            +   unique identifier (OUI). The remaining bytes may be chosen by the
                                organization that owns the OUI. For more information about OUIs, see
                                also http://standards.ieee.org/develop/regauth/oui/public.html.
                             
                            diff --git a/scst/include/scst.h b/scst/include/scst.h
                            index b47ce4b7e..51b84bc59 100644
                            --- a/scst/include/scst.h
                            +++ b/scst/include/scst.h
                            @@ -128,6 +128,14 @@ char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap);
                             #define nr_cpu_ids NR_CPUS
                             #endif
                             
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
                            +/*
                            + * See also patch "fix abuses of ptrdiff_t" (commit ID
                            + * 142956af525002c5378e7d91d81a01189841a785).
                            + */
                            +typedef unsigned long uintptr_t;
                            +#endif
                            +
                             #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
                             #define cpumask_bits(maskp) ((maskp)->bits)
                             #ifdef CONFIG_CPUMASK_OFFSTACK
                            @@ -1404,7 +1412,7 @@ struct scst_dev_type {
                             	int (*dev_done)(struct scst_cmd *cmd);
                             
                             	/*
                            -	 * Called to notify dev hander that the command is about to be freed.
                            +	 * Called to notify dev handler that the command is about to be freed.
                             	 *
                             	 * Could be called on IRQ context.
                             	 *
                            @@ -2505,7 +2513,7 @@ struct scst_device {
                             
                             	/*
                             	 * Set, if this device is being unregistered. Useful to let sysfs
                            -	 * attributes know when they should exit immediatelly to prevent
                            +	 * attributes know when they should exit immediately to prevent
                             	 * possible deadlocks with their device unregistration waiting for
                             	 * their kobj last put.
                             	 */
                            @@ -4373,9 +4381,12 @@ static inline int cancel_delayed_work_sync(struct delayed_work *work)
                             #endif
                             #endif
                             
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && defined(CONFIG_LOCKDEP)
                            +extern struct lockdep_map scst_suspend_dep_map;
                            +#endif
                            +
                             #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && \
                             	defined(CONFIG_DEBUG_LOCK_ALLOC)
                            -extern struct lockdep_map scst_suspend_dep_map;
                             #define scst_assert_activity_suspended()		\
                             	WARN_ON(debug_locks && !lock_is_held(&scst_suspend_dep_map))
                             #else
                            @@ -4848,7 +4859,7 @@ void scst_init_threads(struct scst_cmd_threads *cmd_threads);
                             void scst_deinit_threads(struct scst_cmd_threads *cmd_threads);
                             
                             void scst_pass_through_cmd_done(void *data, char *sense, int result, int resid);
                            -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
                             int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
                             	void (*done)(void *data, char *sense, int result, int resid));
                             #endif
                            diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h
                            index 0250c4a1c..022f0ec77 100644
                            --- a/scst/include/scst_const.h
                            +++ b/scst/include/scst_const.h
                            @@ -292,7 +292,6 @@ static inline int scst_sense_response_code(const uint8_t *sense)
                             #define scst_sense_format_in_progress		NOT_READY,       0x04, 0x04
                             #define scst_sense_tp_transitioning		NOT_READY,	 0x04, 0x0A
                             #define scst_sense_tp_unav			NOT_READY,	 0x04, 0x0C
                            -#define scst_sense_not_ready			NOT_READY,       0x04, 0x10
                             #define scst_sense_no_medium			NOT_READY,       0x3a, 0
                             
                             /* MEDIUM_ERROR is 3 */
                            @@ -300,7 +299,7 @@ static inline int scst_sense_response_code(const uint8_t *sense)
                             #define scst_sense_read_error			MEDIUM_ERROR,    0x11, 0
                             
                             /* HARDWARE_ERROR is 4 */
                            -#define scst_sense_hardw_error			HARDWARE_ERROR,  0x44, 0
                            +#define scst_sense_hardw_error			HARDWARE_ERROR,  0x44, 0 /* non-retriable */
                             
                             /* ILLEGAL_REQUEST is 5 */
                             #define scst_sense_invalid_opcode		ILLEGAL_REQUEST, 0x20, 0
                            @@ -335,6 +334,7 @@ static inline int scst_sense_response_code(const uint8_t *sense)
                             
                             /* ABORTED_COMMAND is 0xb */
                             #define scst_sense_aborted_command		ABORTED_COMMAND, 0x00, 0
                            +#define scst_sense_internal_failure		ABORTED_COMMAND, 0x44, 0 /* retriable */
                             
                             /* MISCOMPARE is 0xe */
                             #define scst_sense_miscompare_error		MISCOMPARE,      0x1D, 0
                            diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-3.17.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.17.patch
                            new file mode 100644
                            index 000000000..0d5a19f0f
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.17.patch
                            @@ -0,0 +1,13 @@
                            +diff --git a/drivers/Kconfig b/drivers/Kconfig
                            +index aa43b91..c96860e 100644
                            +--- a/drivers/Kconfig
                            ++++ b/drivers/Kconfig
                            +@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig"
                            + 
                            + source "drivers/scsi/Kconfig"
                            + 
                            ++source "drivers/scst/Kconfig"
                            ++
                            + source "drivers/ata/Kconfig"
                            + 
                            + source "drivers/md/Kconfig"
                            diff --git a/scst/kernel/in-tree/Kconfig.drivers.Linux-3.18.patch b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.18.patch
                            new file mode 100644
                            index 000000000..0d5a19f0f
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Kconfig.drivers.Linux-3.18.patch
                            @@ -0,0 +1,13 @@
                            +diff --git a/drivers/Kconfig b/drivers/Kconfig
                            +index aa43b91..c96860e 100644
                            +--- a/drivers/Kconfig
                            ++++ b/drivers/Kconfig
                            +@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig"
                            + 
                            + source "drivers/scsi/Kconfig"
                            + 
                            ++source "drivers/scst/Kconfig"
                            ++
                            + source "drivers/ata/Kconfig"
                            + 
                            + source "drivers/md/Kconfig"
                            diff --git a/scst/kernel/in-tree/Makefile.dev_handlers-3.17 b/scst/kernel/in-tree/Makefile.dev_handlers-3.17
                            new file mode 100644
                            index 000000000..f933b36f7
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Makefile.dev_handlers-3.17
                            @@ -0,0 +1,14 @@
                            +ccflags-y += -Wno-unused-parameter
                            +
                            +obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
                            +	scst_vdisk.o scst_raid.o scst_processor.o scst_user.o
                            +
                            +obj-$(CONFIG_SCST_DISK)		+= scst_disk.o
                            +obj-$(CONFIG_SCST_TAPE)		+= scst_tape.o
                            +obj-$(CONFIG_SCST_CDROM)	+= scst_cdrom.o
                            +obj-$(CONFIG_SCST_MODISK)	+= scst_modisk.o
                            +obj-$(CONFIG_SCST_CHANGER)	+= scst_changer.o
                            +obj-$(CONFIG_SCST_RAID)		+= scst_raid.o
                            +obj-$(CONFIG_SCST_PROCESSOR)	+= scst_processor.o
                            +obj-$(CONFIG_SCST_VDISK)	+= scst_vdisk.o
                            +obj-$(CONFIG_SCST_USER)		+= scst_user.o
                            diff --git a/scst/kernel/in-tree/Makefile.dev_handlers-3.18 b/scst/kernel/in-tree/Makefile.dev_handlers-3.18
                            new file mode 100644
                            index 000000000..f933b36f7
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Makefile.dev_handlers-3.18
                            @@ -0,0 +1,14 @@
                            +ccflags-y += -Wno-unused-parameter
                            +
                            +obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
                            +	scst_vdisk.o scst_raid.o scst_processor.o scst_user.o
                            +
                            +obj-$(CONFIG_SCST_DISK)		+= scst_disk.o
                            +obj-$(CONFIG_SCST_TAPE)		+= scst_tape.o
                            +obj-$(CONFIG_SCST_CDROM)	+= scst_cdrom.o
                            +obj-$(CONFIG_SCST_MODISK)	+= scst_modisk.o
                            +obj-$(CONFIG_SCST_CHANGER)	+= scst_changer.o
                            +obj-$(CONFIG_SCST_RAID)		+= scst_raid.o
                            +obj-$(CONFIG_SCST_PROCESSOR)	+= scst_processor.o
                            +obj-$(CONFIG_SCST_VDISK)	+= scst_vdisk.o
                            +obj-$(CONFIG_SCST_USER)		+= scst_user.o
                            diff --git a/scst/kernel/in-tree/Makefile.drivers.Linux-3.17.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-3.17.patch
                            new file mode 100644
                            index 000000000..f7213ed4c
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-3.17.patch
                            @@ -0,0 +1,12 @@
                            +diff --git a/drivers/Makefile b/drivers/Makefile
                            +index ab93de8..45077ec 100644
                            +--- a/drivers/Makefile
                            ++++ b/drivers/Makefile
                            +@@ -128,6 +128,7 @@ obj-$(CONFIG_SSB)		+= ssb/
                            + obj-$(CONFIG_BCMA)		+= bcma/
                            + obj-$(CONFIG_VHOST_RING)	+= vhost/
                            + obj-$(CONFIG_VLYNQ)		+= vlynq/
                            ++obj-$(CONFIG_SCST)		+= scst/
                            + obj-$(CONFIG_STAGING)		+= staging/
                            + obj-y				+= platform/
                            + #common clk code
                            diff --git a/scst/kernel/in-tree/Makefile.drivers.Linux-3.18.patch b/scst/kernel/in-tree/Makefile.drivers.Linux-3.18.patch
                            new file mode 100644
                            index 000000000..4d482c340
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Makefile.drivers.Linux-3.18.patch
                            @@ -0,0 +1,12 @@
                            +diff --git a/drivers/Makefile b/drivers/Makefile
                            +index ebee555..17f67ae 100644
                            +--- a/drivers/Makefile
                            ++++ b/drivers/Makefile
                            +@@ -134,6 +134,7 @@ obj-$(CONFIG_SSB)		+= ssb/
                            + obj-$(CONFIG_BCMA)		+= bcma/
                            + obj-$(CONFIG_VHOST_RING)	+= vhost/
                            + obj-$(CONFIG_VLYNQ)		+= vlynq/
                            ++obj-$(CONFIG_SCST)		+= scst/
                            + obj-$(CONFIG_STAGING)		+= staging/
                            + obj-y				+= platform/
                            + #common clk code
                            diff --git a/scst/kernel/in-tree/Makefile.scst-3.17 b/scst/kernel/in-tree/Makefile.scst-3.17
                            new file mode 100644
                            index 000000000..53af5f388
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Makefile.scst-3.17
                            @@ -0,0 +1,13 @@
                            +ccflags-y += -Wno-unused-parameter
                            +
                            +scst-y        += scst_main.o
                            +scst-y        += scst_pres.o
                            +scst-y        += scst_targ.o
                            +scst-y        += scst_lib.o
                            +scst-y        += scst_sysfs.o
                            +scst-y        += scst_mem.o
                            +scst-y        += scst_tg.o
                            +scst-y        += scst_debug.o
                            +
                            +obj-$(CONFIG_SCST)   += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \
                            +			srpt/ scst_local/
                            diff --git a/scst/kernel/in-tree/Makefile.scst-3.18 b/scst/kernel/in-tree/Makefile.scst-3.18
                            new file mode 100644
                            index 000000000..53af5f388
                            --- /dev/null
                            +++ b/scst/kernel/in-tree/Makefile.scst-3.18
                            @@ -0,0 +1,13 @@
                            +ccflags-y += -Wno-unused-parameter
                            +
                            +scst-y        += scst_main.o
                            +scst-y        += scst_pres.o
                            +scst-y        += scst_targ.o
                            +scst-y        += scst_lib.o
                            +scst-y        += scst_sysfs.o
                            +scst-y        += scst_mem.o
                            +scst-y        += scst_tg.o
                            +scst-y        += scst_debug.o
                            +
                            +obj-$(CONFIG_SCST)   += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \
                            +			srpt/ scst_local/
                            diff --git a/scst/kernel/rhel/scst_exec_req_fifo-2.6.32.patch b/scst/kernel/rhel/scst_exec_req_fifo-2.6.32.patch
                            deleted file mode 100644
                            index bcb3b02e2..000000000
                            --- a/scst/kernel/rhel/scst_exec_req_fifo-2.6.32.patch
                            +++ /dev/null
                            @@ -1,529 +0,0 @@
                            -diff -upkr linux-2.6.32/block/blk-map.c linux-2.6.32/block/blk-map.c
                            ---- linux-2.6.32/block/blk-map.c	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/block/blk-map.c	2011-05-17 20:56:18.341812997 -0400
                            -@@ -5,6 +5,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res = 0;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= 1 << BIO_RW;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.32/include/linux/blkdev.h linux-2.6.32/include/linux/blkdev.h
                            ---- linux-2.6.32/include/linux/blkdev.h	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/include/linux/blkdev.h	2009-12-16 07:21:35.000000000 -0500
                            -@@ -708,6 +708,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -812,6 +814,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.32/include/linux/scatterlist.h linux-2.6.32/include/linux/scatterlist.h
                            ---- linux-2.6.32/include/linux/scatterlist.h	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/include/linux/scatterlist.h	2009-12-16 07:21:35.000000000 -0500
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.32/lib/scatterlist.c linux-2.6.32/lib/scatterlist.c
                            ---- linux-2.6.32/lib/scatterlist.c	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/lib/scatterlist.c	2009-12-16 07:21:35.000000000 -0500
                            -@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.patch b/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.patch
                            deleted file mode 120000
                            index 6a3acd053..000000000
                            --- a/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-121.patch
                            +++ /dev/null
                            @@ -1 +0,0 @@
                            -../scst_exec_req_fifo-3.10.patch
                            \ No newline at end of file
                            diff --git a/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-123.patch b/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-123.patch
                            deleted file mode 100644
                            index d60ddd1de..000000000
                            --- a/scst/kernel/rhel/scst_exec_req_fifo-3.10.0-123.patch
                            +++ /dev/null
                            @@ -1,524 +0,0 @@
                            -diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/block/blk-map.c ./block/blk-map.c
                            ---- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/block/blk-map.c	2014-07-16 20:25:31.000000000 +0200
                            -+++ ./block/blk-map.c	2014-08-07 09:09:11.751302961 +0200
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/blkdev.h ./include/linux/blkdev.h
                            ---- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/blkdev.h	2014-07-16 20:25:31.000000000 +0200
                            -+++ ./include/linux/blkdev.h	2014-08-07 09:09:11.751302961 +0200
                            -@@ -719,6 +719,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -838,6 +840,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/scatterlist.h ./include/linux/scatterlist.h
                            ---- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/include/linux/scatterlist.h	2014-07-16 20:25:31.000000000 +0200
                            -+++ ./include/linux/scatterlist.h	2014-08-07 09:09:11.751302961 +0200
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -244,6 +245,9 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -rup ../../centos-7-orig/linux-3.10.0-123.6.3.el7/lib/scatterlist.c ./lib/scatterlist.c
                            ---- ../../centos-7-orig/linux-3.10.0-123.6.3.el7/lib/scatterlist.c	2014-07-16 20:25:31.000000000 +0200
                            -+++ ./lib/scatterlist.c	2014-08-07 09:09:11.751302961 +0200
                            -@@ -628,3 +628,126 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.30.patch b/scst/kernel/scst_exec_req_fifo-2.6.30.patch
                            deleted file mode 100644
                            index 9b9cb78e1..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.30.patch
                            +++ /dev/null
                            @@ -1,529 +0,0 @@
                            -diff -upkr linux-2.6.30/block/blk-map.c linux-2.6.30/block/blk-map.c
                            ---- linux-2.6.30/block/blk-map.c	2009-06-09 23:05:27.000000000 -0400
                            -+++ linux-2.6.30/block/blk-map.c	2011-05-17 21:03:29.661813000 -0400
                            -@@ -5,6 +5,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -272,6 +273,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= 1 << BIO_RW;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = rq->data = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = rq->data = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = 0;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.30/include/linux/blkdev.h linux-2.6.30/include/linux/blkdev.h
                            ---- linux-2.6.30/include/linux/blkdev.h	2009-06-09 23:05:27.000000000 -0400
                            -+++ linux-2.6.30/include/linux/blkdev.h	2009-08-12 11:48:06.000000000 -0400
                            -@@ -704,6 +704,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -807,6 +809,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.30/include/linux/scatterlist.h linux-2.6.30/include/linux/scatterlist.h
                            ---- linux-2.6.30/include/linux/scatterlist.h	2009-06-09 23:05:27.000000000 -0400
                            -+++ linux-2.6.30/include/linux/scatterlist.h	2009-08-12 11:50:02.000000000 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.30/lib/scatterlist.c linux-2.6.30/lib/scatterlist.c
                            ---- linux-2.6.30/lib/scatterlist.c	2009-06-09 23:05:27.000000000 -0400
                            -+++ linux-2.6.30/lib/scatterlist.c	2009-08-12 11:56:04.000000000 -0400
                            -@@ -485,3 +485,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.31.patch b/scst/kernel/scst_exec_req_fifo-2.6.31.patch
                            deleted file mode 100644
                            index 0d9c06e6d..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.31.patch
                            +++ /dev/null
                            @@ -1,529 +0,0 @@
                            -diff -upkr linux-2.6.31/block/blk-map.c linux-2.6.31/block/blk-map.c
                            ---- linux-2.6.31/block/blk-map.c	2009-09-09 18:13:59.000000000 -0400
                            -+++ linux-2.6.31/block/blk-map.c	2011-05-17 21:05:32.669812993 -0400
                            -@@ -5,6 +5,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= 1 << BIO_RW;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.31/include/linux/blkdev.h linux-2.6.31/include/linux/blkdev.h
                            ---- linux-2.6.31/include/linux/blkdev.h	2009-09-09 18:13:59.000000000 -0400
                            -+++ linux-2.6.31/include/linux/blkdev.h	2009-09-23 06:17:33.000000000 -0400
                            -@@ -699,6 +699,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -803,6 +805,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.31/include/linux/scatterlist.h linux-2.6.31/include/linux/scatterlist.h
                            ---- linux-2.6.31/include/linux/scatterlist.h	2009-09-09 18:13:59.000000000 -0400
                            -+++ linux-2.6.31/include/linux/scatterlist.h	2009-09-23 06:17:33.000000000 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.31/lib/scatterlist.c linux-2.6.31/lib/scatterlist.c
                            ---- linux-2.6.31/lib/scatterlist.c	2009-09-09 18:13:59.000000000 -0400
                            -+++ linux-2.6.31/lib/scatterlist.c	2009-09-23 06:17:33.000000000 -0400
                            -@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.32.patch b/scst/kernel/scst_exec_req_fifo-2.6.32.patch
                            deleted file mode 100644
                            index bc0171019..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.32.patch
                            +++ /dev/null
                            @@ -1,529 +0,0 @@
                            -diff -upkr linux-2.6.32/block/blk-map.c linux-2.6.32/block/blk-map.c
                            ---- linux-2.6.32/block/blk-map.c	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/block/blk-map.c	2011-05-17 20:56:18.341812997 -0400
                            -@@ -5,6 +5,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= 1 << BIO_RW;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.32/include/linux/blkdev.h linux-2.6.32/include/linux/blkdev.h
                            ---- linux-2.6.32/include/linux/blkdev.h	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/include/linux/blkdev.h	2009-12-16 07:21:35.000000000 -0500
                            -@@ -708,6 +708,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -812,6 +814,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.32/include/linux/scatterlist.h linux-2.6.32/include/linux/scatterlist.h
                            ---- linux-2.6.32/include/linux/scatterlist.h	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/include/linux/scatterlist.h	2009-12-16 07:21:35.000000000 -0500
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.32/lib/scatterlist.c linux-2.6.32/lib/scatterlist.c
                            ---- linux-2.6.32/lib/scatterlist.c	2009-12-02 22:51:21.000000000 -0500
                            -+++ linux-2.6.32/lib/scatterlist.c	2009-12-16 07:21:35.000000000 -0500
                            -@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.33.patch b/scst/kernel/scst_exec_req_fifo-2.6.33.patch
                            deleted file mode 100644
                            index fee571fce..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.33.patch
                            +++ /dev/null
                            @@ -1,529 +0,0 @@
                            -diff -upkr linux-2.6.33/block/blk-map.c linux-2.6.33/block/blk-map.c
                            ---- linux-2.6.33/block/blk-map.c	2010-02-24 13:52:17.000000000 -0500
                            -+++ linux-2.6.33/block/blk-map.c	2011-05-17 21:09:00.317812998 -0400
                            -@@ -5,6 +5,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -271,6 +272,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= 1 << BIO_RW;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.33/include/linux/blkdev.h linux-2.6.33/include/linux/blkdev.h
                            ---- linux-2.6.33/include/linux/blkdev.h	2010-02-24 13:52:17.000000000 -0500
                            -+++ linux-2.6.33/include/linux/blkdev.h	2010-03-01 07:41:59.000000000 -0500
                            -@@ -710,6 +710,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -825,6 +827,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.33/include/linux/scatterlist.h linux-2.6.33/include/linux/scatterlist.h
                            ---- linux-2.6.33/include/linux/scatterlist.h	2010-02-24 13:52:17.000000000 -0500
                            -+++ linux-2.6.33/include/linux/scatterlist.h	2010-03-01 07:41:59.000000000 -0500
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.33/lib/scatterlist.c linux-2.6.33/lib/scatterlist.c
                            ---- linux-2.6.33/lib/scatterlist.c	2010-02-24 13:52:17.000000000 -0500
                            -+++ linux-2.6.33/lib/scatterlist.c	2010-03-01 07:41:59.000000000 -0500
                            -@@ -493,3 +493,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.34.patch b/scst/kernel/scst_exec_req_fifo-2.6.34.patch
                            deleted file mode 100644
                            index c7021f573..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.34.patch
                            +++ /dev/null
                            @@ -1,530 +0,0 @@
                            -diff -upkr linux-2.6.34/block/blk-map.c linux-2.6.34/block/blk-map.c
                            ---- linux-2.6.34/block/blk-map.c	2010-05-16 17:17:36.000000000 -0400
                            -+++ linux-2.6.34/block/blk-map.c	2011-05-17 21:10:43.745812995 -0400
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -271,6 +273,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= 1 << BIO_RW;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.34/include/linux/blkdev.h linux-2.6.34/include/linux/blkdev.h
                            ---- linux-2.6.34/include/linux/blkdev.h	2010-05-16 17:17:36.000000000 -0400
                            -+++ linux-2.6.34/include/linux/blkdev.h	2010-05-24 06:51:22.000000000 -0400
                            -@@ -713,6 +713,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -828,6 +830,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.34/include/linux/scatterlist.h linux-2.6.34/include/linux/scatterlist.h
                            ---- linux-2.6.34/include/linux/scatterlist.h	2010-05-16 17:17:36.000000000 -0400
                            -+++ linux-2.6.34/include/linux/scatterlist.h	2010-05-24 06:51:22.000000000 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.34/lib/scatterlist.c linux-2.6.34/lib/scatterlist.c
                            ---- linux-2.6.34/lib/scatterlist.c	2010-05-16 17:17:36.000000000 -0400
                            -+++ linux-2.6.34/lib/scatterlist.c	2010-05-24 06:51:22.000000000 -0400
                            -@@ -494,3 +494,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.35.patch b/scst/kernel/scst_exec_req_fifo-2.6.35.patch
                            deleted file mode 100644
                            index b10ae1b5a..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.35.patch
                            +++ /dev/null
                            @@ -1,530 +0,0 @@
                            -diff -upkr linux-2.6.35/block/blk-map.c linux-2.6.35/block/blk-map.c
                            ---- linux-2.6.35/block/blk-map.c	2010-08-01 18:11:14.000000000 -0400
                            -+++ linux-2.6.35/block/blk-map.c	2011-05-17 21:12:23.125813000 -0400
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -271,6 +273,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= 1 << BIO_RW;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.35/include/linux/blkdev.h linux-2.6.35/include/linux/blkdev.h
                            ---- linux-2.6.35/include/linux/blkdev.h	2010-08-01 18:11:14.000000000 -0400
                            -+++ linux-2.6.35/include/linux/blkdev.h	2010-08-04 04:21:59.737128732 -0400
                            -@@ -717,6 +717,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -832,6 +834,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.35/include/linux/scatterlist.h linux-2.6.35/include/linux/scatterlist.h
                            ---- linux-2.6.35/include/linux/scatterlist.h	2010-08-01 18:11:14.000000000 -0400
                            -+++ linux-2.6.35/include/linux/scatterlist.h	2010-08-04 04:21:59.741129485 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.35/lib/scatterlist.c linux-2.6.35/lib/scatterlist.c
                            ---- linux-2.6.35/lib/scatterlist.c	2010-08-01 18:11:14.000000000 -0400
                            -+++ linux-2.6.35/lib/scatterlist.c	2010-08-04 04:21:59.741129485 -0400
                            -@@ -494,3 +494,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.36.patch b/scst/kernel/scst_exec_req_fifo-2.6.36.patch
                            deleted file mode 100644
                            index d90bdcb8e..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.36.patch
                            +++ /dev/null
                            @@ -1,532 +0,0 @@
                            -diff -upkr linux-2.6.36/block/blk-map.c linux-2.6.36/block/blk-map.c
                            ---- linux-2.6.36/block/blk-map.c	2010-10-20 16:30:22.000000000 -0400
                            -+++ linux-2.6.36/block/blk-map.c	2011-05-17 21:13:42.301812997 -0400
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -271,6 +273,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.36/include/linux/blkdev.h linux-2.6.36/include/linux/blkdev.h
                            ---- linux-2.6.36/include/linux/blkdev.h	2010-10-20 16:30:22.000000000 -0400
                            -+++ linux-2.6.36/include/linux/blkdev.h	2010-10-26 04:00:15.899759399 -0400
                            -@@ -629,6 +629,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -746,6 +748,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.36/include/linux/scatterlist.h linux-2.6.36/include/linux/scatterlist.h
                            ---- linux-2.6.36/include/linux/scatterlist.h	2010-10-20 16:30:22.000000000 -0400
                            -+++ linux-2.6.36/include/linux/scatterlist.h	2010-10-26 04:00:15.899759399 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.36/lib/scatterlist.c linux-2.6.36/lib/scatterlist.c
                            ---- linux-2.6.36/lib/scatterlist.c	2010-10-20 16:30:22.000000000 -0400
                            -+++ linux-2.6.36/lib/scatterlist.c	2010-10-26 04:00:15.899759399 -0400
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.37.patch b/scst/kernel/scst_exec_req_fifo-2.6.37.patch
                            deleted file mode 100644
                            index ab94c18ae..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.37.patch
                            +++ /dev/null
                            @@ -1,532 +0,0 @@
                            -diff -upkr linux-2.6.37/block/blk-map.c linux-2.6.37/block/blk-map.c
                            ---- linux-2.6.37/block/blk-map.c	2011-01-04 19:50:19.000000000 -0500
                            -+++ linux-2.6.37/block/blk-map.c	2011-05-17 21:15:14.329812999 -0400
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.37/include/linux/blkdev.h linux-2.6.37/include/linux/blkdev.h
                            ---- linux-2.6.37/include/linux/blkdev.h	2011-01-04 19:50:19.000000000 -0500
                            -+++ linux-2.6.37/include/linux/blkdev.h	2011-01-08 08:45:54.350430208 -0500
                            -@@ -592,6 +592,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -709,6 +711,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.37/include/linux/scatterlist.h linux-2.6.37/include/linux/scatterlist.h
                            ---- linux-2.6.37/include/linux/scatterlist.h	2011-01-04 19:50:19.000000000 -0500
                            -+++ linux-2.6.37/include/linux/scatterlist.h	2011-01-08 08:45:54.354431761 -0500
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.37/lib/scatterlist.c linux-2.6.37/lib/scatterlist.c
                            ---- linux-2.6.37/lib/scatterlist.c	2011-01-04 19:50:19.000000000 -0500
                            -+++ linux-2.6.37/lib/scatterlist.c	2011-01-08 08:45:54.401930472 -0500
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.38.patch b/scst/kernel/scst_exec_req_fifo-2.6.38.patch
                            deleted file mode 100644
                            index 4561ba2e9..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.38.patch
                            +++ /dev/null
                            @@ -1,532 +0,0 @@
                            -diff -upkr linux-2.6.38/block/blk-map.c linux-2.6.38/block/blk-map.c
                            ---- linux-2.6.38/block/blk-map.c	2011-03-14 21:20:32.000000000 -0400
                            -+++ linux-2.6.38/block/blk-map.c	2011-05-11 22:07:37.589813000 -0400
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.38/include/linux/blkdev.h linux-2.6.38/include/linux/blkdev.h
                            ---- linux-2.6.38/include/linux/blkdev.h	2011-03-14 21:20:32.000000000 -0400
                            -+++ linux-2.6.38/include/linux/blkdev.h	2011-03-18 10:19:00.000000000 -0400
                            -@@ -593,6 +593,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -709,6 +711,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.38/include/linux/scatterlist.h linux-2.6.38/include/linux/scatterlist.h
                            ---- linux-2.6.38/include/linux/scatterlist.h	2011-03-14 21:20:32.000000000 -0400
                            -+++ linux-2.6.38/include/linux/scatterlist.h	2011-03-18 10:19:00.000000000 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.38/lib/scatterlist.c linux-2.6.38/lib/scatterlist.c
                            ---- linux-2.6.38/lib/scatterlist.c	2011-03-14 21:20:32.000000000 -0400
                            -+++ linux-2.6.38/lib/scatterlist.c	2011-03-18 10:46:41.000000000 -0400
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-2.6.39.patch b/scst/kernel/scst_exec_req_fifo-2.6.39.patch
                            deleted file mode 100644
                            index 7ecca2958..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-2.6.39.patch
                            +++ /dev/null
                            @@ -1,532 +0,0 @@
                            -diff -upkr linux-2.6.39/block/blk-map.c linux-2.6.39/block/blk-map.c
                            ---- linux-2.6.39/block/blk-map.c	2011-05-19 00:06:34.000000000 -0400
                            -+++ linux-2.6.39/block/blk-map.c	2011-05-19 10:49:02.753812997 -0400
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-2.6.39/include/linux/blkdev.h linux-2.6.39/include/linux/blkdev.h
                            ---- linux-2.6.39/include/linux/blkdev.h	2011-05-19 00:06:34.000000000 -0400
                            -+++ linux-2.6.39/include/linux/blkdev.h	2011-05-19 10:49:02.753812997 -0400
                            -@@ -592,6 +592,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -707,6 +709,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-2.6.39/include/linux/scatterlist.h linux-2.6.39/include/linux/scatterlist.h
                            ---- linux-2.6.39/include/linux/scatterlist.h	2011-05-19 00:06:34.000000000 -0400
                            -+++ linux-2.6.39/include/linux/scatterlist.h	2011-05-19 10:49:02.753812997 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-2.6.39/lib/scatterlist.c linux-2.6.39/lib/scatterlist.c
                            ---- linux-2.6.39/lib/scatterlist.c	2011-05-19 00:06:34.000000000 -0400
                            -+++ linux-2.6.39/lib/scatterlist.c	2011-05-19 10:49:02.753812997 -0400
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.0.patch b/scst/kernel/scst_exec_req_fifo-3.0.patch
                            deleted file mode 100644
                            index 998f4a32c..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.0.patch
                            +++ /dev/null
                            @@ -1,532 +0,0 @@
                            -diff -upkr linux-3.0.0-orig/block/blk-map.c linux-3.0.0-scst-dbg/block/blk-map.c
                            ---- linux-3.0.0-orig/block/blk-map.c	2011-07-21 22:17:23.000000000 -0400
                            -+++ linux-3.0.0-scst-dbg/block/blk-map.c	2011-07-22 19:40:27.131230804 -0400
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff -upkr linux-3.0.0-orig/include/linux/blkdev.h linux-3.0.0-scst-dbg/include/linux/blkdev.h
                            ---- linux-3.0.0-orig/include/linux/blkdev.h	2011-07-21 22:17:23.000000000 -0400
                            -+++ linux-3.0.0-scst-dbg/include/linux/blkdev.h	2011-07-22 19:24:27.803231156 -0400
                            -@@ -594,6 +594,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -709,6 +711,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff -upkr linux-3.0.0-orig/include/linux/scatterlist.h linux-3.0.0-scst-dbg/include/linux/scatterlist.h
                            ---- linux-3.0.0-orig/include/linux/scatterlist.h	2011-07-21 22:17:23.000000000 -0400
                            -+++ linux-3.0.0-scst-dbg/include/linux/scatterlist.h	2011-07-22 19:24:27.803231156 -0400
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff -upkr linux-3.0.0-orig/lib/scatterlist.c linux-3.0.0-scst-dbg/lib/scatterlist.c
                            ---- linux-3.0.0-orig/lib/scatterlist.c	2011-07-21 22:17:23.000000000 -0400
                            -+++ linux-3.0.0-scst-dbg/lib/scatterlist.c	2011-07-22 19:40:27.131230804 -0400
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.1.patch b/scst/kernel/scst_exec_req_fifo-3.1.patch
                            deleted file mode 100644
                            index d6bb5346b..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.1.patch
                            +++ /dev/null
                            @@ -1,536 +0,0 @@
                            -=== modified file 'linux-3.1-scst/block/blk-map.c'
                            ---- linux-3.1-orig/block/blk-map.c	2011-10-26 20:34:50 +0000
                            -+++ linux-3.1-scst/block/blk-map.c	2011-10-26 20:58:56 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -274,6 +276,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'linux-3.1-scst/include/linux/blkdev.h'
                            ---- linux-3.1-orig/include/linux/blkdev.h	2011-10-26 20:34:50 +0000
                            -+++ linux-3.1-scst/include/linux/blkdev.h	2011-10-26 20:58:56 +0000
                            -@@ -599,6 +599,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -714,6 +716,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'linux-3.1-scst/include/linux/scatterlist.h'
                            ---- linux-3.1-orig/include/linux/scatterlist.h	2011-10-26 20:34:50 +0000
                            -+++ linux-3.1-scst/include/linux/scatterlist.h	2011-10-26 20:58:56 +0000
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'linux-3.1-scst/lib/scatterlist.c'
                            ---- linux-3.1-orig/lib/scatterlist.c	2011-10-26 20:34:50 +0000
                            -+++ linux-3.1-scst/lib/scatterlist.c	2011-10-26 20:58:56 +0000
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.10.patch b/scst/kernel/scst_exec_req_fifo-3.10.patch
                            deleted file mode 100644
                            index 69fce3a5f..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.10.patch
                            +++ /dev/null
                            @@ -1,527 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2013-07-23 02:45:53 +0000
                            -+++ new/block/blk-map.c	2013-07-23 02:50:11 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2013-07-23 02:45:53 +0000
                            -+++ new/include/linux/blkdev.h	2013-07-23 02:50:11 +0000
                            -@@ -676,6 +676,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -795,6 +797,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2013-07-23 02:45:53 +0000
                            -+++ new/include/linux/scatterlist.h	2013-07-23 02:50:11 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -244,6 +245,9 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2013-07-23 02:45:53 +0000
                            -+++ new/lib/scatterlist.c	2013-07-23 02:50:11 +0000
                            -@@ -627,3 +627,126 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.11.patch b/scst/kernel/scst_exec_req_fifo-3.11.patch
                            deleted file mode 100644
                            index 63b2da453..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.11.patch
                            +++ /dev/null
                            @@ -1,528 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2013-09-28 00:14:38 +0000
                            -+++ new/block/blk-map.c	2013-09-28 00:23:26 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2013-09-28 00:14:38 +0000
                            -+++ new/include/linux/blkdev.h	2013-09-28 00:23:26 +0000
                            -@@ -676,6 +676,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -795,6 +797,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2013-09-28 00:14:38 +0000
                            -+++ new/include/linux/scatterlist.h	2013-09-28 00:23:26 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
                            - size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			  void *buf, size_t buflen, off_t skip);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2013-09-28 00:14:38 +0000
                            -+++ new/lib/scatterlist.c	2013-09-28 00:23:26 +0000
                            -@@ -716,3 +716,127 @@ size_t sg_pcopy_to_buffer(struct scatter
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
                            - }
                            - EXPORT_SYMBOL(sg_pcopy_to_buffer);
                            -+
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.12.patch b/scst/kernel/scst_exec_req_fifo-3.12.patch
                            deleted file mode 100644
                            index b08d43f3e..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.12.patch
                            +++ /dev/null
                            @@ -1,528 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2013-11-30 00:34:22 +0000
                            -+++ new/block/blk-map.c	2013-11-30 00:39:53 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2013-11-30 00:34:22 +0000
                            -+++ new/include/linux/blkdev.h	2013-11-30 00:39:53 +0000
                            -@@ -676,6 +676,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -795,6 +797,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2013-11-30 00:34:22 +0000
                            -+++ new/include/linux/scatterlist.h	2013-11-30 00:39:53 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
                            - size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			  void *buf, size_t buflen, off_t skip);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2013-11-30 00:34:22 +0000
                            -+++ new/lib/scatterlist.c	2013-11-30 00:39:53 +0000
                            -@@ -717,3 +717,127 @@ size_t sg_pcopy_to_buffer(struct scatter
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
                            - }
                            - EXPORT_SYMBOL(sg_pcopy_to_buffer);
                            -+
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.13.patch b/scst/kernel/scst_exec_req_fifo-3.13.patch
                            deleted file mode 100644
                            index 84980e46a..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.13.patch
                            +++ /dev/null
                            @@ -1,528 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2014-01-30 00:25:53 +0000
                            -+++ new/block/blk-map.c	2014-01-30 00:44:50 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2014-01-30 00:25:53 +0000
                            -+++ new/include/linux/blkdev.h	2014-01-30 00:44:50 +0000
                            -@@ -712,6 +712,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -831,6 +833,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2014-01-30 00:25:53 +0000
                            -+++ new/include/linux/scatterlist.h	2014-01-30 00:44:50 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
                            - size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			  void *buf, size_t buflen, off_t skip);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2014-01-30 00:25:53 +0000
                            -+++ new/lib/scatterlist.c	2014-01-30 00:44:50 +0000
                            -@@ -717,3 +717,127 @@ size_t sg_pcopy_to_buffer(struct scatter
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
                            - }
                            - EXPORT_SYMBOL(sg_pcopy_to_buffer);
                            -+
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.14.patch b/scst/kernel/scst_exec_req_fifo-3.14.patch
                            deleted file mode 100644
                            index 70c47797d..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.14.patch
                            +++ /dev/null
                            @@ -1,528 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2014-04-17 22:02:06 +0000
                            -+++ new/block/blk-map.c	2014-04-17 22:08:48 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2014-04-17 22:02:06 +0000
                            -+++ new/include/linux/blkdev.h	2014-04-17 22:08:48 +0000
                            -@@ -705,6 +705,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -825,6 +827,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2014-04-17 22:02:06 +0000
                            -+++ new/include/linux/scatterlist.h	2014-04-17 22:08:48 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
                            - size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			  void *buf, size_t buflen, off_t skip);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2014-04-17 22:02:06 +0000
                            -+++ new/lib/scatterlist.c	2014-04-17 22:08:48 +0000
                            -@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
                            - }
                            - EXPORT_SYMBOL(sg_pcopy_to_buffer);
                            -+
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.15.patch b/scst/kernel/scst_exec_req_fifo-3.15.patch
                            deleted file mode 100644
                            index 665cc2606..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.15.patch
                            +++ /dev/null
                            @@ -1,528 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2014-06-18 01:32:48 +0000
                            -+++ new/block/blk-map.c	2014-06-18 01:40:34 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2014-06-18 01:32:48 +0000
                            -+++ new/include/linux/blkdev.h	2014-06-18 01:40:34 +0000
                            -@@ -717,6 +717,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -837,6 +839,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, const struct sg_iovec *,
                            - 			       int, unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2014-06-18 01:32:48 +0000
                            -+++ new/include/linux/scatterlist.h	2014-06-18 01:40:34 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
                            - size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			  void *buf, size_t buflen, off_t skip);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2014-06-18 01:32:48 +0000
                            -+++ new/lib/scatterlist.c	2014-06-18 01:40:34 +0000
                            -@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
                            - }
                            - EXPORT_SYMBOL(sg_pcopy_to_buffer);
                            -+
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.16.patch b/scst/kernel/scst_exec_req_fifo-3.16.patch
                            deleted file mode 100644
                            index a08921920..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.16.patch
                            +++ /dev/null
                            @@ -1,524 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2014-08-19 01:00:36 +0000
                            -+++ new/block/blk-map.c	2014-08-19 01:37:01 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -273,6 +275,333 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2014-08-19 01:00:36 +0000
                            -+++ new/include/linux/blkdev.h	2014-08-19 01:06:48 +0000
                            -@@ -735,6 +735,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -856,6 +858,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, const struct sg_iovec *,
                            - 			       int, unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2014-08-19 01:00:36 +0000
                            -+++ new/include/linux/scatterlist.h	2014-08-19 01:06:48 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -249,6 +250,9 @@ size_t sg_pcopy_from_buffer(struct scatt
                            - size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			  void *buf, size_t buflen, off_t skip);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2014-08-19 01:00:36 +0000
                            -+++ new/lib/scatterlist.c	2014-08-19 01:06:48 +0000
                            -@@ -718,3 +718,127 @@ size_t sg_pcopy_to_buffer(struct scatter
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
                            - }
                            - EXPORT_SYMBOL(sg_pcopy_to_buffer);
                            -+
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.2.patch b/scst/kernel/scst_exec_req_fifo-3.2.patch
                            deleted file mode 100644
                            index 2b8257ce3..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.2.patch
                            +++ /dev/null
                            @@ -1,536 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2012-01-10 22:58:17 +0000
                            -+++ new/block/blk-map.c	2012-01-10 23:01:21 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2012-01-10 22:58:17 +0000
                            -+++ new/include/linux/blkdev.h	2012-01-10 23:01:21 +0000
                            -@@ -599,6 +599,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -716,6 +718,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2012-01-10 22:58:17 +0000
                            -+++ new/include/linux/scatterlist.h	2012-01-10 23:01:21 +0000
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2012-01-10 22:58:17 +0000
                            -+++ new/lib/scatterlist.c	2012-01-10 23:01:21 +0000
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.3.patch b/scst/kernel/scst_exec_req_fifo-3.3.patch
                            deleted file mode 100644
                            index 293d96633..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.3.patch
                            +++ /dev/null
                            @@ -1,536 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2012-03-19 23:46:01 +0000
                            -+++ new/block/blk-map.c	2012-03-20 00:10:37 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,339 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0,
                            -+					KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy,
                            -+			KM_USER0, KM_USER1);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2012-03-19 23:46:01 +0000
                            -+++ new/include/linux/blkdev.h	2012-03-20 00:10:37 +0000
                            -@@ -612,6 +612,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -731,6 +733,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2012-03-19 23:46:01 +0000
                            -+++ new/include/linux/scatterlist.h	2012-03-20 00:10:37 +0000
                            -@@ -3,6 +3,7 @@
                            - 
                            - #include 
                            - #include 
                            -+#include 
                            - #include 
                            - #include 
                            - #include 
                            -@@ -218,6 +219,10 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2012-03-19 23:46:01 +0000
                            -+++ new/lib/scatterlist.c	2012-03-20 00:10:37 +0000
                            -@@ -517,3 +517,132 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len,
                            -+			enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page +
                            -+					 (src_offs >> PAGE_SHIFT), s_km_type) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page +
                            -+					(dst_offs >> PAGE_SHIFT), d_km_type) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr, s_km_type);
                            -+		kunmap_atomic(daddr, d_km_type);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ * @d_km_type:	kmap_atomic type for the destination SG
                            -+ * @s_km_type:	kmap_atomic type for the source SG
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len,
                            -+	    enum km_type d_km_type, enum km_type s_km_type)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len, d_km_type, s_km_type);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.4.patch b/scst/kernel/scst_exec_req_fifo-3.4.patch
                            deleted file mode 100644
                            index 53ac80d0c..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.4.patch
                            +++ /dev/null
                            @@ -1,528 +0,0 @@
                            -diff --git a/block/blk-map.c b/block/blk-map.c
                            -index 623e1cd..20349d0 100644
                            ---- a/block/blk-map.c
                            -+++ b/block/blk-map.c
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
                            -index 4d4ac24..3fa6a30 100644
                            ---- a/include/linux/blkdev.h
                            -+++ b/include/linux/blkdev.h
                            -@@ -609,6 +609,8 @@ extern unsigned long blk_max_low_pfn, blk_max_pfn;
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -728,6 +730,9 @@ extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, uns
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
                            -index ac9586d..4b743d7 100644
                            ---- a/include/linux/scatterlist.h
                            -+++ b/include/linux/scatterlist.h
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -220,6 +221,9 @@ size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -diff --git a/lib/scatterlist.c b/lib/scatterlist.c
                            -index 6096e89..1786ca9 100644
                            ---- a/lib/scatterlist.c
                            -+++ b/lib/scatterlist.c
                            -@@ -517,3 +517,126 @@ size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.5.patch b/scst/kernel/scst_exec_req_fifo-3.5.patch
                            deleted file mode 100644
                            index 78c3f0720..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.5.patch
                            +++ /dev/null
                            @@ -1,527 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2012-08-08 02:57:29 +0000
                            -+++ new/block/blk-map.c	2012-08-08 03:02:56 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2012-08-08 02:57:29 +0000
                            -+++ new/include/linux/blkdev.h	2012-08-08 03:02:56 +0000
                            -@@ -627,6 +627,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -746,6 +748,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2012-08-08 02:57:29 +0000
                            -+++ new/include/linux/scatterlist.h	2012-08-08 03:02:56 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -220,6 +221,9 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2012-08-08 02:57:29 +0000
                            -+++ new/lib/scatterlist.c	2012-08-08 03:02:56 +0000
                            -@@ -517,3 +517,126 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.6.patch b/scst/kernel/scst_exec_req_fifo-3.6.patch
                            deleted file mode 100644
                            index bf9cf76c0..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.6.patch
                            +++ /dev/null
                            @@ -1,527 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2012-10-01 18:39:34 +0000
                            -+++ new/block/blk-map.c	2012-10-01 20:50:07 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2012-10-01 18:39:34 +0000
                            -+++ new/include/linux/blkdev.h	2012-10-01 18:45:47 +0000
                            -@@ -638,6 +638,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -757,6 +759,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2012-10-01 18:39:34 +0000
                            -+++ new/include/linux/scatterlist.h	2012-10-01 18:45:47 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -224,6 +225,9 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2012-10-01 18:39:34 +0000
                            -+++ new/lib/scatterlist.c	2012-10-01 20:50:07 +0000
                            -@@ -573,3 +573,126 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.7.patch b/scst/kernel/scst_exec_req_fifo-3.7.patch
                            deleted file mode 100644
                            index 465558d8f..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.7.patch
                            +++ /dev/null
                            @@ -1,527 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2012-12-17 19:41:04 +0000
                            -+++ new/block/blk-map.c	2012-12-17 22:29:54 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2012-12-17 19:41:04 +0000
                            -+++ new/include/linux/blkdev.h	2012-12-17 22:29:54 +0000
                            -@@ -660,6 +660,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -779,6 +781,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2012-12-17 19:41:04 +0000
                            -+++ new/include/linux/scatterlist.h	2012-12-17 22:29:54 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -225,6 +226,9 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2012-12-17 19:41:04 +0000
                            -+++ new/lib/scatterlist.c	2012-12-17 22:29:54 +0000
                            -@@ -592,3 +592,126 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.8.patch b/scst/kernel/scst_exec_req_fifo-3.8.patch
                            deleted file mode 100644
                            index 0476a9331..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.8.patch
                            +++ /dev/null
                            @@ -1,527 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2013-02-22 21:12:31 +0000
                            -+++ new/block/blk-map.c	2013-02-23 00:07:57 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2013-02-22 21:12:31 +0000
                            -+++ new/include/linux/blkdev.h	2013-02-22 21:21:51 +0000
                            -@@ -668,6 +668,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -787,6 +789,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2013-02-22 21:12:31 +0000
                            -+++ new/include/linux/scatterlist.h	2013-02-22 21:21:51 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -225,6 +226,9 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2013-02-22 21:12:31 +0000
                            -+++ new/lib/scatterlist.c	2013-02-23 00:07:57 +0000
                            -@@ -593,3 +593,126 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/kernel/scst_exec_req_fifo-3.9.patch b/scst/kernel/scst_exec_req_fifo-3.9.patch
                            deleted file mode 100644
                            index fca368b29..000000000
                            --- a/scst/kernel/scst_exec_req_fifo-3.9.patch
                            +++ /dev/null
                            @@ -1,527 +0,0 @@
                            -=== modified file 'block/blk-map.c'
                            ---- old/block/blk-map.c	2013-05-11 05:39:14 +0000
                            -+++ new/block/blk-map.c	2013-05-14 01:25:01 +0000
                            -@@ -5,6 +5,8 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            -+#include 
                            - #include 		/* for struct sg_iovec */
                            - 
                            - #include "blk.h"
                            -@@ -275,6 +277,337 @@ int blk_rq_unmap_user(struct bio *bio)
                            - }
                            - EXPORT_SYMBOL(blk_rq_unmap_user);
                            - 
                            -+struct blk_kern_sg_work {
                            -+	atomic_t bios_inflight;
                            -+	struct sg_table sg_table;
                            -+	struct scatterlist *src_sgl;
                            -+};
                            -+
                            -+static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            -+{
                            -+	struct sg_table *sgt = &bw->sg_table;
                            -+	struct scatterlist *sg;
                            -+	int i;
                            -+
                            -+	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            -+		struct page *pg = sg_page(sg);
                            -+		if (pg == NULL)
                            -+			break;
                            -+		__free_page(pg);
                            -+	}
                            -+
                            -+	sg_free_table(sgt);
                            -+	kfree(bw);
                            -+	return;
                            -+}
                            -+
                            -+static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            -+{
                            -+	struct blk_kern_sg_work *bw = bio->bi_private;
                            -+
                            -+	if (bw != NULL) {
                            -+		/* Decrement the bios in processing and, if zero, free */
                            -+		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            -+		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            -+			if ((bio_data_dir(bio) == READ) && (err == 0)) {
                            -+				unsigned long flags;
                            -+
                            -+				local_irq_save(flags);	/* to protect KMs */
                            -+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
                            -+				local_irq_restore(flags);
                            -+			}
                            -+			blk_free_kern_sg_work(bw);
                            -+		}
                            -+	}
                            -+
                            -+	bio_put(bio);
                            -+	return;
                            -+}
                            -+
                            -+static int blk_rq_copy_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			       int nents, struct blk_kern_sg_work **pbw,
                            -+			       gfp_t gfp, gfp_t page_gfp)
                            -+{
                            -+	int res = 0, i;
                            -+	struct scatterlist *sg;
                            -+	struct scatterlist *new_sgl;
                            -+	int new_sgl_nents;
                            -+	size_t len = 0, to_copy;
                            -+	struct blk_kern_sg_work *bw;
                            -+
                            -+	bw = kzalloc(sizeof(*bw), gfp);
                            -+	if (bw == NULL)
                            -+		goto out;
                            -+
                            -+	bw->src_sgl = sgl;
                            -+
                            -+	for_each_sg(sgl, sg, nents, i)
                            -+		len += sg->length;
                            -+	to_copy = len;
                            -+
                            -+	new_sgl_nents = PFN_UP(len);
                            -+
                            -+	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp);
                            -+	if (res != 0)
                            -+		goto err_free;
                            -+
                            -+	new_sgl = bw->sg_table.sgl;
                            -+
                            -+	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            -+		struct page *pg;
                            -+
                            -+		pg = alloc_page(page_gfp);
                            -+		if (pg == NULL)
                            -+			goto err_free;
                            -+
                            -+		sg_assign_page(sg, pg);
                            -+		sg->length = min_t(size_t, PAGE_SIZE, len);
                            -+
                            -+		len -= PAGE_SIZE;
                            -+	}
                            -+
                            -+	if (rq_data_dir(rq) == WRITE) {
                            -+		/*
                            -+		 * We need to limit amount of copied data to to_copy, because
                            -+		 * sgl might have the last element in sgl not marked as last in
                            -+		 * SG chaining.
                            -+		 */
                            -+		sg_copy(new_sgl, sgl, 0, to_copy);
                            -+	}
                            -+
                            -+	*pbw = bw;
                            -+	/*
                            -+	 * REQ_COPY_USER name is misleading. It should be something like
                            -+	 * REQ_HAS_TAIL_SPACE_FOR_PADDING.
                            -+	 */
                            -+	rq->cmd_flags |= REQ_COPY_USER;
                            -+
                            -+out:
                            -+	return res;
                            -+
                            -+err_free:
                            -+	blk_free_kern_sg_work(bw);
                            -+	res = -ENOMEM;
                            -+	goto out;
                            -+}
                            -+
                            -+static int __blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+	int nents, struct blk_kern_sg_work *bw, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+	struct request_queue *q = rq->q;
                            -+	int rw = rq_data_dir(rq);
                            -+	int max_nr_vecs, i;
                            -+	size_t tot_len;
                            -+	bool need_new_bio;
                            -+	struct scatterlist *sg, *prev_sg = NULL;
                            -+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            -+	int bios;
                            -+
                            -+	if (unlikely((sgl == NULL) || (sgl->length == 0) || (nents <= 0))) {
                            -+		WARN_ON(1);
                            -+		res = -EINVAL;
                            -+		goto out;
                            -+	}
                            -+
                            -+	/*
                            -+	 * Let's keep each bio allocation inside a single page to decrease
                            -+	 * probability of failure.
                            -+	 */
                            -+	max_nr_vecs =  min_t(size_t,
                            -+		((PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec)),
                            -+		BIO_MAX_PAGES);
                            -+
                            -+	need_new_bio = true;
                            -+	tot_len = 0;
                            -+	bios = 0;
                            -+	for_each_sg(sgl, sg, nents, i) {
                            -+		struct page *page = sg_page(sg);
                            -+		void *page_addr = page_address(page);
                            -+		size_t len = sg->length, l;
                            -+		size_t offset = sg->offset;
                            -+
                            -+		tot_len += len;
                            -+		prev_sg = sg;
                            -+
                            -+		/*
                            -+		 * Each segment must be aligned on DMA boundary and
                            -+		 * not on stack. The last one may have unaligned
                            -+		 * length as long as the total length is aligned to
                            -+		 * DMA padding alignment.
                            -+		 */
                            -+		if (i == nents - 1)
                            -+			l = 0;
                            -+		else
                            -+			l = len;
                            -+		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            -+		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            -+			res = -EINVAL;
                            -+			goto out_free_bios;
                            -+		}
                            -+
                            -+		while (len > 0) {
                            -+			size_t bytes;
                            -+			int rc;
                            -+
                            -+			if (need_new_bio) {
                            -+				bio = bio_kmalloc(gfp, max_nr_vecs);
                            -+				if (bio == NULL) {
                            -+					res = -ENOMEM;
                            -+					goto out_free_bios;
                            -+				}
                            -+
                            -+				if (rw == WRITE)
                            -+					bio->bi_rw |= REQ_WRITE;
                            -+
                            -+				bios++;
                            -+				bio->bi_private = bw;
                            -+				bio->bi_end_io = blk_bio_map_kern_endio;
                            -+
                            -+				if (hbio == NULL)
                            -+					hbio = tbio = bio;
                            -+				else
                            -+					tbio = tbio->bi_next = bio;
                            -+			}
                            -+
                            -+			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            -+
                            -+			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            -+			if (rc < bytes) {
                            -+				if (unlikely(need_new_bio || (rc < 0))) {
                            -+					if (rc < 0)
                            -+						res = rc;
                            -+					else
                            -+						res = -EIO;
                            -+					goto out_free_bios;
                            -+				} else {
                            -+					need_new_bio = true;
                            -+					len -= rc;
                            -+					offset += rc;
                            -+					continue;
                            -+				}
                            -+			}
                            -+
                            -+			need_new_bio = false;
                            -+			offset = 0;
                            -+			len -= bytes;
                            -+			page = nth_page(page, 1);
                            -+		}
                            -+	}
                            -+
                            -+	if (hbio == NULL) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	/* Total length must be aligned on DMA padding alignment */
                            -+	if ((tot_len & q->dma_pad_mask) &&
                            -+	    !(rq->cmd_flags & REQ_COPY_USER)) {
                            -+		res = -EINVAL;
                            -+		goto out_free_bios;
                            -+	}
                            -+
                            -+	if (bw != NULL)
                            -+		atomic_set(&bw->bios_inflight, bios);
                            -+
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio->bi_next = NULL;
                            -+
                            -+		blk_queue_bounce(q, &bio);
                            -+
                            -+		res = blk_rq_append_bio(q, rq, bio);
                            -+		if (unlikely(res != 0)) {
                            -+			bio->bi_next = hbio;
                            -+			hbio = bio;
                            -+			/* We can have one or more bios bounced */
                            -+			goto out_unmap_bios;
                            -+		}
                            -+	}
                            -+
                            -+	res = 0;
                            -+
                            -+	rq->buffer = NULL;
                            -+out:
                            -+	return res;
                            -+
                            -+out_unmap_bios:
                            -+	blk_rq_unmap_kern_sg(rq, res);
                            -+
                            -+out_free_bios:
                            -+	while (hbio != NULL) {
                            -+		bio = hbio;
                            -+		hbio = hbio->bi_next;
                            -+		bio_put(bio);
                            -+	}
                            -+	goto out;
                            -+}
                            -+
                            -+/**
                            -+ * blk_rq_map_kern_sg - map kernel data to a request, for REQ_TYPE_BLOCK_PC
                            -+ * @rq:		request to fill
                            -+ * @sgl:	area to map
                            -+ * @nents:	number of elements in @sgl
                            -+ * @gfp:	memory allocation flags
                            -+ *
                            -+ * Description:
                            -+ *    Data will be mapped directly if possible. Otherwise a bounce
                            -+ *    buffer will be used.
                            -+ */
                            -+int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+		       int nents, gfp_t gfp)
                            -+{
                            -+	int res;
                            -+
                            -+	res = __blk_rq_map_kern_sg(rq, sgl, nents, NULL, gfp);
                            -+	if (unlikely(res != 0)) {
                            -+		struct blk_kern_sg_work *bw = NULL;
                            -+
                            -+		res = blk_rq_copy_kern_sg(rq, sgl, nents, &bw,
                            -+				gfp, rq->q->bounce_gfp | gfp);
                            -+		if (unlikely(res != 0))
                            -+			goto out;
                            -+
                            -+		res = __blk_rq_map_kern_sg(rq, bw->sg_table.sgl,
                            -+				bw->sg_table.nents, bw, gfp);
                            -+		if (res != 0) {
                            -+			blk_free_kern_sg_work(bw);
                            -+			goto out;
                            -+		}
                            -+	}
                            -+
                            -+	rq->buffer = NULL;
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_map_kern_sg);
                            -+
                            -+/**
                            -+ * blk_rq_unmap_kern_sg - unmap a request with kernel sg
                            -+ * @rq:		request to unmap
                            -+ * @err:	non-zero error code
                            -+ *
                            -+ * Description:
                            -+ *    Unmap a rq previously mapped by blk_rq_map_kern_sg(). Must be called
                            -+ *    only in case of an error!
                            -+ */
                            -+void blk_rq_unmap_kern_sg(struct request *rq, int err)
                            -+{
                            -+	struct bio *bio = rq->bio;
                            -+
                            -+	while (bio) {
                            -+		struct bio *b = bio;
                            -+		bio = bio->bi_next;
                            -+		b->bi_end_io(b, err);
                            -+	}
                            -+	rq->bio = NULL;
                            -+
                            -+	return;
                            -+}
                            -+EXPORT_SYMBOL(blk_rq_unmap_kern_sg);
                            -+
                            - /**
                            -  * blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
                            -  * @q:		request queue where request should be inserted
                            -
                            -=== modified file 'include/linux/blkdev.h'
                            ---- old/include/linux/blkdev.h	2013-05-11 05:39:14 +0000
                            -+++ new/include/linux/blkdev.h	2013-05-11 05:48:04 +0000
                            -@@ -670,6 +670,8 @@ extern unsigned long blk_max_low_pfn, bl
                            - #define BLK_DEFAULT_SG_TIMEOUT	(60 * HZ)
                            - #define BLK_MIN_SG_TIMEOUT	(7 * HZ)
                            - 
                            -+#define SCSI_EXEC_REQ_FIFO_DEFINED
                            -+
                            - #ifdef CONFIG_BOUNCE
                            - extern int init_emergency_isa_pool(void);
                            - extern void blk_queue_bounce(struct request_queue *q, struct bio **bio);
                            -@@ -789,6 +791,9 @@ extern int blk_rq_map_kern(struct reques
                            - extern int blk_rq_map_user_iov(struct request_queue *, struct request *,
                            - 			       struct rq_map_data *, struct sg_iovec *, int,
                            - 			       unsigned int, gfp_t);
                            -+extern int blk_rq_map_kern_sg(struct request *rq, struct scatterlist *sgl,
                            -+			      int nents, gfp_t gfp);
                            -+extern void blk_rq_unmap_kern_sg(struct request *rq, int err);
                            - extern int blk_execute_rq(struct request_queue *, struct gendisk *,
                            - 			  struct request *, int);
                            - extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,
                            -
                            -=== modified file 'include/linux/scatterlist.h'
                            ---- old/include/linux/scatterlist.h	2013-05-11 05:39:14 +0000
                            -+++ new/include/linux/scatterlist.h	2013-05-11 05:48:04 +0000
                            -@@ -8,6 +8,7 @@
                            - #include 
                            - #include 
                            - #include 
                            -+#include 
                            - 
                            - struct sg_table {
                            - 	struct scatterlist *sgl;	/* the list */
                            -@@ -225,6 +226,9 @@ size_t sg_copy_from_buffer(struct scatte
                            - size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
                            - 			 void *buf, size_t buflen);
                            - 
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len);
                            -+
                            - /*
                            -  * Maximum number of entries that will be allocated in one piece, if
                            -  * a list larger than this is required then chaining will be utilized.
                            -
                            -=== modified file 'lib/scatterlist.c'
                            ---- old/lib/scatterlist.c	2013-05-11 05:39:14 +0000
                            -+++ new/lib/scatterlist.c	2013-05-14 01:25:01 +0000
                            -@@ -629,3 +629,126 @@ size_t sg_copy_to_buffer(struct scatterl
                            - 	return sg_copy_buffer(sgl, nents, buf, buflen, 1);
                            - }
                            - EXPORT_SYMBOL(sg_copy_to_buffer);
                            -+
                            -+/*
                            -+ * Can switch to the next dst_sg element, so, to copy to strictly only
                            -+ * one dst_sg element, it must be either last in the chain, or
                            -+ * copy_len == dst_sg->length.
                            -+ */
                            -+static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
                            -+			size_t *pdst_offs, struct scatterlist *src_sg,
                            -+			size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	struct scatterlist *dst_sg;
                            -+	size_t src_len, dst_len, src_offs, dst_offs;
                            -+	struct page *src_page, *dst_page;
                            -+
                            -+	dst_sg = *pdst_sg;
                            -+	dst_len = *pdst_len;
                            -+	dst_offs = *pdst_offs;
                            -+	dst_page = sg_page(dst_sg);
                            -+
                            -+	src_page = sg_page(src_sg);
                            -+	src_len = src_sg->length;
                            -+	src_offs = src_sg->offset;
                            -+
                            -+	do {
                            -+		void *saddr, *daddr;
                            -+		size_t n;
                            -+
                            -+		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
                            -+				    (src_offs & ~PAGE_MASK);
                            -+		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
                            -+				    (dst_offs & ~PAGE_MASK);
                            -+
                            -+		if (((src_offs & ~PAGE_MASK) == 0) &&
                            -+		    ((dst_offs & ~PAGE_MASK) == 0) &&
                            -+		    (src_len >= PAGE_SIZE) && (dst_len >= PAGE_SIZE) &&
                            -+		    (copy_len >= PAGE_SIZE)) {
                            -+			copy_page(daddr, saddr);
                            -+			n = PAGE_SIZE;
                            -+		} else {
                            -+			n = min_t(size_t, PAGE_SIZE - (dst_offs & ~PAGE_MASK),
                            -+					  PAGE_SIZE - (src_offs & ~PAGE_MASK));
                            -+			n = min(n, src_len);
                            -+			n = min(n, dst_len);
                            -+			n = min_t(size_t, n, copy_len);
                            -+			memcpy(daddr, saddr, n);
                            -+		}
                            -+		dst_offs += n;
                            -+		src_offs += n;
                            -+
                            -+		kunmap_atomic(saddr);
                            -+		kunmap_atomic(daddr);
                            -+
                            -+		res += n;
                            -+		copy_len -= n;
                            -+		if (copy_len == 0)
                            -+			goto out;
                            -+
                            -+		src_len -= n;
                            -+		dst_len -= n;
                            -+		if (dst_len == 0) {
                            -+			dst_sg = sg_next(dst_sg);
                            -+			if (dst_sg == NULL)
                            -+				goto out;
                            -+			dst_page = sg_page(dst_sg);
                            -+			dst_len = dst_sg->length;
                            -+			dst_offs = dst_sg->offset;
                            -+		}
                            -+	} while (src_len > 0);
                            -+
                            -+out:
                            -+	*pdst_sg = dst_sg;
                            -+	*pdst_len = dst_len;
                            -+	*pdst_offs = dst_offs;
                            -+	return res;
                            -+}
                            -+
                            -+/**
                            -+ * sg_copy - copy one SG vector to another
                            -+ * @dst_sg:	destination SG
                            -+ * @src_sg:	source SG
                            -+ * @nents_to_copy: maximum number of entries to copy
                            -+ * @copy_len:	maximum amount of data to copy. If 0, then copy all.
                            -+ *
                            -+ * Description:
                            -+ *    Data from the source SG vector will be copied to the destination SG
                            -+ *    vector. End of the vectors will be determined by sg_next() returning
                            -+ *    NULL. Returns number of bytes copied.
                            -+ */
                            -+int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                            -+	    int nents_to_copy, size_t copy_len)
                            -+{
                            -+	int res = 0;
                            -+	size_t dst_len, dst_offs;
                            -+
                            -+	if (copy_len == 0)
                            -+		copy_len = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	if (nents_to_copy == 0)
                            -+		nents_to_copy = 0x7FFFFFFF; /* copy all */
                            -+
                            -+	dst_len = dst_sg->length;
                            -+	dst_offs = dst_sg->offset;
                            -+
                            -+	do {
                            -+		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
                            -+				src_sg, copy_len);
                            -+		copy_len -= copied;
                            -+		res += copied;
                            -+		if ((copy_len == 0) || (dst_sg == NULL))
                            -+			goto out;
                            -+
                            -+		nents_to_copy--;
                            -+		if (nents_to_copy == 0)
                            -+			goto out;
                            -+
                            -+		src_sg = sg_next(src_sg);
                            -+	} while (src_sg != NULL);
                            -+
                            -+out:
                            -+	return res;
                            -+}
                            -+EXPORT_SYMBOL(sg_copy);
                            -
                            diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c
                            index 684aca4ff..ad58fc438 100644
                            --- a/scst/src/dev_handlers/scst_disk.c
                            +++ b/scst/src/dev_handlers/scst_disk.c
                            @@ -47,7 +47,7 @@ static void disk_detach(struct scst_device *dev);
                             static int disk_parse(struct scst_cmd *cmd);
                             static int disk_perf_exec(struct scst_cmd *cmd);
                             static int disk_done(struct scst_cmd *cmd);
                            -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
                             static int disk_exec(struct scst_cmd *cmd);
                             static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd);
                             #endif
                            @@ -61,7 +61,7 @@ static struct scst_dev_type disk_devtype = {
                             	.attach =		disk_attach,
                             	.detach =		disk_detach,
                             	.parse =		disk_parse,
                            -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
                             	.exec =			disk_exec,
                             	.on_sg_tablesize_low = disk_on_sg_tablesize_low,
                             #endif
                            @@ -82,7 +82,7 @@ static struct scst_dev_type disk_devtype_perf = {
                             	.parse =		disk_parse,
                             	.exec =			disk_perf_exec,
                             	.dev_done =		disk_done,
                            -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
                             	.on_sg_tablesize_low = disk_on_sg_tablesize_low,
                             #endif
                             #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
                            @@ -293,7 +293,7 @@ static int disk_done(struct scst_cmd *cmd)
                             	return res;
                             }
                             
                            -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
                             
                             static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd)
                             {
                            @@ -536,7 +536,7 @@ out:
                             	return res;
                             
                             out_err_restore:
                            -	scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +	scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
                             	goto out_restore;
                             
                             out_error:
                            @@ -544,7 +544,7 @@ out_error:
                             	goto out_done;
                             }
                             
                            -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
                            +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
                             
                             static int disk_perf_exec(struct scst_cmd *cmd)
                             {
                            diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c
                            index ec4e85bc8..4aeba2417 100644
                            --- a/scst/src/dev_handlers/scst_tape.c
                            +++ b/scst/src/dev_handlers/scst_tape.c
                            @@ -291,7 +291,7 @@ static int tape_done(struct scst_cmd *cmd)
                             			PRINT_ERROR("Sense format 0x%x is not supported",
                             				scst_sense_response_code(cmd->sense));
                             			scst_set_cmd_error(cmd,
                            -				SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				SCST_LOAD_SENSE(scst_sense_internal_failure));
                             			goto out;
                             		}
                             
                            diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c
                            index 9f8d9e316..5dabae13d 100644
                            --- a/scst/src/dev_handlers/scst_user.c
                            +++ b/scst/src/dev_handlers/scst_user.c
                            @@ -1155,7 +1155,7 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
                             		/*
                             		 * If we don't put such commands in the queue head, then under
                             		 * high load we might delay threads, waiting for memory
                            -		 * allocations, for too long and start loosing NOPs, which
                            +		 * allocations, for too long and start losing NOPs, which
                             		 * would lead to consider us by remote initiators as
                             		 * unresponsive and stuck => broken connections, etc. If none
                             		 * of our commands completed in NOP timeout to allow the head
                            @@ -1259,7 +1259,7 @@ out_unmap:
                             	ucmd->data_pages = NULL;
                             	res = -EFAULT;
                             	if (ucmd->cmd != NULL)
                            -		scst_set_cmd_error(ucmd->cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +		scst_set_cmd_error(ucmd->cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
                             	goto out_err;
                             }
                             
                            @@ -1370,14 +1370,14 @@ out_process:
                             
                             out_inval:
                             	PRINT_ERROR("Invalid parse_reply parameters (LUN %lld, op %s, cmd %p)",
                            -		(long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd);
                            +		(unsigned long long int)cmd->lun, scst_get_opcode_name(cmd), cmd);
                             	PRINT_BUFFER("Invalid parse_reply", reply, sizeof(*reply));
                             	scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
                             	res = -EINVAL;
                             	goto out_abnormal;
                             
                            -out_hwerr_res_set:
                            -	scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +out_intern_fail_res_set:
                            +	scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
                             
                             out_abnormal:
                             	scst_set_cmd_abnormal_done_state(cmd);
                            @@ -1392,7 +1392,7 @@ out_status:
                             
                             		res = scst_alloc_sense(cmd, 0);
                             		if (res != 0)
                            -			goto out_hwerr_res_set;
                            +			goto out_intern_fail_res_set;
                             
                             		sense_len = min_t(int, cmd->sense_buflen, preply->sense_len);
                             
                            @@ -1402,7 +1402,7 @@ out_status:
                             		if (rc != 0) {
                             			PRINT_ERROR("Failed to copy %d sense's bytes", rc);
                             			res = -EFAULT;
                            -			goto out_hwerr_res_set;
                            +			goto out_intern_fail_res_set;
                             		}
                             		cmd->sense_valid_len = sense_len;
                             	}
                            @@ -1494,7 +1494,7 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
                             				if (unlikely((ereply->pbuf & ~PAGE_MASK) != 0)) {
                             					PRINT_ERROR("Supplied pbuf %llx isn't "
                             						"page aligned", ereply->pbuf);
                            -					goto out_hwerr;
                            +					goto out_intern_fail;
                             				}
                             				pages = cmd->sg_cnt;
                             			} else
                            @@ -1539,7 +1539,7 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
                             		if (rc != 0) {
                             			PRINT_ERROR("Failed to copy %d sense's bytes", rc);
                             			res = -EFAULT;
                            -			goto out_hwerr_res_set;
                            +			goto out_intern_fail_res_set;
                             		}
                             		cmd->sense_valid_len = sense_len;
                             	}
                            @@ -1556,19 +1556,19 @@ out:
                             
                             out_inval:
                             	PRINT_ERROR("Invalid exec_reply parameters (LUN %lld, op %s, cmd %p)",
                            -		(long long unsigned int)cmd->lun, scst_get_opcode_name(cmd), cmd);
                            +		(unsigned long long int)cmd->lun, scst_get_opcode_name(cmd), cmd);
                             	PRINT_BUFFER("Invalid exec_reply", reply, sizeof(*reply));
                             
                            -out_hwerr:
                            +out_intern_fail:
                             	res = -EINVAL;
                             
                            -out_hwerr_res_set:
                            +out_intern_fail_res_set:
                             	if (ucmd->background_exec) {
                             		ucmd_put(ucmd);
                             		goto out;
                             	} else {
                             		scst_set_cmd_error(cmd,
                            -				   SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				   SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		goto out_compl;
                             	}
                             
                            @@ -1996,7 +1996,7 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg)
                             		goto out_up;
                             	}
                             
                            -	TRACE_DBG("ureply %lld (dev %s)", (long long unsigned int)ureply,
                            +	TRACE_DBG("ureply %lld (dev %s)", (unsigned long long int)ureply,
                             		dev->name);
                             
                             	cmd = kmem_cache_alloc(user_get_cmd_cachep, GFP_KERNEL);
                            @@ -2260,7 +2260,7 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
                             				scst_set_busy(ucmd->cmd);
                             			else
                             				scst_set_cmd_error(ucmd->cmd,
                            -				       SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				       SCST_LOAD_SENSE(scst_sense_lun_not_supported));
                             		}
                             		scst_set_cmd_abnormal_done_state(ucmd->cmd);
                             
                            @@ -2291,7 +2291,7 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
                             				scst_set_busy(ucmd->cmd);
                             			else
                             				scst_set_cmd_error(ucmd->cmd,
                            -				       SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				       SCST_LOAD_SENSE(scst_sense_lun_not_supported));
                             		}
                             
                             		ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT,
                            diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c
                            index 3e45ec1b2..165ec00cc 100644
                            --- a/scst/src/dev_handlers/scst_vdisk.c
                            +++ b/scst/src/dev_handlers/scst_vdisk.c
                            @@ -184,7 +184,7 @@ struct scst_vdisk_dev {
                             	uint64_t format_progress_to_do, format_progress_done;
                             
                             	int virt_id;
                            -	char name[16+1];	/* Name of the virtual device,
                            +	char name[64+1];	/* Name of the virtual device,
                             				   must be <= SCSI Model + 1 */
                             	char *filename;		/* File name, protected by
                             				   scst_mutex and suspended activities */
                            @@ -1387,8 +1387,8 @@ static int vdisk_attach(struct scst_device *dev)
                             		      (dev->type == TYPE_DISK) ? "disk" : "cdrom",
                             		      virt_dev->name, vdev_get_filename(virt_dev),
                             		      virt_dev->file_size >> 20, dev->block_size,
                            -		      (long long unsigned int)virt_dev->nblocks,
                            -		      (long long unsigned int)virt_dev->nblocks/64/32,
                            +		      (unsigned long long int)virt_dev->nblocks,
                            +		      (unsigned long long int)virt_dev->nblocks/64/32,
                             		      virt_dev->nblocks < 64*32
                             		      ? " !WARNING! cyln less than 1" : "");
                             	} else {
                            @@ -1541,8 +1541,8 @@ static enum compl_status_e vdisk_synchronize_cache(struct vdisk_cmd_params *p)
                             
                             	TRACE(TRACE_ORDER, "SYNCHRONIZE_CACHE: "
                             	      "loff=%lld, data_len=%lld, immed=%d",
                            -	      (long long unsigned int)loff,
                            -	      (long long unsigned int)data_len, immed);
                            +	      (unsigned long long int)loff,
                            +	      (unsigned long long int)data_len, immed);
                             
                             	if (data_len == 0) {
                             		struct scst_vdisk_dev *virt_dev = dev->dh_priv;
                            @@ -2326,9 +2326,9 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd)
                             
                             	loff = (loff_t)lba_start << dev->block_shift;
                             	TRACE_DBG("cmd %p, lba_start %lld, loff %lld, data_len %lld", cmd,
                            -		  (long long unsigned int)lba_start,
                            -		  (long long unsigned int)loff,
                            -		  (long long unsigned int)data_len);
                            +		  (unsigned long long int)lba_start,
                            +		  (unsigned long long int)loff,
                            +		  (unsigned long long int)data_len);
                             
                             	EXTRACHECKS_BUG_ON((loff < 0) || unlikely(data_len < 0));
                             
                            @@ -2340,9 +2340,9 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd)
                             		} else {
                             			PRINT_INFO("Access beyond the end of device %s "
                             				"(%lld of %lld, data len %lld)", virt_dev->name,
                            -				(long long unsigned int)loff,
                            -				(long long unsigned int)virt_dev->file_size,
                            -				(long long unsigned int)data_len);
                            +				(unsigned long long int)loff,
                            +				(unsigned long long int)virt_dev->file_size,
                            +				(unsigned long long int)data_len);
                             			scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
                             					scst_sense_block_out_range_error));
                             		}
                            @@ -2358,8 +2358,8 @@ static bool vdisk_parse_offset(struct vdisk_cmd_params *p, struct scst_cmd *cmd)
                             		fua = (cdb[1] & 0x8);
                             		if (fua) {
                             			TRACE(TRACE_ORDER, "FUA: loff=%lld, "
                            -				"data_len=%lld", (long long unsigned int)loff,
                            -				(long long unsigned int)data_len);
                            +				"data_len=%lld", (unsigned long long int)loff,
                            +				(unsigned long long int)data_len);
                             		}
                             		break;
                             	}
                            @@ -2777,7 +2777,8 @@ static int fileio_alloc_data_buf(struct scst_cmd *cmd)
                             	 * copy.
                             	 */
                             	if (cmd->tgt_i_data_buf_alloced ||
                            -	    (cmd->data_direction & SCST_DATA_READ) == 0) {
                            +	    (cmd->data_direction & SCST_DATA_READ) == 0 ||
                            +	    (virt_dev->fd && !virt_dev->fd->f_mapping->a_ops->readpage)) {
                             		p->use_zero_copy = false;
                             	}
                             	if (!p->use_zero_copy)
                            @@ -3119,7 +3120,7 @@ static int vdisk_unmap_range(struct scst_cmd *cmd,
                             #endif
                             	} else {
                             		loff_t off = start_lba << cmd->dev->block_shift;
                            -		loff_t len = blocks << cmd->dev->block_shift;
                            +		loff_t len = (u64)blocks << cmd->dev->block_shift;
                             
                             		res = vdisk_unmap_file_range(cmd, virt_dev, off, len, fd);
                             		if (unlikely(res != 0))
                            @@ -3847,16 +3848,21 @@ static int vdisk_format_pg(unsigned char *p, int pcontrol,
                             
                             static int vdisk_caching_pg(unsigned char *p, int pcontrol,
                             			     struct scst_vdisk_dev *virt_dev)
                            -{	/* Caching page for mode_sense */
                            -	unsigned char caching_pg[] = {0x8, 0x12, 0x0, 0, 0, 0, 0, 0,
                            -		0, 0, 0, 0, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
                            +{
                            +	/* Caching page for mode_sense */
                            +	static const unsigned char caching_pg[] = {
                            +		0x8, 0x12, 0x0, 0, 0, 0, 0, 0,
                            +		0, 0, 0, 0, 0x80, 0x14, 0, 0,
                            +		0, 0, 0, 0
                            +	};
                            +
                            +	memcpy(p, caching_pg, sizeof(caching_pg));
                             
                             	if (!virt_dev->nv_cache && vdev_saved_mode_pages_enabled)
                            -		caching_pg[0] |= 0x80;
                            +		p[0] |= 0x80;
                             
                             	switch (pcontrol) {
                             	case 0: /* current */
                            -		memcpy(p, caching_pg, sizeof(caching_pg));
                             		p[2] |= (virt_dev->wt_flag || virt_dev->nv_cache) ? 0 : WCE;
                             		break;
                             	case 1: /* changeable */
                            @@ -3865,11 +3871,9 @@ static int vdisk_caching_pg(unsigned char *p, int pcontrol,
                             			p[2] |= WCE;
                             		break;
                             	case 2: /* default */
                            -		memcpy(p, caching_pg, sizeof(caching_pg));
                             		p[2] |= (DEF_WRITE_THROUGH || virt_dev->nv_cache) ? 0 : WCE;
                             		break;
                             	case 3: /* saved */
                            -		memcpy(p, caching_pg, sizeof(caching_pg));
                             		p[2] |= (virt_dev->wt_flag_saved || virt_dev->nv_cache) ? 0 : WCE;
                             		break;
                             	default:
                            @@ -4923,7 +4927,7 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p)
                             	if (unlikely(length < 0)) {
                             		PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
                             		scst_set_cmd_error(cmd,
                            -		    SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +		    SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		goto out;
                             	}
                             
                            @@ -4952,7 +4956,7 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p)
                             		} else if (unlikely(length < 0)) {
                             			PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
                             			scst_set_cmd_error(cmd,
                            -			    SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +			    SCST_LOAD_SENSE(scst_sense_internal_failure));
                             			goto out_set_fs;
                             		}
                             
                            @@ -4964,7 +4968,7 @@ static enum compl_status_e fileio_exec_read(struct vdisk_cmd_params *p)
                             
                             		if ((err < 0) || (err < full_len)) {
                             			PRINT_ERROR("readv() returned %lld from %zd",
                            -				    (long long unsigned int)err,
                            +				    (unsigned long long int)err,
                             				    full_len);
                             			if (err == -EAGAIN)
                             				scst_set_busy(cmd);
                            @@ -5046,7 +5050,7 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p)
                             	if (unlikely(length < 0)) {
                             		PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
                             		scst_set_cmd_error(cmd,
                            -		    SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +		    SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		goto out;
                             	}
                             
                            @@ -5074,7 +5078,7 @@ static enum compl_status_e fileio_exec_write(struct vdisk_cmd_params *p)
                             		} else if (unlikely(length < 0)) {
                             			PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
                             			scst_set_cmd_error(cmd,
                            -			    SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +			    SCST_LOAD_SENSE(scst_sense_internal_failure));
                             			goto out_set_fs;
                             		}
                             
                            @@ -5089,7 +5093,7 @@ restart:
                             
                             		if (err < 0) {
                             			PRINT_ERROR("write() returned %lld from %zd",
                            -				    (long long unsigned int)err,
                            +				    (unsigned long long int)err,
                             				    full_len);
                             			if (err == -EAGAIN)
                             				scst_set_busy(cmd);
                            @@ -5319,15 +5323,18 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua)
                             
                             	/* Allocate and initialize blockio_work struct */
                             	blockio_work = kmem_cache_alloc(blockio_work_cachep, gfp_mask);
                            -	if (blockio_work == NULL)
                            -		goto out_no_mem;
                            +	if (blockio_work == NULL) {
                            +		scst_set_busy(cmd);
                            +		goto finish_cmd;
                            +	}
                             
                             #if 0
                             	{
                             		static int err_inj_cntr;
                             		if (++err_inj_cntr % 256 == 0) {
                             			PRINT_INFO("blockio_exec_rw() error injection");
                            -			goto out_no_bio;
                            +			scst_set_busy(cmd);
                            +			goto free_bio;
                             		}
                             	}
                             #endif
                            @@ -5345,6 +5352,18 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua)
                             	need_new_bio = 1;
                             
                             	length = scst_get_sg_page_first(cmd, &page, &offset);
                            +	/*
                            +	 * bv_len and bv_offset must be a multiple of 512 (SECTOR_SIZE), so
                            +	 * check this here.
                            +	 */
                            +	if (WARN_ONCE((length & 511) != 0 || (offset & 511) != 0,
                            +		      "Refused bio with invalid length %d and/or offset %d.\n",
                            +		      length, offset)) {
                            +		scst_set_cmd_error(cmd,
                            +				   SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +		goto free_bio;
                            +	}
                            +
                             	while (length > 0) {
                             		int len, bytes, off, thislen;
                             		struct page *pg;
                            @@ -5370,7 +5389,8 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua)
                             					PRINT_ERROR("Failed to create bio "
                             						"for data segment %d (cmd %p)",
                             						cmd->get_sg_buf_entry_num, cmd);
                            -					goto out_no_bio;
                            +					scst_set_busy(cmd);
                            +					goto free_bio;
                             				}
                             
                             				bios++;
                            @@ -5457,7 +5477,7 @@ out:
                             	TRACE_EXIT();
                             	return;
                             
                            -out_no_bio:
                            +free_bio:
                             	while (hbio) {
                             		bio = hbio;
                             		hbio = hbio->bi_next;
                            @@ -5465,8 +5485,7 @@ out_no_bio:
                             	}
                             	kmem_cache_free(blockio_work_cachep, blockio_work);
                             
                            -out_no_mem:
                            -	scst_set_busy(cmd);
                            +finish_cmd:
                             	cmd->completed = 1;
                             	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
                             	goto out;
                            @@ -5703,17 +5722,7 @@ static ssize_t fileio_read_sync(struct file *fd, void *buf, size_t len,
                             
                             	old_fs = get_fs();
                             	set_fs(get_ds());
                            -
                            -	if (fd->f_op->llseek)
                            -		ret = fd->f_op->llseek(fd, *loff, 0/*SEEK_SET*/);
                            -	else
                            -		ret = default_llseek(fd, *loff, 0/*SEEK_SET*/);
                            -	if (ret < 0)
                            -		goto out;
                            -
                             	ret = vfs_read(fd, (char __force __user *)buf, len, loff);
                            -
                            -out:
                             	set_fs(old_fs);
                             
                             	return ret;
                            @@ -5728,17 +5737,7 @@ static ssize_t fileio_write_sync(struct file *fd, void *buf, size_t len,
                             
                             	old_fs = get_fs();
                             	set_fs(get_ds());
                            -
                            -	if (fd->f_op->llseek)
                            -		ret = fd->f_op->llseek(fd, *loff, 0/*SEEK_SET*/);
                            -	else
                            -		ret = default_llseek(fd, *loff, 0/*SEEK_SET*/);
                            -	if (ret < 0)
                            -		goto out;
                            -
                             	ret = vfs_write(fd, (char __force __user *)buf, len, loff);
                            -
                            -out:
                             	set_fs(old_fs);
                             
                             	return ret;
                            @@ -5837,7 +5836,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p)
                             		err = vdev_read_sync(virt_dev, mem_verify, len_mem, &loff);
                             		if ((err < 0) || (err < len_mem)) {
                             			PRINT_ERROR("verify() returned %lld from %zd",
                            -				    (long long unsigned int)err, len_mem);
                            +				    (unsigned long long int)err, len_mem);
                             			if (err == -EAGAIN)
                             				scst_set_busy(cmd);
                             			else {
                            @@ -5868,7 +5867,7 @@ static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p)
                             	if (length < 0) {
                             		PRINT_ERROR("scst_get_buf_() failed: %zd", length);
                             		scst_set_cmd_error(cmd,
                            -		    SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +		    SCST_LOAD_SENSE(scst_sense_internal_failure));
                             	}
                             
                             out_free:
                            @@ -5926,7 +5925,7 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p)
                             			scst_set_busy(cmd);
                             		else
                             			scst_set_cmd_error(cmd,
                            -				SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		goto out;
                             	}
                             
                            @@ -6067,64 +6066,69 @@ static void vdisk_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd,
                             
                             static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev)
                             {
                            -	char buf[128];
                            +	enum { buf_size = 256 };
                            +	char *buf = kmalloc(buf_size, GFP_KERNEL);
                             	int i, j;
                             
                            -	i = snprintf(buf, sizeof(buf), "Registering virtual %s device %s ",
                            +	if (!buf) {
                            +		PRINT_ERROR("%s: out of memory", __func__);
                            +		return;
                            +	}
                            +
                            +	i = snprintf(buf, buf_size, "Registering virtual %s device %s ",
                             		virt_dev->vdev_devt->name, virt_dev->name);
                             	j = i;
                             
                             	if (virt_dev->wt_flag)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "(WRITE_THROUGH");
                            +		i += snprintf(&buf[i], buf_size - i, "(WRITE_THROUGH");
                             
                             	if (virt_dev->nv_cache)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sNV_CACHE",
                            +		i += snprintf(&buf[i], buf_size - i, "%sNV_CACHE",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->rd_only)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sREAD_ONLY",
                            +		i += snprintf(&buf[i], buf_size - i, "%sREAD_ONLY",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->o_direct_flag)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sO_DIRECT",
                            +		i += snprintf(&buf[i], buf_size - i, "%sO_DIRECT",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->nullio)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sNULLIO",
                            +		i += snprintf(&buf[i], buf_size - i, "%sNULLIO",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->blockio)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sBLOCKIO",
                            +		i += snprintf(&buf[i], buf_size - i, "%sBLOCKIO",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->removable)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE",
                            +		i += snprintf(&buf[i], buf_size - i, "%sREMOVABLE",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->tst != DEF_TST)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sTST %d",
                            +		i += snprintf(&buf[i], buf_size - i, "%sTST %d",
                             			(j == i) ? "(" : ", ", virt_dev->tst);
                             
                             	if (virt_dev->rotational)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sROTATIONAL",
                            +		i += snprintf(&buf[i], buf_size - i, "%sROTATIONAL",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->thin_provisioned)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sTHIN_PROVISIONED",
                            +		i += snprintf(&buf[i], buf_size - i, "%sTHIN_PROVISIONED",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->zero_copy)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sZERO_COPY",
                            +		i += snprintf(&buf[i], buf_size - i, "%sZERO_COPY",
                             			(j == i) ? "(" : ", ");
                             
                             	if (virt_dev->dummy)
                            -		i += snprintf(&buf[i], sizeof(buf) - i, "%sDUMMY",
                            +		i += snprintf(&buf[i], buf_size - i, "%sDUMMY",
                             			(j == i) ? "(" : ", ");
                             
                            -	if (j == i)
                            -		PRINT_INFO("%s", buf);
                            -	else
                            -		PRINT_INFO("%s)", buf);
                            +	PRINT_INFO("%s%s", buf, j == i ? "" : ")");
                            +
                            +	kfree(buf);
                             
                             	return;
                             }
                            @@ -6159,8 +6163,8 @@ static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev)
                             		"(fs=%lldMB, bs=%d, nblocks=%lld, cyln=%lld%s)",
                             		virt_dev->name, virt_dev->file_size >> 20,
                             		virt_dev->dev->block_size,
                            -		(long long unsigned int)virt_dev->nblocks,
                            -		(long long unsigned int)virt_dev->nblocks/64/32,
                            +		(unsigned long long int)virt_dev->nblocks,
                            +		(unsigned long long int)virt_dev->nblocks/64/32,
                             		virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
                             						"than 1" : "");
                             
                            @@ -6959,8 +6963,8 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
                             			" cyln=%lld%s)", virt_dev->name,
                             			vdev_get_filename(virt_dev),
                             			virt_dev->file_size >> 20, virt_dev->dev->block_size,
                            -			(long long unsigned int)virt_dev->nblocks,
                            -			(long long unsigned int)virt_dev->nblocks/64/32,
                            +			(unsigned long long int)virt_dev->nblocks,
                            +			(unsigned long long int)virt_dev->nblocks/64/32,
                             			virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
                             							"than 1" : "");
                             	} else {
                            @@ -8033,7 +8037,7 @@ static ssize_t vdev_sysfs_naa_id_store(struct kobject *kobj,
                             	switch (c) {
                             	case 0:
                             	case 2 * 8:
                            -		if (strchr("1235cCdDeEfF", buf[0]))
                            +		if (strchr("235", buf[0]))
                             			break;
                             		else
                             			goto out;
                            diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c
                            index f05ac814e..5bd950d06 100644
                            --- a/scst/src/scst_lib.c
                            +++ b/scst/src/scst_lib.c
                            @@ -109,17 +109,18 @@ char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
                              */
                             int hex_to_bin(char ch)
                             {
                            -        if (ch >= '0' && ch <= '9')
                            -                return ch - '0';
                            -        ch = tolower(ch);
                            -        if (ch >= 'a' && ch <= 'f')
                            -                return ch - 'a' + 10;
                            -        return -1;
                            +	if (ch >= '0' && ch <= '9')
                            +		return ch - '0';
                            +	ch = tolower(ch);
                            +	if (ch >= 'a' && ch <= 'f')
                            +		return ch - 'a' + 10;
                            +	return -1;
                             }
                             EXPORT_SYMBOL(hex_to_bin);
                             #endif
                             
                            -#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) && !defined(HAVE_SG_COPY)
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) || \
                            +	!defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                             static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                             #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
                             	    int nents_to_copy, size_t copy_len,
                            @@ -554,17 +555,17 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
                             	{.ops = 0x04, .devkey = "M    O O        ",
                             	 .info_op_name = "FORMAT UNIT",
                             	 .info_data_direction = SCST_DATA_NONE,
                            -	 .info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
                            +	 .info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM|SCST_STRICTLY_SERIALIZED,
                             	 .get_cdb_info = get_cdb_info_fmt},
                             	{.ops = 0x04, .devkey = " O              ",
                             	 .info_op_name = "FORMAT MEDIUM",
                             	 .info_data_direction = SCST_DATA_WRITE,
                            -	 .info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM,
                            +	 .info_op_flags = SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM|SCST_STRICTLY_SERIALIZED,
                             	 .info_len_off = 3, .get_cdb_info = get_cdb_info_len_2},
                             	{.ops = 0x04, .devkey = "  O             ",
                             	 .info_op_name = "FORMAT",
                             	 .info_data_direction = SCST_DATA_NONE,
                            -	 .info_op_flags = SCST_WRITE_MEDIUM,
                            +	 .info_op_flags = SCST_WRITE_MEDIUM|SCST_STRICTLY_SERIALIZED,
                             	 .get_cdb_info = get_cdb_info_none},
                             	{.ops = 0x05, .devkey = "VMVVVV  V       ",
                             	 .info_op_name = "READ BLOCK LIMITS",
                            @@ -952,15 +953,15 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
                             	 .info_op_name = "WRITE BUFFER",
                             	 .info_data_direction = SCST_DATA_WRITE,
                             	 .info_op_flags = SCST_SMALL_TIMEOUT,
                            -	 .info_len_off = 5, .info_len_len = 2,
                            -	 .get_cdb_info = get_cdb_info_len_2},
                            +	 .info_len_off = 6, .info_len_len = 3,
                            +	 .get_cdb_info = get_cdb_info_len_3},
                             	{.ops = 0x3C, .devkey = "OOOOOOOOOOOOOOOO",
                             	 .info_op_name = "READ BUFFER",
                             	 .info_data_direction = SCST_DATA_READ,
                             	 .info_op_flags = SCST_SMALL_TIMEOUT |
                             		 SCST_WRITE_EXCL_ALLOWED,
                            -	 .info_len_off = 5, .info_len_len = 2,
                            -	 .get_cdb_info = get_cdb_info_len_2},
                            +	 .info_len_off = 6, .info_len_len = 3,
                            +	 .get_cdb_info = get_cdb_info_len_3},
                             	{.ops = 0x3D, .devkey = "    O  O        ",
                             	 .info_op_name = "UPDATE BLOCK",
                             	 .info_data_direction = SCST_DATA_WRITE,
                            @@ -3176,7 +3177,7 @@ static bool __scst_adjust_sg(struct scst_cmd *cmd, struct scatterlist *sg,
                             			TRACE_DBG_FLAG(TRACE_SG_OP|TRACE_MEMORY|TRACE_DEBUG,
                             				"cmd %p (tag %llu), sg %p, sg_cnt %d, "
                             				"adjust_len %d, i %d, sg[j].length %d, left %d",
                            -				cmd, (long long unsigned int)cmd->tag,
                            +				cmd, (unsigned long long int)cmd->tag,
                             				sg, *sg_cnt, adjust_len, i,
                             				sgi->length, left);
                             
                            @@ -4642,7 +4643,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess,
                             		scst_sgv_pool_use_dma(tgt_dev);
                             
                             	TRACE_MGMT_DBG("Device %s on SCST lun=%lld",
                            -	       dev->virt_name, (long long unsigned int)tgt_dev->lun);
                            +	       dev->virt_name, (unsigned long long int)tgt_dev->lun);
                             
                             	spin_lock_init(&tgt_dev->tgt_dev_lock);
                             	INIT_LIST_HEAD(&tgt_dev->UA_list);
                            @@ -5128,7 +5129,7 @@ static void scst_complete_request_sense(struct scst_cmd *req_cmd)
                             			PRINT_ERROR("%s", "Unable to get the sense via "
                             				"REQUEST SENSE, returning HARDWARE ERROR");
                             			scst_set_cmd_error(orig_cmd,
                            -				SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		}
                             	}
                             
                            @@ -5917,7 +5918,7 @@ void scst_free_cmd(struct scst_cmd *cmd)
                             	TRACE_ENTRY();
                             
                             	TRACE_DBG("Freeing cmd %p (tag %llu)",
                            -		  cmd, (long long unsigned int)cmd->tag);
                            +		  cmd, (unsigned long long int)cmd->tag);
                             
                             	if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)))
                             		TRACE_MGMT_DBG("Freeing aborted cmd %p", cmd);
                            @@ -5958,7 +5959,7 @@ void scst_free_cmd(struct scst_cmd *cmd)
                             					&cmd->cmd_flags);
                             			TRACE_SN("Out of SN cmd %p (tag %llu, sn %d), "
                             				"destroy=%d", cmd,
                            -				(long long unsigned int)cmd->tag,
                            +				(unsigned long long int)cmd->tag,
                             				cmd->sn, destroy);
                             		}
                             	}
                            @@ -6221,8 +6222,373 @@ out:
                             	return;
                             }
                             
                            -#if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) && !defined(HAVE_SG_COPY)
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
                            +struct blk_kern_sg_work {
                            +	atomic_t bios_inflight;
                            +	struct sg_table sg_table;
                            +	struct scatterlist *src_sgl;
                            +};
                             
                            +static void blk_free_kern_sg_work(struct blk_kern_sg_work *bw)
                            +{
                            +	struct sg_table *sgt = &bw->sg_table;
                            +	struct scatterlist *sg;
                            +	struct page *pg;
                            +	int i;
                            +
                            +	for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
                            +		pg = sg_page(sg);
                            +		if (pg == NULL)
                            +			break;
                            +		__free_page(pg);
                            +	}
                            +
                            +	sg_free_table(sgt);
                            +	kfree(bw);
                            +	return;
                            +}
                            +
                            +static void blk_bio_map_kern_endio(struct bio *bio, int err)
                            +{
                            +	struct blk_kern_sg_work *bw = bio->bi_private;
                            +
                            +	if (bw != NULL) {
                            +		/* Decrement the bios in processing and, if zero, free */
                            +		BUG_ON(atomic_read(&bw->bios_inflight) <= 0);
                            +		if (atomic_dec_and_test(&bw->bios_inflight)) {
                            +			if (bio_data_dir(bio) == READ && err == 0) {
                            +				unsigned long flags;
                            +
                            +				local_irq_save(flags);	/* to protect KMs */
                            +				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
                            +					, KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ
                            +#endif
                            +					);
                            +				local_irq_restore(flags);
                            +			}
                            +			blk_free_kern_sg_work(bw);
                            +		}
                            +	}
                            +
                            +	bio_put(bio);
                            +	return;
                            +}
                            +
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
                            +/*
                            + * See also patch "block: Add blk_make_request(), takes bio, returns a
                            + * request" (commit 79eb63e9e5875b84341a3a05f8e6ae9cdb4bb6f6).
                            + */
                            +static struct request *blk_make_request(struct request_queue *q,
                            +					struct bio *bio,
                            +					gfp_t gfp_mask)
                            +{
                            +	struct request *rq = blk_get_request(q, bio_data_dir(bio), gfp_mask);
                            +
                            +	if (unlikely(!rq))
                            +		return ERR_PTR(-ENOMEM);
                            +
                            +	rq->cmd_type = REQ_TYPE_BLOCK_PC;
                            +
                            +	for ( ; bio; bio = bio->bi_next) {
                            +		struct bio *bounce_bio = bio;
                            +		int ret;
                            +
                            +		blk_queue_bounce(q, &bounce_bio);
                            +		ret = blk_rq_append_bio(q, rq, bounce_bio);
                            +		if (unlikely(ret)) {
                            +			blk_put_request(rq);
                            +			return ERR_PTR(ret);
                            +		}
                            +	}
                            +
                            +	return rq;
                            +}
                            +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
                            +
                            +/*
                            + * Copy an sg-list. This function is related to bio_copy_kern() but duplicates
                            + * an sg-list instead of creating a bio out of a single kernel address range.
                            + */
                            +static struct blk_kern_sg_work *blk_copy_kern_sg(struct request_queue *q,
                            +	struct scatterlist *sgl, int nents, gfp_t gfp_mask, bool reading)
                            +{
                            +	int res = 0, i;
                            +	struct scatterlist *sg;
                            +	struct scatterlist *new_sgl;
                            +	int new_sgl_nents;
                            +	size_t len = 0, to_copy;
                            +	struct blk_kern_sg_work *bw;
                            +
                            +	res = -ENOMEM;
                            +	bw = kzalloc(sizeof(*bw), gfp_mask);
                            +	if (bw == NULL)
                            +		goto err;
                            +
                            +	bw->src_sgl = sgl;
                            +
                            +	for_each_sg(sgl, sg, nents, i)
                            +		len += sg->length;
                            +	to_copy = len;
                            +
                            +	new_sgl_nents = PFN_UP(len);
                            +
                            +	res = sg_alloc_table(&bw->sg_table, new_sgl_nents, gfp_mask);
                            +	if (res != 0)
                            +		goto err_free_bw;
                            +
                            +	new_sgl = bw->sg_table.sgl;
                            +
                            +	res = -ENOMEM;
                            +	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
                            +		struct page *pg;
                            +
                            +		pg = alloc_page(q->bounce_gfp | gfp_mask);
                            +		if (pg == NULL)
                            +			goto err_free_table;
                            +
                            +		sg_assign_page(sg, pg);
                            +		sg->length = min_t(size_t, PAGE_SIZE, len);
                            +
                            +		len -= PAGE_SIZE;
                            +	}
                            +
                            +	if (!reading) {
                            +		/*
                            +		 * We need to limit amount of copied data to to_copy, because
                            +		 * sgl might have the last element in sgl not marked as last in
                            +		 * SG chaining.
                            +		 */
                            +		sg_copy(new_sgl, sgl, 0, to_copy
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
                            +			, KM_USER0, KM_USER1
                            +#endif
                            +			);
                            +	}
                            +
                            +out:
                            +	return bw;
                            +
                            +err_free_table:
                            +	sg_free_table(&bw->sg_table);
                            +
                            +err_free_bw:
                            +	blk_free_kern_sg_work(bw);
                            +
                            +err:
                            +	sBUG_ON(res == 0);
                            +	bw = ERR_PTR(res);
                            +	goto out;
                            +}
                            +
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
                            +static void bio_kmalloc_destructor(struct bio *bio)
                            +{
                            +	kfree(bio->bi_io_vec);
                            +	kfree(bio);
                            +}
                            +#endif
                            +
                            +/* __blk_map_kern_sg - map kernel data to a request for REQ_TYPE_BLOCK_PC */
                            +static struct request *__blk_map_kern_sg(struct request_queue *q,
                            +	struct scatterlist *sgl, int nents, struct blk_kern_sg_work *bw,
                            +	gfp_t gfp_mask, bool reading)
                            +{
                            +	struct request *rq;
                            +	int max_nr_vecs, i;
                            +	size_t tot_len;
                            +	bool need_new_bio;
                            +	struct scatterlist *sg;
                            +	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
                            +	int bios;
                            +
                            +	if (unlikely(sgl == NULL || sgl->length == 0 || nents <= 0)) {
                            +		WARN_ON_ONCE(true);
                            +		rq = ERR_PTR(-EINVAL);
                            +		goto out;
                            +	}
                            +
                            +	/*
                            +	 * Restrict bio size to a single page to minimize the probability that
                            +	 * bio allocation fails.
                            +	 */
                            +	max_nr_vecs = min_t(int,
                            +		(PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec),
                            +		BIO_MAX_PAGES);
                            +
                            +	need_new_bio = true;
                            +	tot_len = 0;
                            +	bios = 0;
                            +	for_each_sg(sgl, sg, nents, i) {
                            +		struct page *page = sg_page(sg);
                            +		void *page_addr = page_address(page);
                            +		size_t len = sg->length, l;
                            +		size_t offset = sg->offset;
                            +
                            +		tot_len += len;
                            +
                            +		/*
                            +		 * Each segment must be DMA-aligned and must not reside not on
                            +		 * the stack. The last segment may have unaligned length as
                            +		 * long as the total length satisfies the DMA padding
                            +		 * alignment requirements.
                            +		 */
                            +		if (i == nents - 1)
                            +			l = 0;
                            +		else
                            +			l = len;
                            +		if (((sg->offset | l) & queue_dma_alignment(q)) ||
                            +		    (page_addr && object_is_on_stack(page_addr + sg->offset))) {
                            +			rq = ERR_PTR(-EINVAL);
                            +			goto out_free_bios;
                            +		}
                            +
                            +		while (len > 0) {
                            +			size_t bytes;
                            +			int rc;
                            +
                            +			if (need_new_bio) {
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
                            +				bio = bio_alloc_bioset(gfp_mask, max_nr_vecs, NULL);
                            +				if (bio)
                            +					bio->bi_destructor =
                            +						bio_kmalloc_destructor;
                            +#else
                            +				bio = bio_kmalloc(gfp_mask, max_nr_vecs);
                            +#endif
                            +				if (bio == NULL) {
                            +					rq = ERR_PTR(-ENOMEM);
                            +					goto out_free_bios;
                            +				}
                            +
                            +				if (!reading)
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
                            +					bio->bi_rw |= 1 << BIO_RW;
                            +#else
                            +					bio->bi_rw |= REQ_WRITE;
                            +#endif
                            +				bios++;
                            +				bio->bi_private = bw;
                            +				bio->bi_end_io = blk_bio_map_kern_endio;
                            +
                            +				if (hbio == NULL)
                            +					hbio = bio;
                            +				else
                            +					tbio->bi_next = bio;
                            +				tbio = bio;
                            +			}
                            +
                            +			bytes = min_t(size_t, len, PAGE_SIZE - offset);
                            +
                            +			rc = bio_add_pc_page(q, bio, page, bytes, offset);
                            +			if (rc < bytes) {
                            +				if (unlikely(need_new_bio || rc < 0)) {
                            +					rq = ERR_PTR(rc < 0 ? rc : -EIO);
                            +					goto out_free_bios;
                            +				} else {
                            +					need_new_bio = true;
                            +					len -= rc;
                            +					offset += rc;
                            +				}
                            +			} else {
                            +				need_new_bio = false;
                            +				offset = 0;
                            +				len -= bytes;
                            +				page = nth_page(page, 1);
                            +			}
                            +		}
                            +	}
                            +
                            +	if (hbio == NULL) {
                            +		rq = ERR_PTR(-EINVAL);
                            +		goto out_free_bios;
                            +	}
                            +
                            +	/* Total length must satisfy DMA padding alignment */
                            +	if ((tot_len & q->dma_pad_mask) && bw != NULL) {
                            +		rq = ERR_PTR(-EINVAL);
                            +		goto out_free_bios;
                            +	}
                            +
                            +	rq = blk_make_request(q, hbio, gfp_mask);
                            +	if (unlikely(IS_ERR(rq)))
                            +		goto out_free_bios;
                            +
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
                            +	/*
                            +	 * See also patch "block: add blk_rq_set_block_pc()" (commit
                            +	 * f27b087b81b7).
                            +	 */
                            +	rq->cmd_type = REQ_TYPE_BLOCK_PC;
                            +#endif
                            +
                            +	if (bw != NULL) {
                            +		atomic_set(&bw->bios_inflight, bios);
                            +		rq->cmd_flags |= REQ_COPY_USER;
                            +	}
                            +
                            +out:
                            +	return rq;
                            +
                            +out_free_bios:
                            +	while (hbio != NULL) {
                            +		bio = hbio;
                            +		hbio = hbio->bi_next;
                            +		bio_put(bio);
                            +	}
                            +	goto out;
                            +}
                            +
                            +/**
                            + * blk_map_kern_sg - map kernel data to a request for REQ_TYPE_BLOCK_PC
                            + * @rq:		request to fill
                            + * @sgl:	area to map
                            + * @nents:	number of elements in @sgl
                            + * @gfp:	memory allocation flags
                            + *
                            + * Description:
                            + *    Data will be mapped directly if possible. Otherwise a bounce
                            + *    buffer will be used.
                            + */
                            +static struct request *blk_map_kern_sg(struct request_queue *q,
                            +		struct scatterlist *sgl, int nents, gfp_t gfp, bool reading)
                            +{
                            +	struct request *rq;
                            +
                            +	if (!sgl) {
                            +		rq = blk_get_request(q, reading ? READ : WRITE, gfp);
                            +		if (unlikely(!rq))
                            +			return ERR_PTR(-ENOMEM);
                            +
                            +		rq->cmd_type = REQ_TYPE_BLOCK_PC;
                            +		goto out;
                            +	}
                            +
                            +	rq = __blk_map_kern_sg(q, sgl, nents, NULL, gfp, reading);
                            +	if (unlikely(IS_ERR(rq))) {
                            +		struct blk_kern_sg_work *bw;
                            +
                            +		bw = blk_copy_kern_sg(q, sgl, nents, gfp, reading);
                            +		if (unlikely(IS_ERR(bw))) {
                            +			rq = ERR_PTR(PTR_ERR(bw));
                            +			goto out;
                            +		}
                            +
                            +		rq = __blk_map_kern_sg(q, bw->sg_table.sgl, bw->sg_table.nents,
                            +				       bw, gfp, reading);
                            +		if (IS_ERR(rq)) {
                            +			blk_free_kern_sg_work(bw);
                            +			goto out;
                            +		}
                            +	}
                            +
                            +out:
                            +	return rq;
                            +}
                            +#endif
                            +
                            +#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                             /*
                              * Can switch to the next dst_sg element, so, to copy to strictly only
                              * one dst_sg element, it must be either last in the chain, or
                            @@ -6374,16 +6740,22 @@ static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
                             out:
                             	return res;
                             }
                            +#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
                             
                            -#endif /* !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) */
                            -
                            -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
                             static void scsi_end_async(struct request *req, int error)
                             {
                             	struct scsi_io_context *sioc = req->end_io_data;
                             
                             	TRACE_DBG("sioc %p, cmd %p", sioc, sioc->data);
                             
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
                            +	lockdep_assert_held(req->q->queue_lock);
                            +#else
                            +	if (!req->q->mq_ops)
                            +		lockdep_assert_held(req->q->queue_lock);
                            +#endif
                            +
                             	if (sioc->done)
                             #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
                             		sioc->done(sioc->data, sioc->sense, req->errors, req->data_len);
                            @@ -6410,7 +6782,7 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
                             	struct request_queue *q = cmd->dev->scsi_dev->request_queue;
                             	struct request *rq;
                             	struct scsi_io_context *sioc;
                            -	int write = (cmd->data_direction & SCST_DATA_WRITE) ? WRITE : READ;
                            +	bool reading = !(cmd->data_direction & SCST_DATA_WRITE);
                             	gfp_t gfp = cmd->cmd_gfp_mask;
                             	int cmd_len = cmd->cdb_len;
                             
                            @@ -6420,54 +6792,38 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
                             		goto out;
                             	}
                             
                            -	rq = blk_get_request(q, write, gfp);
                            -	if (rq == NULL) {
                            -		res = -ENOMEM;
                            -		goto out_free_sioc;
                            -	}
                            -
                            -	rq->cmd_type = REQ_TYPE_BLOCK_PC;
                            -	rq->cmd_flags |= REQ_QUIET;
                            -
                            -	if (cmd->sg == NULL)
                            -		goto done;
                            -
                             	if (cmd->data_direction == SCST_DATA_BIDI) {
                             		struct request *next_rq;
                             
                             		if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) {
                             			res = -EOPNOTSUPP;
                            -			goto out_free_rq;
                            +			goto out;
                             		}
                             
                            -		res = blk_rq_map_kern_sg(rq, cmd->out_sg, cmd->out_sg_cnt, gfp);
                            -		if (res != 0) {
                            -			TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res);
                            -			goto out_free_rq;
                            +		rq = blk_map_kern_sg(q, cmd->out_sg, cmd->out_sg_cnt, gfp,
                            +				     reading);
                            +		if (IS_ERR(rq)) {
                            +			res = PTR_ERR(rq);
                            +			TRACE_DBG("blk_map_kern_sg() failed: %d", res);
                            +			goto out;
                             		}
                             
                            -		next_rq = blk_get_request(q, READ, gfp);
                            -		if (next_rq == NULL) {
                            -			res = -ENOMEM;
                            +		next_rq = blk_map_kern_sg(q, cmd->sg, cmd->sg_cnt, gfp, false);
                            +		if (IS_ERR(next_rq)) {
                            +			res = PTR_ERR(next_rq);
                            +			TRACE_DBG("blk_map_kern_sg() failed: %d", res);
                             			goto out_free_unmap;
                             		}
                             		rq->next_rq = next_rq;
                            -		next_rq->cmd_type = rq->cmd_type;
                            -
                            -		res = blk_rq_map_kern_sg(next_rq, cmd->sg, cmd->sg_cnt, gfp);
                            -		if (res != 0) {
                            -			TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res);
                            -			goto out_free_unmap;
                            -		}
                             	} else {
                            -		res = blk_rq_map_kern_sg(rq, cmd->sg, cmd->sg_cnt, gfp);
                            -		if (res != 0) {
                            -			TRACE_DBG("blk_rq_map_kern_sg() failed: %d", res);
                            -			goto out_free_rq;
                            +		rq = blk_map_kern_sg(q, cmd->sg, cmd->sg_cnt, gfp, reading);
                            +		if (IS_ERR(rq)) {
                            +			res = PTR_ERR(rq);
                            +			TRACE_DBG("blk_map_kern_sg() failed: %d", res);
                            +			goto out;
                             		}
                             	}
                             
                            -done:
                             	TRACE_DBG("sioc %p, cmd %p", sioc, cmd);
                             
                             	sioc->data = data;
                            @@ -6485,6 +6841,7 @@ done:
                             	rq->timeout = cmd->timeout;
                             	rq->retries = cmd->retries;
                             	rq->end_io_data = sioc;
                            +	rq->cmd_flags |= REQ_QUIET;
                             
                             	blk_execute_rq_nowait(rq->q, NULL, rq,
                             		(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE), scsi_end_async);
                            @@ -6492,22 +6849,23 @@ out:
                             	return res;
                             
                             out_free_unmap:
                            -	if (rq->next_rq != NULL) {
                            -		blk_put_request(rq->next_rq);
                            -		rq->next_rq = NULL;
                            +	{
                            +	struct bio *bio = rq->bio, *b;
                            +
                            +	while (bio) {
                            +		b = bio;
                            +		bio = bio->bi_next;
                            +		b->bi_end_io(b, res);
                             	}
                            -	blk_rq_unmap_kern_sg(rq, res);
                            +	}
                            +	rq->bio = NULL;
                             
                            -out_free_rq:
                             	blk_put_request(rq);
                            -
                            -out_free_sioc:
                            -	kmem_cache_free(scsi_io_context_cache, sioc);
                             	goto out;
                             }
                             EXPORT_SYMBOL(scst_scsi_exec_async);
                             
                            -#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) && defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
                            +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
                             
                             /**
                              * scst_copy_sg() - copy data between the command's SGs
                            @@ -6657,7 +7015,7 @@ int scst_get_buf_full_sense(struct scst_cmd *cmd, uint8_t **buf)
                             			scst_set_busy(cmd);
                             		else
                             			scst_set_cmd_error(cmd,
                            -				SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		goto out;
                             	}
                             
                            @@ -7523,7 +7881,8 @@ int scst_calc_block_shift(int sector_size)
                             		sector_size = 512;
                             
                             	block_shift = ilog2(sector_size);
                            -	WARN_ON(1 << block_shift != sector_size);
                            +	WARN_ONCE(1 << block_shift != sector_size, "1 << %d != %d\n",
                            +		  block_shift, sector_size);
                             
                             	if (block_shift < 9) {
                             		PRINT_ERROR("Wrong sector size %d", sector_size);
                            @@ -8600,7 +8959,7 @@ static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev)
                             	list_for_each_entry_safe(UA_entry, t,
                             				 &tgt_dev->UA_list, UA_list_entry) {
                             		TRACE_MGMT_DBG("Clearing UA for tgt_dev LUN %lld",
                            -			       (long long unsigned int)tgt_dev->lun);
                            +			       (unsigned long long int)tgt_dev->lun);
                             		list_del(&UA_entry->UA_list_entry);
                             		mempool_free(UA_entry, scst_ua_mempool);
                             	}
                            @@ -8683,7 +9042,7 @@ restart:
                             			 * !! destroyed!				     !!
                             			 */
                             			TRACE_SN("cmd %p (tag %llu) with skipped sn %d found",
                            -				 cmd, (long long unsigned int)cmd->tag, cmd->sn);
                            +				 cmd, (unsigned long long int)cmd->tag, cmd->sn);
                             			order_data->def_cmd_count--;
                             			list_del(&cmd->deferred_cmd_list_entry);
                             			spin_unlock_irq(&order_data->sn_lock);
                            @@ -8783,13 +9142,13 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd)
                             	if (dev->block_count > 0) {
                             		TRACE_BLOCK("Delaying cmd %p due to blocking "
                             			"(tag %llu, op %s, dev %s)", cmd,
                            -			(long long unsigned int)cmd->tag,
                            +			(unsigned long long int)cmd->tag,
                             			scst_get_opcode_name(cmd), dev->virt_name);
                             		goto out_block;
                             	} else if ((cmd->op_flags & SCST_STRICTLY_SERIALIZED) == SCST_STRICTLY_SERIALIZED) {
                             		TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further "
                             			"cmds on dev %s due to strict serialization", cmd,
                            -			(long long unsigned int)cmd->tag,
                            +			(unsigned long long int)cmd->tag,
                             			scst_get_opcode_name(cmd), dev->virt_name);
                             		scst_block_dev(dev);
                             		if (dev->on_dev_cmd_count > 1) {
                            @@ -8804,7 +9163,7 @@ bool __scst_check_blocked_dev(struct scst_cmd *cmd)
                             	} else if ((dev->dev_double_ua_possible) ||
                             		   ((cmd->op_flags & SCST_SERIALIZED) != 0)) {
                             		TRACE_BLOCK("cmd %p (tag %llu, op %s): blocking further cmds "
                            -			"on dev %s due to %s", cmd, (long long unsigned int)cmd->tag,
                            +			"on dev %s due to %s", cmd, (unsigned long long int)cmd->tag,
                             			scst_get_opcode_name(cmd), dev->virt_name,
                             			dev->dev_double_ua_possible ? "possible double reset UA" :
                             						      "serialized cmd");
                            @@ -9253,13 +9612,13 @@ void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd)
                             		if (test_bit(SCST_CMD_DEVICE_TAS, &cmd->cmd_flags)) {
                             			TRACE_MGMT_DBG("Flag ABORTED OTHER set for cmd %p "
                             				"(tag %llu), returning TASK ABORTED ", cmd,
                            -				(long long unsigned int)cmd->tag);
                            +				(unsigned long long int)cmd->tag);
                             			scst_set_cmd_error_status(cmd, SAM_STAT_TASK_ABORTED);
                             		} else {
                             			TRACE_MGMT_DBG("Flag ABORTED OTHER set for cmd %p "
                             				"(tag %llu), aborting without delivery or "
                             				"notification",
                            -				cmd, (long long unsigned int)cmd->tag);
                            +				cmd, (unsigned long long int)cmd->tag);
                             			/*
                             			 * There is no need to check/requeue possible UA,
                             			 * because, if it exists, it will be delivered
                            @@ -10680,7 +11039,7 @@ void tm_dbg_release_cmd(struct scst_cmd *cmd)
                             				if (((scst_random() % 10) == 5)) {
                             					scst_set_cmd_error(cmd,
                             						SCST_LOAD_SENSE(
                            -						scst_sense_hardw_error));
                            +							scst_sense_internal_failure));
                             					/* It's completed now */
                             				}
                             			}
                            diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c
                            index 14fa55849..6c40d22ea 100644
                            --- a/scst/src/scst_main.c
                            +++ b/scst/src/scst_main.c
                            @@ -47,18 +47,13 @@ option or use a 64-bit configuration instead. See README file for \
                             details.
                             #endif
                             
                            -#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
                            -#if !defined(CONFIG_SCST_STRICT_SERIALIZING)
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) && \
                            +	!defined(SCSI_EXEC_REQ_FIFO_DEFINED) &&	     \
                            +	!defined(CONFIG_SCST_STRICT_SERIALIZING)
                             #warning Patch scst_exec_req_fifo- was not applied on \
                             your kernel and CONFIG_SCST_STRICT_SERIALIZING is not defined. \
                             Pass-through dev handlers will not work.
                            -#endif /* !defined(CONFIG_SCST_STRICT_SERIALIZING) */
                            -#else  /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
                            -#warning Patch scst_exec_req_fifo- was not applied on \
                            -your kernel. Pass-through dev handlers will not work.
                            -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
                            -#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
                            +#endif
                             
                             /**
                              ** SCST global variables. They are all uninitialized to have their layout in
                            @@ -1607,26 +1602,18 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type,
                             	if (res != 0)
                             		goto out;
                             
                            -#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) && \
                            +	!defined(SCSI_EXEC_REQ_FIFO_DEFINED) && \
                            +	!defined(CONFIG_SCST_STRICT_SERIALIZING)
                             	if (dev_type->exec == NULL) {
                            -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
                            -#if !defined(CONFIG_SCST_STRICT_SERIALIZING)
                             		PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not "
                             			"supported. Consider applying on your kernel patch "
                             			"scst_exec_req_fifo- or define "
                             			"CONFIG_SCST_STRICT_SERIALIZING", dev_type->name);
                             		res = -EINVAL;
                             		goto out;
                            -#endif /* !defined(CONFIG_SCST_STRICT_SERIALIZING) */
                            -#else  /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
                            -		PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not "
                            -			"supported. Consider applying on your kernel patch "
                            -			"scst_exec_req_fifo-", dev_type->name);
                            -		res = -EINVAL;
                            -		goto out;
                            -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
                             	}
                            -#endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
                            +#endif
                             
                             #ifdef CONFIG_SCST_PROC
                             	res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER);
                            @@ -2513,55 +2500,59 @@ static int __init init_scst(void)
                             	}
                             
                             /* Used for rarely used or read-mostly on fast path structures */
                            -#define INIT_CACHEP(p, s, o) do {					\
                            -		p = KMEM_CACHE(s, SCST_SLAB_FLAGS);			\
                            -		TRACE_MEM("Slab create: %s at %p size %zd", #s, p,	\
                            +#define INIT_CACHEP(p, s) ({						\
                            +		(p) = KMEM_CACHE(s, SCST_SLAB_FLAGS);			\
                            +		TRACE_MEM("Slab create: %s at %p size %zd", #s, (p),	\
                             			  sizeof(struct s));				\
                            -		if (p == NULL) {					\
                            -			res = -ENOMEM;					\
                            -			goto o;						\
                            -		}							\
                            -	} while (0)
                            +		(p);							\
                            +	})
                             
                             /* Used for structures with fast path write access */
                            -#define INIT_CACHEP_ALIGN(p, s, o) do {					\
                            -		p = KMEM_CACHE(s, SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);	\
                            -		TRACE_MEM("Slab create: %s at %p size %zd", #s, p,	\
                            +#define INIT_CACHEP_ALIGN(p, s) ({					\
                            +		(p) = KMEM_CACHE(s, SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);\
                            +		TRACE_MEM("Slab create: %s at %p size %zd", #s, (p),	\
                             			  sizeof(struct s));				\
                            -		if (p == NULL) {					\
                            -			res = -ENOMEM;					\
                            -			goto o;						\
                            -		}							\
                            -	} while (0)
                            +		(p);							\
                            +	})
                             
                            -	INIT_CACHEP(scst_mgmt_cachep, scst_mgmt_cmd, out_lib_exit);
                            -	INIT_CACHEP(scst_mgmt_stub_cachep, scst_mgmt_cmd_stub,
                            -			out_destroy_mgmt_cache);
                            -	INIT_CACHEP(scst_ua_cachep, scst_tgt_dev_UA,
                            -			out_destroy_mgmt_stub_cache);
                            +	res = -ENOMEM;
                            +	if (!INIT_CACHEP(scst_mgmt_cachep, scst_mgmt_cmd))
                            +		goto out_lib_exit;
                            +	if (!INIT_CACHEP(scst_mgmt_stub_cachep, scst_mgmt_cmd_stub))
                            +		goto out_destroy_mgmt_cache;
                            +	if (!INIT_CACHEP(scst_ua_cachep, scst_tgt_dev_UA))
                            +		goto out_destroy_mgmt_stub_cache;
                             	{
                             		struct scst_sense { uint8_t s[SCST_SENSE_BUFFERSIZE]; };
                            -		INIT_CACHEP(scst_sense_cachep, scst_sense,
                            -			    out_destroy_ua_cache);
                            +		if (!INIT_CACHEP(scst_sense_cachep, scst_sense))
                            +			goto out_destroy_ua_cache;
                             	}
                            -	INIT_CACHEP(scst_aen_cachep, scst_aen, out_destroy_sense_cache); /* read-mostly */
                            -	INIT_CACHEP_ALIGN(scst_cmd_cachep, scst_cmd, out_destroy_aen_cache);
                            +	if (!INIT_CACHEP(scst_aen_cachep, scst_aen)) /* read-mostly */
                            +		goto out_destroy_sense_cache;
                            +	if (!INIT_CACHEP_ALIGN(scst_cmd_cachep, scst_cmd))
                            +		goto out_destroy_aen_cache;
                             #ifdef CONFIG_SCST_MEASURE_LATENCY
                            -	INIT_CACHEP_ALIGN(scst_sess_cachep, scst_session,
                            -			  out_destroy_cmd_cache);
                            +	if (!INIT_CACHEP_ALIGN(scst_sess_cachep, scst_session))
                            +		goto out_destroy_cmd_cache;
                             #else
                             	/* Big enough with read-mostly head and tail */
                            -	INIT_CACHEP(scst_sess_cachep, scst_session, out_destroy_cmd_cache);
                            +	if (!INIT_CACHEP(scst_sess_cachep, scst_session))
                            +		goto out_destroy_cmd_cache;
                             #endif
                            -	INIT_CACHEP(scst_dev_cachep, scst_device, out_destroy_sess_cache); /* big enough */
                            -	INIT_CACHEP(scst_tgt_cachep, scst_tgt, out_destroy_dev_cache); /* read-mostly */
                            +	if (!INIT_CACHEP(scst_dev_cachep, scst_device)) /* big enough */
                            +		goto out_destroy_sess_cache;
                            +	if (!INIT_CACHEP(scst_tgt_cachep, scst_tgt)) /* read-mostly */
                            +		goto out_destroy_dev_cache;
                             #ifdef CONFIG_SCST_MEASURE_LATENCY
                            -	INIT_CACHEP_ALIGN(scst_tgtd_cachep, scst_tgt_dev, out_destroy_tgt_cache); /* big enough */
                            +	if (!INIT_CACHEP_ALIGN(scst_tgtd_cachep, scst_tgt_dev)) /* big enough */
                            +		goto out_destroy_tgt_cache;
                             #else
                             	/* Big enough with read-mostly head and tail */
                            -	INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev, out_destroy_tgt_cache); /* big enough */
                            +	if (!INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev)) /* big enough */
                            +		goto out_destroy_tgt_cache;
                             #endif
                            -	INIT_CACHEP(scst_acgd_cachep, scst_acg_dev, out_destroy_tgtd_cache); /* read-mostly */
                            +	if (!INIT_CACHEP(scst_acgd_cachep, scst_acg_dev)) /* read-mostly */
                            +		goto out_destroy_tgtd_cache;
                             
                             	scst_mgmt_mempool = mempool_create(64, mempool_alloc_slab,
                             		mempool_free_slab, scst_mgmt_cachep);
                            @@ -2571,7 +2562,7 @@ static int __init init_scst(void)
                             	}
                             
                             	/*
                            -	 * All mgmt stubs, UAs and sense buffers are bursty and loosing them
                            +	 * All mgmt stubs, UAs and sense buffers are bursty and losing them
                             	 * may have fatal consequences, so let's have big pools for them.
                             	 */
                             
                            diff --git a/scst/src/scst_mem.c b/scst/src/scst_mem.c
                            index 7bc6b4418..b3ab75e1a 100644
                            --- a/scst/src/scst_mem.c
                            +++ b/scst/src/scst_mem.c
                            @@ -350,7 +350,7 @@ out_unlock_put:
                             	goto out;
                             }
                             
                            -static unsigned long __sgv_can_be_shrinked(void)
                            +static unsigned long __sgv_can_be_shrunk(void)
                             {
                             	unsigned long res;
                             	struct sgv_pool *pool;
                            @@ -374,10 +374,10 @@ static unsigned long __sgv_can_be_shrinked(void)
                             }
                             
                             #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
                            -static unsigned long sgv_can_be_shrinked(struct shrinker *shrinker,
                            +static unsigned long sgv_can_be_shrunk(struct shrinker *shrinker,
                             					 struct shrink_control *sc)
                             {
                            -	return __sgv_can_be_shrinked();
                            +	return __sgv_can_be_shrunk();
                             }
                             
                             static unsigned long sgv_scan_shrink(struct shrinker *shrinker,
                            @@ -413,7 +413,7 @@ static int sgv_shrink(struct shrinker *shrinker, struct shrink_control *sc)
                             		nr = __sgv_shrink(nr, SGV_MIN_SHRINK_INTERVAL, &freed);
                             		TRACE_MEM("Left %d", nr);
                             	} else
                            -		nr = __sgv_can_be_shrinked();
                            +		nr = __sgv_can_be_shrunk();
                             
                             	TRACE_EXIT_RES(nr);
                             	return nr;
                            @@ -1809,7 +1809,7 @@ int scst_sgv_pools_init(unsigned long mem_hwmark, unsigned long mem_lwmark)
                             	sgv_shrinker = set_shrinker(DEFAULT_SEEKS, sgv_shrink);
                             #else
                             #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
                            -	sgv_shrinker.count_objects = sgv_can_be_shrinked;
                            +	sgv_shrinker.count_objects = sgv_can_be_shrunk;
                             	sgv_shrinker.scan_objects = sgv_scan_shrink;
                             #else
                             	sgv_shrinker.shrink = sgv_shrink;
                            diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c
                            index 9e862bbcf..2f5ecbdbe 100644
                            --- a/scst/src/scst_pres.c
                            +++ b/scst/src/scst_pres.c
                            @@ -1036,7 +1036,7 @@ out:
                                    * the affected initiator.
                                    */
                             		if (cmd != NULL)
                            -			scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +			scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
                             #endif
                             	}
                             
                            diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h
                            index 4f742c8a0..fce62d9af 100644
                            --- a/scst/src/scst_priv.h
                            +++ b/scst/src/scst_priv.h
                            @@ -412,15 +412,6 @@ static inline int scst_exec_req(struct scsi_device *sdev,
                             	    (void *)sgl, bufflen, nents, timeout, retries, privdata, done, gfp);
                             #endif
                             }
                            -#else /* i.e. LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
                            -#if !defined(SCSI_EXEC_REQ_FIFO_DEFINED)
                            -static inline int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
                            -	void (*done)(void *data, char *sense, int result, int resid))
                            -{
                            -	WARN_ON_ONCE(1);
                            -	return -1;
                            -}
                            -#endif
                             #endif
                             
                             int scst_alloc_space(struct scst_cmd *cmd);
                            diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c
                            index d00101511..e1b458bcb 100644
                            --- a/scst/src/scst_proc.c
                            +++ b/scst/src/scst_proc.c
                            @@ -2546,7 +2546,7 @@ static int scst_groups_devices_show(struct seq_file *seq, void *v)
                             	list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
                             		seq_printf(seq, "%-60s%-13lld%s\n",
                             			       acg_dev->dev->virt_name,
                            -			       (long long unsigned int)acg_dev->lun,
                            +			       (unsigned long long int)acg_dev->lun,
                             			       acg_dev->acg_dev_rd_only ? "RO" : "");
                             	}
                             	mutex_unlock(&scst_mutex);
                            diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c
                            index ac28b01b5..71be58f89 100644
                            --- a/scst/src/scst_sysfs.c
                            +++ b/scst/src/scst_sysfs.c
                            @@ -313,7 +313,9 @@ out:
                             
                             #endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */
                             
                            -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) &&	\
                            +	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 ||	\
                            +	 (RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 6))
                             /**
                              ** Backported sysfs functions.
                              **/
                            @@ -1266,6 +1268,7 @@ static int __scst_process_luns_mgmt_store(char *buffer,
                             			goto out_unlock;
                             		} else if (virt_lun > SCST_MAX_LUN) {
                             			PRINT_ERROR("Too big LUN %ld (max %d)", virt_lun, SCST_MAX_LUN);
                            +			res = -EINVAL;
                             			goto out_unlock;
                             		}
                             
                            diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c
                            index eda3226b8..8980b9b5e 100644
                            --- a/scst/src/scst_targ.c
                            +++ b/scst/src/scst_targ.c
                            @@ -157,7 +157,7 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd)
                             
                             	if (unlikely(cmd->unblock_dev)) {
                             		TRACE_BLOCK("cmd %p (tag %llu): unblocking dev %s", cmd,
                            -			(long long unsigned int)cmd->tag, dev->virt_name);
                            +			(unsigned long long int)cmd->tag, dev->virt_name);
                             		cmd->unblock_dev = 0;
                             		scst_unblock_dev(dev);
                             	} else if (unlikely(dev->strictly_serialized_cmd_waiting)) {
                            @@ -416,9 +416,9 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
                             	TRACE_DBG("Preferred context: %d (cmd %p)", pref_context, cmd);
                             	TRACE(TRACE_SCSI, "NEW CDB: len %d, lun %lld, initiator %s, "
                             		"target %s, queue_type %x, tag %llu (cmd %p, sess %p)",
                            -		cmd->cdb_len, (long long unsigned int)cmd->lun,
                            +		cmd->cdb_len, (unsigned long long int)cmd->lun,
                             		cmd->sess->initiator_name, cmd->tgt->tgt_name, cmd->queue_type,
                            -		(long long unsigned int)cmd->tag, cmd, sess);
                            +		(unsigned long long int)cmd->tag, cmd, sess);
                             	PRINT_BUFF_FLAG(TRACE_SCSI, "CDB", cmd->cdb, cmd->cdb_len);
                             
                             #ifdef CONFIG_SCST_EXTRACHECKS
                            @@ -655,6 +655,9 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
                             			goto out;
                             
                             		case SCST_CMD_STATE_STOP:
                            +			/*
                            +			 * !! cmd can be dead now!
                            +			 */
                             			TRACE_DBG("Dev handler %s parse() requested stop "
                             				"processing", devt->name);
                             			res = SCST_CMD_STATE_RES_CONT_NEXT;
                            @@ -939,11 +942,11 @@ set_res:
                             		TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_MINOR, "Atomic context and "
                             			"non-WRITE data direction, rescheduling (cmd %p)", cmd);
                             		res = SCST_CMD_STATE_RES_NEED_THREAD;
                            -		goto out;
                            +		/* go through */
                             	}
                             #endif
                             
                            -out:
                            +out_check_compl:
                             #ifdef CONFIG_SCST_EXTRACHECKS
                             	if (unlikely(cmd->completed)) {
                             		/* Command completed with error */
                            @@ -1000,6 +1003,7 @@ out:
                             		}
                             	}
                             
                            +out:
                             	TRACE_EXIT_HRES(res);
                             	return res;
                             
                            @@ -1010,7 +1014,7 @@ out_hw_error:
                             out_done:
                             	scst_set_cmd_abnormal_done_state(cmd);
                             	res = SCST_CMD_STATE_RES_CONT_SAME;
                            -	goto out;
                            +	goto out_check_compl;
                             }
                             
                             static void scst_set_write_len(struct scst_cmd *cmd)
                            @@ -1259,7 +1263,7 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status,
                             
                             	TRACE_DBG("Preferred context: %d", pref_context);
                             	TRACE_DBG("tag=%llu, status=%#x",
                            -		  (long long unsigned int)scst_cmd_get_tag(cmd),
                            +		  (unsigned long long int)scst_cmd_get_tag(cmd),
                             		  status);
                             
                             #ifdef CONFIG_SCST_EXTRACHECKS
                            @@ -1774,14 +1778,16 @@ static inline enum scst_exec_context scst_optimize_post_exec_context(
                              */
                             void scst_pass_through_cmd_done(void *data, char *sense, int result, int resid)
                             {
                            -	struct scst_cmd *cmd;
                            +	struct scst_cmd *cmd = data;
                             
                             	TRACE_ENTRY();
                             
                            -	cmd = (struct scst_cmd *)data;
                             	if (cmd == NULL)
                             		goto out;
                             
                            +	TRACE_DBG("cmd %p; CDB[0/%d] %#x: result %d; resid %d", cmd,
                            +		  cmd->cdb_len, cmd->cdb[0], result, resid);
                            +
                             	scst_do_cmd_done(cmd, result, sense, SCSI_SENSE_BUFFERSIZE, resid);
                             
                             	cmd->state = SCST_CMD_STATE_PRE_DEV_DONE;
                            @@ -2348,7 +2354,7 @@ static int scst_reserve_local(struct scst_cmd *cmd)
                             
                             	if ((cmd->cdb[0] == RESERVE_10) && (cmd->cdb[2] & SCST_RES_3RDPTY)) {
                             		PRINT_ERROR("RESERVE_10: 3rdPty RESERVE not implemented "
                            -		     "(lun=%lld)", (long long unsigned int)cmd->lun);
                            +		     "(lun=%lld)", (unsigned long long int)cmd->lun);
                             		scst_set_invalid_field_in_cdb(cmd, 2,
                             			SCST_INVAL_FIELD_BIT_OFFS_VALID | 4);
                             		goto out_done;
                            @@ -2983,14 +2989,16 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
                             		sBUG_ON(res != SCST_EXEC_NOT_COMPLETED);
                             	}
                             
                            -	TRACE_DBG("Sending cmd %p to SCSI mid-level", cmd);
                            -
                             	scsi_dev = dev->scsi_dev;
                             
                            +	TRACE_DBG("Sending cmd %p to SCSI mid-level dev %d:%d:%d:%lld", cmd,
                            +		  scsi_dev->host->host_no, scsi_dev->channel, scsi_dev->id,
                            +		  (u64)scsi_dev->lun);
                            +
                             	if (unlikely(scsi_dev == NULL)) {
                             		PRINT_ERROR("Command for virtual device must be "
                             			"processed by device handler (LUN %lld)!",
                            -			(long long unsigned int)cmd->lun);
                            +			(unsigned long long int)cmd->lun);
                             		goto out_error;
                             	}
                             
                            @@ -3334,7 +3342,7 @@ static int scst_exec_check_sn(struct scst_cmd **active_cmd)
                             				/* Necessary to allow aborting out of sn cmds */
                             				TRACE_MGMT_DBG("Aborting out of sn cmd %p "
                             					"(tag %llu, sn %u)", cmd,
                            -					(long long unsigned)cmd->tag, cmd->sn);
                            +					(unsigned long long)cmd->tag, cmd->sn);
                             				order_data->def_cmd_count--;
                             				scst_set_cmd_abnormal_done_state(cmd);
                             				res = SCST_CMD_STATE_RES_CONT_SAME;
                            @@ -3414,7 +3422,7 @@ static int scst_check_sense(struct scst_cmd *cmd)
                             						"detected for device %p", dev);
                             					TRACE_DBG("Retrying cmd"
                             						" %p (tag %llu)", cmd,
                            -						(long long unsigned)cmd->tag);
                            +						(unsigned long long)cmd->tag);
                             
                             					cmd->status = 0;
                             					cmd->msg_status = 0;
                            @@ -3513,7 +3521,7 @@ static bool scst_check_auto_sense(struct scst_cmd *cmd)
                             				"%s)", cmd->host_status, cmd, scst_get_opcode_name(cmd),
                             				cmd->tgt->tgt_name, cmd->dev->virt_name);
                             			scst_set_cmd_error(cmd,
                            -				SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		}
                             	}
                             
                            @@ -3541,7 +3549,7 @@ static int scst_pre_dev_done(struct scst_cmd *cmd)
                             			PRINT_ERROR("%s", "Unable to issue REQUEST SENSE, "
                             				    "returning HARDWARE ERROR");
                             			scst_set_cmd_error(cmd,
                            -				SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		}
                             		goto out;
                             	}
                            @@ -3577,7 +3585,7 @@ next:
                             					"MODE_SENSE buffer");
                             				scst_set_cmd_error(cmd,
                             					SCST_LOAD_SENSE(
                            -						scst_sense_hardw_error));
                            +						scst_sense_internal_failure));
                             				err = true;
                             			} else if (length > 2 && cmd->cdb[0] == MODE_SENSE)
                             				address[2] |= 0x80;   /* Write Protect*/
                            @@ -3609,7 +3617,7 @@ next:
                             					PRINT_INFO("NormACA set for device: "
                             						"lun=%lld, type 0x%02x. Clear it, "
                             						"since it's unsupported.",
                            -						(long long unsigned int)cmd->lun,
                            +						(unsigned long long int)cmd->lun,
                             						buffer[0]);
                             				}
                             #endif
                            @@ -3618,7 +3626,7 @@ next:
                             				PRINT_ERROR("%s", "Unable to get INQUIRY "
                             				    "buffer");
                             				scst_set_cmd_error(cmd,
                            -				       SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +				       SCST_LOAD_SENSE(scst_sense_internal_failure));
                             				err = true;
                             			}
                             			if (buflen > 0)
                            @@ -3632,7 +3640,7 @@ next:
                             		    (cmd->cdb[0] == MODE_SELECT_10) ||
                             		    (cmd->cdb[0] == LOG_SELECT))) {
                             			TRACE(TRACE_SCSI, "MODE/LOG SELECT succeeded (LUN %lld)",
                            -				(long long unsigned int)cmd->lun);
                            +				(unsigned long long int)cmd->lun);
                             			cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS;
                             			goto out;
                             		}
                            @@ -3645,7 +3653,7 @@ next:
                             					SCST_SENSE_ASCx_VALID,
                             					0, 0x2a, 0x01)) {
                             			TRACE(TRACE_SCSI, "MODE PARAMETERS CHANGED UA (lun "
                            -				"%lld)", (long long unsigned int)cmd->lun);
                            +				"%lld)", (unsigned long long int)cmd->lun);
                             			cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS;
                             			goto out;
                             		}
                            @@ -3682,7 +3690,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
                             
                             			TRACE(TRACE_SCSI, "MODE/LOG SELECT succeeded, "
                             				"setting the SELECT UA (lun=%lld)",
                            -				(long long unsigned int)cmd->lun);
                            +				(unsigned long long int)cmd->lun);
                             
                             			spin_lock_bh(&dev->dev_lock);
                             			if (cmd->cdb[0] == LOG_SELECT) {
                            @@ -3728,7 +3736,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
                             
                             		TRACE(TRACE_SCSI, "Possible parameters changed UA %x "
                             			"(LUN %lld): getting new parameters", cmd->sense[12],
                            -			(long long unsigned int)cmd->lun);
                            +			(unsigned long long int)cmd->lun);
                             
                             		scst_obtain_device_parameters(cmd->dev, NULL);
                             	} else
                            @@ -3885,7 +3893,7 @@ again:
                             	if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) {
                             		EXTRACHECKS_BUG_ON(!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags));
                             		TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), "
                            -			"skipping", cmd, (long long unsigned int)cmd->tag);
                            +			"skipping", cmd, (unsigned long long int)cmd->tag);
                             		cmd->state = SCST_CMD_STATE_FINISHED;
                             		goto out_same;
                             	}
                            @@ -4390,7 +4398,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
                             
                             	if (likely(!test_bit(SCST_FLAG_SUSPENDED, &scst_flags))) {
                             		TRACE_DBG("Finding tgt_dev for cmd %p (lun %lld)", cmd,
                            -			(long long unsigned int)cmd->lun);
                            +			(unsigned long long int)cmd->lun);
                             		res = -1;
                             		tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
                             		if (tgt_dev) {
                            @@ -4407,7 +4415,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
                             			} else {
                             				PRINT_INFO("Dev handler for device %lld is NULL, "
                             					"the device will not be visible remotely",
                            -					(long long unsigned int)cmd->lun);
                            +					(unsigned long long int)cmd->lun);
                             				nul_dev = true;
                             			}
                             		}
                            @@ -4416,7 +4424,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
                             				TRACE(TRACE_MINOR,
                             					"tgt_dev for LUN %lld not found, command to "
                             					"unexisting LU (initiator %s, target %s)?",
                            -					(long long unsigned int)cmd->lun,
                            +					(unsigned long long int)cmd->lun,
                             					cmd->sess->initiator_name, cmd->tgt->tgt_name);
                             			}
                             			scst_put(cmd->cpu_cmd_counter);
                            @@ -4559,7 +4567,7 @@ restart:
                             			}
                             		} else {
                             			TRACE_MGMT_DBG("Aborting not inited cmd %p (tag %llu)",
                            -				       cmd, (long long unsigned int)cmd->tag);
                            +				       cmd, (unsigned long long int)cmd->tag);
                             			scst_set_cmd_abnormal_done_state(cmd);
                             		}
                             
                            @@ -4781,7 +4789,7 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
                             				res = SCST_CMD_STATE_RES_CONT_NEXT;
                             				TRACE_MGMT_DBG("Skipping cmd %p (tag %llu), "
                             					"because of TM DBG delay", cmd,
                            -					(long long unsigned int)cmd->tag);
                            +					(unsigned long long int)cmd->tag);
                             				break;
                             			}
                             			res = scst_exec_check_sn(&cmd);
                            @@ -5029,7 +5037,7 @@ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd)
                             	TRACE_ENTRY();
                             
                             	TRACE_DBG("Finding tgt_dev for mgmt cmd %p (lun %lld)", mcmd,
                            -	      (long long unsigned int)mcmd->lun);
                            +	      (unsigned long long int)mcmd->lun);
                             
                             	res = scst_get_mgmt(mcmd);
                             	if (unlikely(res != 0))
                            @@ -5060,7 +5068,7 @@ void scst_done_cmd_mgmt(struct scst_cmd *cmd)
                             	TRACE_ENTRY();
                             
                             	TRACE_MGMT_DBG("cmd %p done (tag %llu)",
                            -		       cmd, (long long unsigned int)cmd->tag);
                            +		       cmd, (unsigned long long int)cmd->tag);
                             
                             	spin_lock_irqsave(&scst_mcmd_lock, flags);
                             
                            @@ -5216,7 +5224,7 @@ void scst_finish_cmd_mgmt(struct scst_cmd *cmd)
                             	TRACE_ENTRY();
                             
                             	TRACE(TRACE_MGMT, "Aborted cmd %p finished (tag %llu, ref %d)", cmd,
                            -		(long long unsigned int)cmd->tag, atomic_read(&cmd->cmd_ref));
                            +		(unsigned long long int)cmd->tag, atomic_read(&cmd->cmd_ref));
                             
                             	spin_lock_irqsave(&scst_mcmd_lock, flags);
                             
                            @@ -5317,7 +5325,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
                             		EXTRACHECKS_BUG_ON(!mcmd);
                             
                             	TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "Aborting cmd %p (tag %llu, op %s)",
                            -		cmd, (long long unsigned int)cmd->tag, scst_get_opcode_name(cmd));
                            +		cmd, (unsigned long long int)cmd->tag, scst_get_opcode_name(cmd));
                             
                             	/* To protect from concurrent aborts */
                             	spin_lock_irqsave(&other_ini_lock, flags);
                            @@ -5399,7 +5407,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
                             
                             		if (cmd->sent_for_exec && !cmd->done) {
                             			TRACE_MGMT_DBG("cmd %p (tag %llu) is being executed",
                            -				cmd, (long long unsigned int)cmd->tag);
                            +				cmd, (unsigned long long int)cmd->tag);
                             			mstb->done_counted = 1;
                             			mcmd->cmd_done_wait_count++;
                             		}
                            @@ -5425,7 +5433,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
                             				"deferring ABORT (cmd_done_wait_count %d, "
                             				"cmd_finish_wait_count %d, internal %d, mcmd "
                             				"fn %d (mcmd %p), initiator %s, target %s)",
                            -				cmd, (long long unsigned int)cmd->tag,
                            +				cmd, (unsigned long long int)cmd->tag,
                             				cmd->sn, cmd->state, scst_get_opcode_name(cmd),
                             				(long)(jiffies - cmd->start_time) / HZ,
                             				cmd->timeout / HZ, mcmd->cmd_done_wait_count,
                            @@ -5649,7 +5657,7 @@ static int scst_abort_task_set(struct scst_mgmt_cmd *mcmd)
                             	struct scst_tgt_dev *tgt_dev = mcmd->mcmd_tgt_dev;
                             
                             	TRACE(TRACE_MGMT, "Aborting task set (lun=%lld, mcmd=%p)",
                            -	      (long long unsigned int)tgt_dev->lun, mcmd);
                            +	      (unsigned long long int)tgt_dev->lun, mcmd);
                             
                             	__scst_abort_task_set(mcmd, tgt_dev);
                             
                            @@ -5681,7 +5689,7 @@ static bool scst_is_cmd_belongs_to_dev(struct scst_cmd *cmd,
                             	TRACE_ENTRY();
                             
                             	TRACE_DBG("Finding match for dev %s and cmd %p (lun %lld)",
                            -		  dev->virt_name, cmd, (long long unsigned int)cmd->lun);
                            +		  dev->virt_name, cmd, (unsigned long long int)cmd->lun);
                             
                             	tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
                             	res = tgt_dev && tgt_dev->dev == dev;
                            @@ -5701,7 +5709,7 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
                             	TRACE_ENTRY();
                             
                             	TRACE(TRACE_MGMT, "Clearing task set (lun=%lld, mcmd=%p)",
                            -		(long long unsigned int)mcmd->lun, mcmd);
                            +		(unsigned long long int)mcmd->lun, mcmd);
                             
                             #if 0 /* we are SAM-3 */
                             	/*
                            @@ -5816,7 +5824,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
                             		if (cmd == NULL) {
                             			TRACE_MGMT_DBG("ABORT TASK: command "
                             			      "for tag %llu not found",
                            -			      (long long unsigned int)mcmd->tag);
                            +			      (unsigned long long int)mcmd->tag);
                             			scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_TASK_NOT_EXIST);
                             			spin_unlock_irq(&sess->sess_list_lock);
                             			res = scst_set_mcmd_next_state(mcmd);
                            @@ -5828,7 +5836,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
                             			mcmd->cpu_cmd_counter = scst_get();
                             		spin_unlock_irq(&sess->sess_list_lock);
                             		TRACE_DBG("Cmd to abort %p for tag %llu found (tgt_dev %p)",
                            -			cmd, (long long unsigned int)mcmd->tag, tgt_dev);
                            +			cmd, (unsigned long long int)mcmd->tag, tgt_dev);
                             		mcmd->cmd_to_abort = cmd;
                             		sBUG_ON(mcmd->mcmd_tgt_dev != NULL);
                             		mcmd->mcmd_tgt_dev = tgt_dev;
                            @@ -5870,7 +5878,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
                             			mcmd->state = SCST_MCMD_STATE_EXEC;
                             		else if (rc < 0) {
                             			PRINT_ERROR("Corresponding device for LUN %lld not "
                            -				"found", (long long unsigned int)mcmd->lun);
                            +				"found", (unsigned long long int)mcmd->lun);
                             			scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_LUN_NOT_EXIST);
                             			res = scst_set_mcmd_next_state(mcmd);
                             		} else
                            @@ -6000,7 +6008,7 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
                             	TRACE_ENTRY();
                             
                             	TRACE(TRACE_MGMT, "Resetting LUN %lld (mcmd %p)",
                            -	      (long long unsigned int)tgt_dev->lun, mcmd);
                            +	      (unsigned long long int)tgt_dev->lun, mcmd);
                             
                             	mcmd->needs_unblocking = 1;
                             
                            @@ -6185,21 +6193,21 @@ static int scst_abort_task(struct scst_mgmt_cmd *mcmd)
                             
                             	TRACE_MGMT_DBG("Aborting task (cmd %p, sn %d, set %d, tag %llu, "
                             		"queue_type %x)", cmd, cmd->sn, cmd->sn_set,
                            -		(long long unsigned int)mcmd->tag, cmd->queue_type);
                            +		(unsigned long long int)mcmd->tag, cmd->queue_type);
                             
                             	if (mcmd->lun_set && (mcmd->lun != cmd->lun)) {
                             		PRINT_ERROR("ABORT TASK: LUN mismatch: mcmd LUN %llx, "
                             			"cmd LUN %llx, cmd tag %llu",
                            -			(long long unsigned int)mcmd->lun,
                            -			(long long unsigned int)cmd->lun,
                            -			(long long unsigned int)mcmd->tag);
                            +			(unsigned long long int)mcmd->lun,
                            +			(unsigned long long int)cmd->lun,
                            +			(unsigned long long int)mcmd->tag);
                             		scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_REJECTED);
                             	} else if (mcmd->cmd_sn_set &&
                             		   (scst_sn_before(mcmd->cmd_sn, cmd->tgt_sn) ||
                             		    (mcmd->cmd_sn == cmd->tgt_sn))) {
                             		PRINT_ERROR("ABORT TASK: SN mismatch: mcmd SN %x, "
                             			"cmd SN %x, cmd tag %llu", mcmd->cmd_sn,
                            -			cmd->tgt_sn, (long long unsigned int)mcmd->tag);
                            +			cmd->tgt_sn, (unsigned long long int)mcmd->tag);
                             		scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_REJECTED);
                             	} else {
                             		spin_lock_irq(&cmd->sess->sess_list_lock);
                            @@ -6758,9 +6766,9 @@ int scst_rx_mgmt_fn(struct scst_session *sess,
                             	TRACE_MGMT_DBG("sess=%p, tag_set %d, tag %lld, lun_set %d, "
                             		"lun=%lld, cmd_sn_set %d, cmd_sn %d, priv %p", sess,
                             		params->tag_set,
                            -		(long long unsigned int)params->tag,
                            +		(unsigned long long int)params->tag,
                             		params->lun_set,
                            -		(long long unsigned int)mcmd->lun,
                            +		(unsigned long long int)mcmd->lun,
                             		params->cmd_sn_set,
                             		params->cmd_sn,
                             		params->tgt_priv);
                            @@ -7451,7 +7459,7 @@ static struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess,
                             	/* ToDo: hash list */
                             
                             	TRACE_DBG("%s (sess=%p, tag=%llu)", "Searching in sess cmd list",
                            -		  sess, (long long unsigned int)tag);
                            +		  sess, (unsigned long long int)tag);
                             
                             	list_for_each_entry(cmd, &sess->sess_cmd_list,
                             			sess_cmd_list_entry) {
                            diff --git a/scst_local/in-tree/Makefile-3.17 b/scst_local/in-tree/Makefile-3.17
                            new file mode 100644
                            index 000000000..8cbbbff63
                            --- /dev/null
                            +++ b/scst_local/in-tree/Makefile-3.17
                            @@ -0,0 +1,2 @@
                            +obj-$(CONFIG_SCST_LOCAL) += scst_local.o
                            +
                            diff --git a/scst_local/in-tree/Makefile-3.18 b/scst_local/in-tree/Makefile-3.18
                            new file mode 100644
                            index 000000000..8cbbbff63
                            --- /dev/null
                            +++ b/scst_local/in-tree/Makefile-3.18
                            @@ -0,0 +1,2 @@
                            +obj-$(CONFIG_SCST_LOCAL) += scst_local.o
                            +
                            diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c
                            index d17fc9d47..8f3e182f8 100644
                            --- a/scst_local/scst_local.c
                            +++ b/scst_local/scst_local.c
                            @@ -229,7 +229,7 @@ static int scst_local_get_sas_transport_id(struct scst_local_sess *sess,
                             	tr_id[5]  = 0xEE;
                             	tr_id[6]  = 0xDE;
                             	tr_id[7]  = 0x40 | ((sess->number >> 4) & 0x0F);
                            -	tr_id[8]  = 0x0F | (sess->number & 0xF0);
                            +	tr_id[8]  = 0x0F | ((sess->number & 0x0F) << 4);
                             	tr_id[9]  = 0xAD;
                             	tr_id[10] = 0xE0;
                             	tr_id[11] = 0x50;
                            @@ -1068,22 +1068,8 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt,
                             	sgl_count = scsi_sg_count(SCpnt);
                             #endif
                             
                            -	dir = SCST_DATA_NONE;
                            -	switch (SCpnt->sc_data_direction) {
                            -	case DMA_TO_DEVICE:
                            -		dir = SCST_DATA_WRITE;
                            -		scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
                            -		scst_cmd_set_noio_mem_alloc(scst_cmd);
                            -		scst_cmd_set_tgt_sg(scst_cmd, sgl, sgl_count);
                            -		break;
                            -	case DMA_FROM_DEVICE:
                            -		dir = SCST_DATA_READ;
                            -		scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
                            -		scst_cmd_set_noio_mem_alloc(scst_cmd);
                            -		scst_cmd_set_tgt_sg(scst_cmd, sgl, sgl_count);
                            -		break;
                            -	case DMA_BIDIRECTIONAL:
                            -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24))
                            +	if (scsi_bidi_cmnd(SCpnt)) {
                            +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
                             		/* Some of these symbols are only defined after 2.6.24 */
                             		dir = SCST_DATA_BIDI;
                             		scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
                            @@ -1093,13 +1079,20 @@ static int scst_local_queuecommand_lck(struct scsi_cmnd *SCpnt,
                             		scst_cmd_set_tgt_sg(scst_cmd, scsi_in(SCpnt)->table.sgl,
                             			scsi_in(SCpnt)->table.nents);
                             		scst_cmd_set_tgt_out_sg(scst_cmd, sgl, sgl_count);
                            -		break;
                             #endif
                            -	case DMA_NONE:
                            -	default:
                            +	} else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
                            +		dir = SCST_DATA_WRITE;
                            +		scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
                            +		scst_cmd_set_noio_mem_alloc(scst_cmd);
                            +		scst_cmd_set_tgt_sg(scst_cmd, sgl, sgl_count);
                            +	} else if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
                            +		dir = SCST_DATA_READ;
                            +		scst_cmd_set_expected(scst_cmd, dir, scsi_bufflen(SCpnt));
                            +		scst_cmd_set_noio_mem_alloc(scst_cmd);
                            +		scst_cmd_set_tgt_sg(scst_cmd, sgl, sgl_count);
                            +	} else {
                             		dir = SCST_DATA_NONE;
                             		scst_cmd_set_expected(scst_cmd, dir, 0);
                            -		break;
                             	}
                             
                             	/* Save the correct thing below depending on version */
                            diff --git a/scstadmin/init.d/scst b/scstadmin/init.d/scst
                            index eb32774b5..99bc1f6ce 100755
                            --- a/scstadmin/init.d/scst
                            +++ b/scstadmin/init.d/scst
                            @@ -211,7 +211,7 @@ unload_scst() {
                             start_scst() {
                                     if [ -e /sys/module/scst -a -e /sys/module/scst/refcnt ]; then
                                         echo Already started
                            -            return 1
                            +            return 0
                                     fi
                             
                                     parse_scst_conf
                            diff --git a/scstadmin/scstadmin.procfs/scst-0.8.22/lib/SCST/SCST.pm b/scstadmin/scstadmin.procfs/scst-0.8.22/lib/SCST/SCST.pm
                            index e69905653..4476df3df 100644
                            --- a/scstadmin/scstadmin.procfs/scst-0.8.22/lib/SCST/SCST.pm
                            +++ b/scstadmin/scstadmin.procfs/scst-0.8.22/lib/SCST/SCST.pm
                            @@ -534,7 +534,7 @@ sub openDevice {
                             	$rc = !$self->handlerDeviceExists($handler, $device);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "openDevice(): An error occured while opening device '$device'. ".
                            +		$self->{'error'} = "openDevice(): An error occurred while opening device '$device'. ".
                             		  "See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -575,7 +575,7 @@ sub closeDevice {
                             	$rc = $self->handlerDeviceExists($handler, $device);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "closeDevice(): An error occured while closing device '$device'. ".
                            +		$self->{'error'} = "closeDevice(): An error occurred while closing device '$device'. ".
                             		  "See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -647,7 +647,7 @@ sub setT10DeviceId {
                             	my $devices = $self->handlerDevices($handler);
                             
                             	if ($$devices{$device}->{'T10_DEVICE_ID'} ne $t10_id) {
                            -                $self->{'error'} = "setT10DeviceId(): An error occured while setting T10 device ID to '$t10_id' ".
                            +                $self->{'error'} = "setT10DeviceId(): An error occurred while setting T10 device ID to '$t10_id' ".
                             		  "for device '$device'. See dmesg/kernel log for more information.";
                             		return 1;
                             	}
                            @@ -720,7 +720,7 @@ sub addUser {
                             	$rc = !$self->userExists($user, $group);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "addUser(): An error occured while adding user '$user' to group '$group'. ".
                            +		$self->{'error'} = "addUser(): An error occurred while adding user '$user' to group '$group'. ".
                             		  "See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -752,7 +752,7 @@ sub removeUser {
                             	$rc = $self->userExists($user, $group);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "removeUser(): An error occured while removing user '$user' ".
                            +		$self->{'error'} = "removeUser(): An error occurred while removing user '$user' ".
                             		  "from group '$group'. See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -795,7 +795,7 @@ sub moveUser {
                             	$rc = !$self->userExists($user, $toGroup);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "addUser(): An error occured while moving user '$user' from group '$fromGroup' ".
                            +		$self->{'error'} = "addUser(): An error occurred while moving user '$user' from group '$fromGroup' ".
                             		  "to group '$toGroup'. See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -818,7 +818,7 @@ sub clearUsers {
                             	return 0 if ($self->{'debug'});
                             
                             	if ($rc) {
                            -		$self->{'error'} = "clearUsers(): An error occured while clearing users from ".
                            +		$self->{'error'} = "clearUsers(): An error occurred while clearing users from ".
                             		  "group '$group'. See dmesg/kernel log for more information.";
                             		return $rc;
                             	}
                            @@ -956,7 +956,7 @@ sub assignDeviceToGroup {
                             	$rc = !$self->groupDeviceExists($device, $group, $lun);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "assignDeviceToGroup(): An error occured while assigning device '$device' ".
                            +		$self->{'error'} = "assignDeviceToGroup(): An error occurred while assigning device '$device' ".
                             		  "to group '$group'. See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -1003,7 +1003,7 @@ sub replaceDeviceInGroup {
                             	$rc = !$self->groupDeviceExists($newDevice, $group, $lun);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "replaceDeviceInGroup(): An error occured while replacing lun '$lun' with ".
                            +		$self->{'error'} = "replaceDeviceInGroup(): An error occurred while replacing lun '$lun' with ".
                             		  " device '$newDevice' in group '$group'. See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -1044,7 +1044,7 @@ sub assignDeviceToHandler {
                             	$rc = !$self->handlerDeviceExists($handler, $device);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "assignDeviceToHandler(): An error occured while assigning device '$device' ".
                            +		$self->{'error'} = "assignDeviceToHandler(): An error occurred while assigning device '$device' ".
                             		  "to handler '$handler_name' ($handler). See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -1076,7 +1076,7 @@ sub removeDeviceFromGroup {
                             	$rc = $self->groupDeviceExists($device, $group);
                             
                             	if ($rc) {
                            -		$self->{'error'} = "removeDeviceFromGroup(): An error occured while removing device '$device' ".
                            +		$self->{'error'} = "removeDeviceFromGroup(): An error occurred while removing device '$device' ".
                             		  "from group '$group'. See dmesg/kernel log for more information.";
                             	}
                             
                            @@ -1096,7 +1096,7 @@ sub clearGroupDevices {
                             	return 0 if ($self->{'debug'});
                             
                             	if ($rc) {
                            -		$self->{'error'} = "clearGroupDevices(): An error occured while clearing devices from ".
                            +		$self->{'error'} = "clearGroupDevices(): An error occurred while clearing devices from ".
                             		  "group '$group'. See dmesg/kernel log for more information.";
                             		return $rc;
                             	}
                            @@ -1544,8 +1544,8 @@ Returns: (int) $success
                             
                             =item SCST::SCST->errorString();
                             
                            -Contains a description of the last error occured or undef if no error
                            -has occured or if this method has already been called once since the
                            +Contains a description of the last error occurred or undef if no error
                            +has occurred or if this method has already been called once since the
                             last error.
                             
                             Arguments: (void)
                            diff --git a/scstadmin/scstadmin.procfs/scstadmin b/scstadmin/scstadmin.procfs/scstadmin
                            index 9cb546a3b..f5c4181f0 100755
                            --- a/scstadmin/scstadmin.procfs/scstadmin
                            +++ b/scstadmin/scstadmin.procfs/scstadmin
                            @@ -1074,7 +1074,7 @@ sub applyConfiguration {
                             		}
                             
                             		if (!defined($GROUPS{$group})) {
                            -			print "\t-> WARNING: Unable to assign to non-existant group '$group'.\n";
                            +			print "\t-> WARNING: Unable to assign to non-existent group '$group'.\n";
                             			$errs += 1;
                             			next;
                             		}
                            @@ -1265,7 +1265,7 @@ sub addDevice {
                             	my $htype = $SCST->handlerType($_handler);
                             
                             	if (!$htype) {
                            -		print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
                            +		print "WARNING: Internal error occurred: ".$SCST->errorString()."\n";
                             		return $TRUE;
                             	}
                             
                            @@ -1327,7 +1327,7 @@ sub removeDevice {
                             	my $htype = $SCST->handlerType($_handler);
                             
                             	if (!$htype) {
                            -		print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
                            +		print "WARNING: Internal error occurred: ".$SCST->errorString()."\n";
                             		return $TRUE;
                             	}
                             
                            @@ -1586,7 +1586,7 @@ sub assignDevice {
                             	}
                             
                             	if (!defined($$DEVICES{$device})) {
                            -		print "WARNING: Unable to assign non-existant device '$device' to group '$group'.\n";
                            +		print "WARNING: Unable to assign non-existent device '$device' to group '$group'.\n";
                             		return $TRUE;
                             	}
                             
                            @@ -1632,7 +1632,7 @@ sub replaceDevice {
                             	}
                             			
                             	if (!defined($$DEVICES{$newDevice})) {
                            -		print "WARNING: Unable to assign non-existant device '$newDevice' to group '$group'.\n";
                            +		print "WARNING: Unable to assign non-existent device '$newDevice' to group '$group'.\n";
                             		return $TRUE;
                             	}
                             
                            diff --git a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm
                            index 11370124c..0b2bb7065 100644
                            --- a/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm
                            +++ b/scstadmin/scstadmin.sysfs/scst-0.9.10/lib/SCST/SCST.pm
                            @@ -176,7 +176,7 @@ SCST_C_TGRP_TGT_SETATTR_FAIL => 172,
                             };
                             
                             my %VERBOSE_ERROR = (
                            -(SCST_C_FATAL_ERROR)          => 'A fatal error occured. See "dmesg" for more information.',
                            +(SCST_C_FATAL_ERROR)          => 'A fatal error occurred. See "dmesg" for more information.',
                             (SCST_C_BAD_ATTRIBUTES)       => 'Bad attributes given for SCST.',
                             (SCST_C_ATTRIBUTE_STATIC)     => 'SCST attribute specified is static',
                             (SCST_C_SETATTR_FAIL)         => 'Failed to set a SCST attribute. See "dmesg" for more information.',
                            diff --git a/srpt/Makefile b/srpt/Makefile
                            index 555525018..62e680454 100644
                            --- a/srpt/Makefile
                            +++ b/srpt/Makefile
                            @@ -43,7 +43,7 @@ SRC_FILES=$(wildcard */*.[ch])
                             
                             # The file Modules.symvers has been renamed in the 2.6.18 kernel to
                             # Module.symvers. Find out which name to use by looking in $(KDIR).
                            -MODULE_SYMVERS:=$(shell if [ -e $(KDIR)/Module.symvers ]; then \
                            +MODULE_SYMVERS:=$(shell if [ -e "$(KDIR)/Module.symvers" ]; then \
                             		       echo Module.symvers; else echo Modules.symvers; fi)
                             
                             # Name of the OFED kernel RPM.
                            @@ -52,7 +52,7 @@ OFED_KERNEL_IB_RPM:=$(shell for r in mlnx-ofa_kernel compat-rdma kernel-ib; do r
                             # Name of the OFED kernel development RPM.
                             OFED_KERNEL_IB_DEVEL_RPM:=$(shell for r in mlnx-ofa_kernel-devel compat-rdma-devel kernel-ib-devel; do rpm -q $$r 2>/dev/null | grep -q "^$$r" && echo $$r && break; done)
                             
                            -OFED_FLAVOR=$(shell /usr/bin/ofed_info 2>/dev/null | head -n1 | sed -n 's/^MLNX_OFED.*/MOFED/p;s/^OFED-.*/OFED/p')
                            +OFED_FLAVOR=$(shell /usr/bin/ofed_info 2>/dev/null | head -n1 | sed -n 's/^\(MLNX_OFED\|OFED-internal\).*/MOFED/p;s/^OFED-.*/OFED/p')
                             
                             ifneq ($(OFED_KERNEL_IB_RPM),)
                             ifeq ($(OFED_KERNEL_IB_RPM),compat-rdma)
                            diff --git a/srpt/README b/srpt/README
                            index 0d805bdb3..b051f835f 100644
                            --- a/srpt/README
                            +++ b/srpt/README
                            @@ -56,10 +56,10 @@ The ib_srpt kernel module supports the following parameters:
                                  GUID (e.g. 0002:c903:0005:f34a).
                               3. Access control configuration per HCA port and referring to a HCA via its
                                  port GID (e.g. fe80:0000:0000:0000:0002:c903:0005:f34b).
                            -  Mode (1) is choosen if both one_target_per_port and
                            -  use_node_guid_in_target_name are false. Mode (2) is choosen if
                            +  Mode (1) is chosen if both one_target_per_port and
                            +  use_node_guid_in_target_name are false. Mode (2) is chosen if
                               one_target_per_port is false and use_node_guid_in_target_name is true. Mode
                            -  (3) is choosen if one_target_per_port is true. This last mode is the
                            +  (3) is chosen if one_target_per_port is true. This last mode is the
                               default mode.
                             * rdma_cm_port (number)
                               A 16-bit number that specifies the port number to be registered via the
                            @@ -362,7 +362,7 @@ Performance Notes - Target Side
                               improves performance compared to debug mode.
                             
                             * When using high-latency storage devices (hard disks), the default value
                            -  choosen by SCST for DEVICE.threads_num should be fine. When using
                            +  chosen by SCST for DEVICE.threads_num should be fine. When using
                               low-latency storage devices though (SSDs), DEVICE.threads_num should be set
                               to 1 or 2 in /etc/scst.conf in order to reach optimal performance for small
                               block sizes (e.g. 4 KB).
                            diff --git a/srpt/patches/kernel-3.17-pre-cflags.patch b/srpt/patches/kernel-3.17-pre-cflags.patch
                            new file mode 100644
                            index 000000000..3964ee179
                            --- /dev/null
                            +++ b/srpt/patches/kernel-3.17-pre-cflags.patch
                            @@ -0,0 +1,12 @@
                            +diff --git a/Makefile b/Makefile
                            +index 540f7b2..078307f 100644
                            +--- a/Makefile
                            ++++ b/Makefile
                            +@@ -361,6 +361,7 @@ USERINCLUDE    := \
                            + # Use LINUXINCLUDE when you must reference the include/ directory.
                            + # Needed to be compatible with the O= option
                            + LINUXINCLUDE    := \
                            ++		$(PRE_CFLAGS) \
                            + 		-I$(srctree)/arch/$(hdr-arch)/include \
                            + 		-Iarch/$(hdr-arch)/include/generated \
                            + 		$(if $(KBUILD_SRC), -I$(srctree)/include) \
                            diff --git a/srpt/patches/kernel-3.18-pre-cflags.patch b/srpt/patches/kernel-3.18-pre-cflags.patch
                            new file mode 100644
                            index 000000000..a6adaf47b
                            --- /dev/null
                            +++ b/srpt/patches/kernel-3.18-pre-cflags.patch
                            @@ -0,0 +1,12 @@
                            +diff --git a/Makefile b/Makefile
                            +index fd80c6e..09ca4ea 100644
                            +--- a/Makefile
                            ++++ b/Makefile
                            +@@ -390,6 +390,7 @@ USERINCLUDE    := \
                            + # Use LINUXINCLUDE when you must reference the include/ directory.
                            + # Needed to be compatible with the O= option
                            + LINUXINCLUDE    := \
                            ++		$(PRE_CFLAGS) \
                            + 		-I$(srctree)/arch/$(hdr-arch)/include \
                            + 		-Iarch/$(hdr-arch)/include/generated \
                            + 		$(if $(KBUILD_SRC), -I$(srctree)/include) \
                            diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c
                            index 437aa5e83..63a861e36 100644
                            --- a/srpt/src/ib_srpt.c
                            +++ b/srpt/src/ib_srpt.c
                            @@ -50,6 +50,7 @@
                             #endif
                             #endif
                             #include "ib_srpt.h"
                            +#include "srp-ext.h"
                             #define LOG_PREFIX "ib_srpt" /* Prefix for SCST tracing macros. */
                             #if defined(INSIDE_KERNEL_TREE)
                             #include 
                            @@ -118,6 +119,16 @@ module_param(srp_max_rsp_size, int, S_IRUGO | S_IWUSR);
                             MODULE_PARM_DESC(srp_max_rsp_size,
                             		 "Maximum size of SRP response messages in bytes.");
                             
                            +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \
                            +    || defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
                            +static int use_srq = true;
                            +#else
                            +static bool use_srq = true;
                            +#endif
                            +module_param(use_srq, bool, S_IRUGO | S_IWUSR);
                            +MODULE_PARM_DESC(use_srq,
                            +		 "Whether or not to use SRQ");
                            +
                             static int srpt_srq_size = DEFAULT_SRPT_SRQ_SIZE;
                             module_param(srpt_srq_size, int, S_IRUGO | S_IWUSR);
                             MODULE_PARM_DESC(srpt_srq_size,
                            @@ -459,6 +470,7 @@ static void srpt_get_ioc(struct srpt_device *sdev, u32 slot,
                             			 struct ib_dm_mad *mad)
                             {
                             	struct ib_dm_ioc_profile *iocp;
                            +	int send_queue_depth;
                             
                             	iocp = (struct ib_dm_ioc_profile *)mad->data;
                             
                            @@ -472,6 +484,11 @@ static void srpt_get_ioc(struct srpt_device *sdev, u32 slot,
                             		return;
                             	}
                             
                            +	if (sdev->use_srq)
                            +		send_queue_depth = sdev->srq_size;
                            +	else
                            +		send_queue_depth = min(SRPT_RQ_SIZE, sdev->dev_attr.max_qp_wr);
                            +
                             	memset(iocp, 0, sizeof(*iocp));
                             	strcpy(iocp->id_string, SRPT_ID_STRING);
                             	iocp->guid = cpu_to_be64(srpt_service_guid);
                            @@ -484,7 +501,8 @@ static void srpt_get_ioc(struct srpt_device *sdev, u32 slot,
                             	iocp->io_subclass = cpu_to_be16(SRP_IO_SUBCLASS);
                             	iocp->protocol = cpu_to_be16(SRP_PROTOCOL);
                             	iocp->protocol_version = cpu_to_be16(SRP_PROTOCOL_VERSION);
                            -	iocp->send_queue_depth = cpu_to_be16(sdev->srq_size);
                            +	iocp->send_queue_depth = cpu_to_be16(send_queue_depth);
                            +
                             	iocp->rdma_read_depth = 4;
                             	iocp->send_size = cpu_to_be32(srp_max_req_size);
                             	iocp->rdma_size = cpu_to_be32(min(max(srp_max_rdma_size, 256U),
                            @@ -772,21 +790,27 @@ static void srpt_unregister_mad_agent(struct srpt_device *sdev)
                              */
                             static struct srpt_ioctx *srpt_alloc_ioctx(struct srpt_device *sdev,
                             					   int ioctx_size, int dma_size,
                            +					   int alignment_offset,
                             					   enum dma_data_direction dir)
                             {
                             	struct srpt_ioctx *ioctx;
                             
                            -	ioctx = kmalloc(ioctx_size, GFP_KERNEL);
                            +	ioctx = kzalloc(ioctx_size, GFP_KERNEL);
                             	if (!ioctx)
                             		goto err;
                             
                            -	ioctx->buf = kmalloc(dma_size, GFP_KERNEL);
                            +	ioctx->buf = kmalloc(dma_size + alignment_offset, GFP_KERNEL);
                             	if (!ioctx->buf)
                             		goto err_free_ioctx;
                             
                            -	ioctx->dma = ib_dma_map_single(sdev->device, ioctx->buf, dma_size, dir);
                            +	/* Complain if it is not safe to use zero-copy */
                            +	WARN_ON_ONCE(alignment_offset && ((uintptr_t)ioctx->buf & 511));
                            +
                            +	ioctx->dma = ib_dma_map_single(sdev->device, ioctx->buf,
                            +				       dma_size + alignment_offset, dir);
                             	if (ib_dma_mapping_error(sdev->device, ioctx->dma))
                             		goto err_free_buf;
                            +	ioctx->offset = alignment_offset;
                             
                             	return ioctx;
                             
                            @@ -822,7 +846,8 @@ static void srpt_free_ioctx(struct srpt_device *sdev, struct srpt_ioctx *ioctx,
                              */
                             static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev,
                             				int ring_size, int ioctx_size,
                            -				int dma_size, enum dma_data_direction dir)
                            +				int dma_size, int alignment_offset,
                            +				enum dma_data_direction dir)
                             {
                             	struct srpt_ioctx **ring;
                             	int i;
                            @@ -836,7 +861,8 @@ static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev,
                             	if (!ring)
                             		goto out;
                             	for (i = 0; i < ring_size; ++i) {
                            -		ring[i] = srpt_alloc_ioctx(sdev, ioctx_size, dma_size, dir);
                            +		ring[i] = srpt_alloc_ioctx(sdev, ioctx_size, dma_size,
                            +					   alignment_offset, dir);
                             		if (!ring[i])
                             			goto err;
                             		ring[i]->index = i;
                            @@ -845,7 +871,7 @@ static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev,
                             
                             err:
                             	while (--i >= 0)
                            -		srpt_free_ioctx(sdev, ring[i], dma_size, dir);
                            +		srpt_free_ioctx(sdev, ring[i], dma_size + ring[i]->offset, dir);
                             	kfree(ring);
                             	ring = NULL;
                             out:
                            @@ -862,8 +888,12 @@ static void srpt_free_ioctx_ring(struct srpt_ioctx **ioctx_ring,
                             {
                             	int i;
                             
                            +	if (!ioctx_ring)
                            +		return;
                            +
                             	for (i = 0; i < ring_size; ++i)
                            -		srpt_free_ioctx(sdev, ioctx_ring[i], dma_size, dir);
                            +		srpt_free_ioctx(sdev, ioctx_ring[i],
                            +				dma_size + ioctx_ring[i]->offset, dir);
                             	kfree(ioctx_ring);
                             }
                             
                            @@ -913,16 +943,17 @@ static bool srpt_test_and_set_cmd_state(struct srpt_send_ioctx *ioctx,
                             /**
                              * srpt_post_recv() - Post an IB receive request.
                              */
                            -static int srpt_post_recv(struct srpt_device *sdev,
                            +static int srpt_post_recv(struct srpt_device *sdev, struct srpt_rdma_ch *ch,
                             			  struct srpt_recv_ioctx *ioctx)
                             {
                             	struct ib_sge list;
                             	struct ib_recv_wr wr, *bad_wr;
                            +	int status;
                             
                             	BUG_ON(!sdev);
                             	wr.wr_id = encode_wr_id(SRPT_RECV, ioctx->ioctx.index);
                             
                            -	list.addr = ioctx->ioctx.dma;
                            +	list.addr = ioctx->ioctx.dma + ioctx->ioctx.offset;
                             	list.length = srp_max_req_size;
                             	list.lkey = sdev->mr->lkey;
                             
                            @@ -930,10 +961,14 @@ static int srpt_post_recv(struct srpt_device *sdev,
                             	wr.sg_list = &list;
                             	wr.num_sge = 1;
                             
                            -	return ib_post_srq_recv(sdev->srq, &wr, &bad_wr);
                            +	if (sdev->use_srq)
                            +		status = ib_post_srq_recv(sdev->srq, &wr, &bad_wr);
                            +	else
                            +		status = ib_post_recv(ch->qp, &wr, &bad_wr);
                            +	return status;
                             }
                             
                            -static int srpt_adjust_srq_wr_avail(struct srpt_rdma_ch *ch, int delta)
                            +static int srpt_adjust_sq_wr_avail(struct srpt_rdma_ch *ch, int delta)
                             {
                             	return atomic_add_return(delta, &ch->sq_wr_avail);
                             }
                            @@ -952,8 +987,9 @@ static int srpt_post_send(struct srpt_rdma_ch *ch,
                             	int ret;
                             
                             	ret = -ENOMEM;
                            -	if (srpt_adjust_srq_wr_avail(ch, -1) < 0) {
                            -		PRINT_WARNING("IB send queue full (needed 1)");
                            +	if (srpt_adjust_sq_wr_avail(ch, -1) < 0) {
                            +		PRINT_WARNING("ch %s-%d send queue full (needed 1)",
                            +			      ch->sess_name, ch->qp->qp_num);
                             		goto out;
                             	}
                             
                            @@ -975,7 +1011,7 @@ static int srpt_post_send(struct srpt_rdma_ch *ch,
                             
                             out:
                             	if (ret < 0)
                            -		srpt_adjust_srq_wr_avail(ch, 1);
                            +		srpt_adjust_sq_wr_avail(ch, 1);
                             	return ret;
                             }
                             
                            @@ -1012,7 +1048,8 @@ static int srpt_zerolength_write(struct srpt_rdma_ch *ch)
                              * Returns -EINVAL when the SRP_CMD request contains inconsistent descriptors;
                              * -ENOMEM when memory allocation fails and zero upon success.
                              */
                            -static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx,
                            +static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx,
                            +			     struct srpt_send_ioctx *ioctx,
                             			     struct srp_cmd *srp_cmd,
                             			     scst_data_direction *dir, u64 *data_len)
                             {
                            @@ -1064,7 +1101,38 @@ static int srpt_get_desc_tbl(struct srpt_send_ioctx *ioctx,
                             	 * is four times the value specified in bits 3..7. Hence the "& ~3".
                             	 */
                             	add_cdb_offset = srp_cmd->add_cdb_len & ~3;
                            -	if (fmt == SRP_DATA_DESC_DIRECT) {
                            +	if (fmt == SRP_DATA_DESC_IMM) {
                            +		struct srp_imm_buf *imm_buf = (void *)(srp_cmd->add_data
                            +						       + add_cdb_offset);
                            +		void *data;
                            +		uint32_t header_size;
                            +		uint64_t req_size;
                            +
                            +		header_size = be32_to_cpu(imm_buf->offset);
                            +		*data_len = be32_to_cpu(imm_buf->len);
                            +		req_size = header_size + *data_len;
                            +		data = (void *)srp_cmd + header_size;
                            +		if (req_size > srp_max_req_size) {
                            +			PRINT_ERROR("Immediate data (length %d + %lld) exceeds"
                            +				    " request size %d", header_size, *data_len,
                            +				    srp_max_req_size);
                            +			ret = -EINVAL;
                            +			goto out;
                            +		}
                            +		if (WARN_ONCE(recv_ioctx->byte_len < req_size,
                            +			      "received too few data - %d < %lld\n",
                            +			      recv_ioctx->byte_len, req_size)) {
                            +			print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16,
                            +				       1, srp_cmd, recv_ioctx->byte_len, 1);
                            +			ret = -EIO;
                            +		}
                            +		ioctx->imm_data = data;
                            +		ioctx->recv_ioctx = recv_ioctx;
                            +		if (((uintptr_t)data & 511) == 0) {
                            +			sg_init_one(&ioctx->imm_sg, ioctx->imm_data, *data_len);
                            +			scst_cmd_set_tgt_sg(&ioctx->scmnd, &ioctx->imm_sg, 1);
                            +		}
                            +	} else if (fmt == SRP_DATA_DESC_DIRECT) {
                             		ioctx->n_rbuf = 1;
                             		ioctx->rbufs = &ioctx->single_rbuf;
                             
                            @@ -1260,8 +1328,10 @@ static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch)
                             	BUG_ON(ioctx->ch != ch);
                             	spin_lock_init(&ioctx->spinlock);
                             	ioctx->state = SRPT_STATE_NEW;
                            +	EXTRACHECKS_WARN_ON(ioctx->recv_ioctx);
                             	ioctx->n_rbuf = 0;
                             	ioctx->rbufs = NULL;
                            +	ioctx->imm_data = NULL;
                             	ioctx->n_rdma = 0;
                             	ioctx->n_rdma_ius = 0;
                             	ioctx->rdma_ius = NULL;
                            @@ -1276,12 +1346,15 @@ static struct srpt_send_ioctx *srpt_get_send_ioctx(struct srpt_rdma_ch *ch)
                              */
                             static void srpt_put_send_ioctx(struct srpt_send_ioctx *ioctx)
                             {
                            -	struct srpt_rdma_ch *ch;
                            +	struct srpt_rdma_ch *ch = ioctx->ch;
                            +	struct srpt_recv_ioctx *recv_ioctx = ioctx->recv_ioctx;
                             	unsigned long flags;
                             
                            -	BUG_ON(!ioctx);
                            -	ch = ioctx->ch;
                            -	BUG_ON(!ch);
                            +	if (recv_ioctx) {
                            +		EXTRACHECKS_WARN_ON(!list_empty(&recv_ioctx->wait_list));
                            +		ioctx->recv_ioctx = NULL;
                            +		srpt_post_recv(ch->sport->sdev, ch, recv_ioctx);
                            +	}
                             
                             	/*
                             	 * If the WARN_ON() below gets triggered this means that
                            @@ -1411,7 +1484,7 @@ static void srpt_handle_send_err_comp(struct srpt_rdma_ch *ch, u64 wr_id,
                             	struct srpt_send_ioctx *ioctx = ch->ioctx_ring[index];
                             	enum srpt_command_state state = ioctx->state;
                             
                            -	srpt_adjust_srq_wr_avail(ch, 1);
                            +	srpt_adjust_sq_wr_avail(ch, 1);
                             
                             	switch (state) {
                             	case SRPT_STATE_NEED_DATA:
                            @@ -1442,7 +1515,7 @@ static void srpt_handle_send_comp(struct srpt_rdma_ch *ch,
                             				  struct srpt_send_ioctx *ioctx,
                             				  enum scst_exec_context context)
                             {
                            -	srpt_adjust_srq_wr_avail(ch, 1);
                            +	srpt_adjust_sq_wr_avail(ch, 1);
                             
                             	switch (srpt_set_cmd_state(ioctx, SRPT_STATE_DONE)) {
                             	case SRPT_STATE_CMD_RSP_SENT:
                            @@ -1472,7 +1545,7 @@ static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch,
                             	struct scst_cmd *scmnd = &ioctx->scmnd;
                             
                             	EXTRACHECKS_WARN_ON(ioctx->n_rdma <= 0);
                            -	srpt_adjust_srq_wr_avail(ch, ioctx->n_rdma);
                            +	srpt_adjust_sq_wr_avail(ch, ioctx->n_rdma);
                             
                             	if (opcode == SRPT_RDMA_READ_LAST && scmnd) {
                             		if (srpt_test_and_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA,
                            @@ -1507,7 +1580,7 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch,
                             				    ioctx->ioctx.index);
                             			break;
                             		}
                            -		srpt_adjust_srq_wr_avail(ch, ioctx->n_rdma);
                            +		srpt_adjust_sq_wr_avail(ch, ioctx->n_rdma);
                             		if (state == SRPT_STATE_NEED_DATA)
                             			srpt_abort_cmd(ioctx, context);
                             		else
                            @@ -1661,7 +1734,7 @@ static int srpt_handle_cmd(struct srpt_rdma_ch *ch,
                             
                             	BUG_ON(!send_ioctx);
                             
                            -	srp_cmd = recv_ioctx->ioctx.buf;
                            +	srp_cmd = recv_ioctx->ioctx.buf + recv_ioctx->ioctx.offset;
                             
                             	scmnd = &send_ioctx->scmnd;
                             	ret = scst_rx_cmd_prealloced(scmnd, ch->scst_sess, (u8 *) &srp_cmd->lun,
                            @@ -1673,7 +1746,8 @@ static int srpt_handle_cmd(struct srpt_rdma_ch *ch,
                             		goto err;
                             	}
                             
                            -	ret = srpt_get_desc_tbl(send_ioctx, srp_cmd, &dir, &data_len);
                            +	ret = srpt_get_desc_tbl(recv_ioctx, send_ioctx, srp_cmd, &dir,
                            +				&data_len);
                             	if (ret) {
                             		PRINT_ERROR("0x%llx: parsing SRP descriptor table failed.",
                             			    srp_cmd->tag);
                            @@ -1739,7 +1813,7 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch,
                             
                             	srpt_set_cmd_state(send_ioctx, SRPT_STATE_MGMT);
                             
                            -	srp_tsk = recv_ioctx->ioctx.buf;
                            +	srp_tsk = recv_ioctx->ioctx.buf + recv_ioctx->ioctx.offset;
                             
                             	TRACE_DBG("recv_tsk_mgmt= %d for task_tag= %lld"
                             		  " using tag= %lld ch= %p sess= %p",
                            @@ -1829,10 +1903,11 @@ srpt_handle_new_iu(struct srpt_rdma_ch *ch,
                             		goto push;
                             
                             	ib_dma_sync_single_for_cpu(ch->sport->sdev->device,
                            -				   recv_ioctx->ioctx.dma, srp_max_req_size,
                            +				   recv_ioctx->ioctx.dma,
                            +				   recv_ioctx->ioctx.offset + srp_max_req_size,
                             				   DMA_FROM_DEVICE);
                             
                            -	srp_cmd = recv_ioctx->ioctx.buf;
                            +	srp_cmd = recv_ioctx->ioctx.buf + recv_ioctx->ioctx.offset;
                             	opcode = srp_cmd->opcode;
                             	if (opcode == SRP_CMD || opcode == SRP_TSK_MGMT) {
                             		send_ioctx = srpt_get_send_ioctx(ch);
                            @@ -1867,7 +1942,8 @@ srpt_handle_new_iu(struct srpt_rdma_ch *ch,
                             		break;
                             	}
                             
                            -	srpt_post_recv(ch->sport->sdev, recv_ioctx);
                            +	if (!send_ioctx || !send_ioctx->recv_ioctx)
                            +		srpt_post_recv(ch->sport->sdev, ch, recv_ioctx);
                             
                             out:
                             	return send_ioctx;
                            @@ -1883,7 +1959,6 @@ static void srpt_process_rcv_completion(struct ib_cq *cq,
                             					struct srpt_rdma_ch *ch,
                             					struct ib_wc *wc)
                             {
                            -	struct srpt_device *sdev = ch->sport->sdev;
                             	struct srpt_recv_ioctx *ioctx;
                             	u32 index;
                             
                            @@ -1894,7 +1969,11 @@ static void srpt_process_rcv_completion(struct ib_cq *cq,
                             		req_lim = srpt_adjust_req_lim(ch, -1, 0);
                             		if (unlikely(req_lim < 0))
                             			PRINT_ERROR("req_lim = %d < 0", req_lim);
                            -		ioctx = sdev->ioctx_ring[index];
                            +		if (ch->sport->sdev->use_srq)
                            +			ioctx = ch->sport->sdev->ioctx_ring[index];
                            +		else
                            +			ioctx = ch->ioctx_recv_ring[index];
                            +		ioctx->byte_len = wc->byte_len;
                             		srpt_handle_new_iu(ch, ioctx, srpt_new_iu_context);
                             	} else {
                             		PRINT_INFO("receiving failed for idx %u with status %d",
                            @@ -2057,6 +2136,10 @@ static void srpt_unreg_sess(struct scst_session *scst_sess)
                             			     sdev, ch->rq_size,
                             			     ch->max_rsp_size, DMA_TO_DEVICE);
                             
                            +	srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_recv_ring,
                            +			     sdev, ch->rq_size,
                            +			     srp_max_req_size, DMA_FROM_DEVICE);
                            +
                             	/* Wait until CM callbacks have finished and prevent new callbacks. */
                             	if (ch->using_rdma_cm)
                             		rdma_destroy_id(ch->rdma_cm.cm_id);
                            @@ -2122,7 +2205,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
                             {
                             	struct ib_qp_init_attr *qp_init;
                             	struct srpt_device *sdev = ch->sport->sdev;
                            -	int ret;
                            +	int i, ret;
                             
                             	EXTRACHECKS_WARN_ON(ch->rq_size < 1);
                             
                            @@ -2151,12 +2234,25 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
                             		= (void(*)(struct ib_event *, void*))srpt_qp_event;
                             	qp_init->send_cq = ch->cq;
                             	qp_init->recv_cq = ch->cq;
                            -	qp_init->srq = sdev->srq;
                             	qp_init->sq_sig_type = IB_SIGNAL_REQ_WR;
                             	qp_init->qp_type = IB_QPT_RC;
                             	qp_init->cap.max_send_wr = srpt_sq_size;
                            -	ch->max_sge = max_t(int, 1, sdev->dev_attr.max_sge - max_sge_delta);
                            +	/*
                            +	 * For max_sge values > 2 * max_sge_delta, subtract max_sge_delta. For
                            +	 * max_sge values < max_sge_delta, use max_sge. For intermediate
                            +	 * max_sge values, use max_sge_delta.
                            +	 */
                            +	ch->max_sge = sdev->dev_attr.max_sge -
                            +		min(max_sge_delta,
                            +		    max_t(unsigned, 0, sdev->dev_attr.max_sge - max_sge_delta));
                             	qp_init->cap.max_send_sge = ch->max_sge;
                            +	qp_init->cap.max_recv_sge = ch->max_sge;
                            +	if (sdev->use_srq) {
                            +		qp_init->srq = sdev->srq;
                            +	} else {
                            +		qp_init->cap.max_recv_wr = ch->rq_size;
                            +		qp_init->cap.max_recv_sge = ch->max_sge;
                            +	}
                             
                             	if (ch->using_rdma_cm) {
                             		ret = rdma_create_qp(ch->rdma_cm.cm_id, sdev->pd, qp_init);
                            @@ -2182,6 +2278,10 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
                             
                             	TRACE_DBG("qp_num = %#x", ch->qp->qp_num);
                             
                            +	if (!sdev->use_srq)
                            +		for (i = 0; i < ch->rq_size; i++)
                            +			srpt_post_recv(sdev, ch, ch->ioctx_recv_ring[i]);
                            +
                             	atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr);
                             
                             	TRACE_DBG("%s: max_cqe= %d max_sge= %d sq_size = %d ch= %p", __func__,
                            @@ -2465,7 +2565,8 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
                             		   " %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x,"
                             		   " t_port_id %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x and"
                             		   " it_iu_len %d on port %d"
                            -		   " (guid=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x)",
                            +		   " (guid=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x);"
                            +		   " pkey %#04x",
                             	    be16_to_cpu(*(__be16 *)&req->initiator_port_id[0]),
                             	    be16_to_cpu(*(__be16 *)&req->initiator_port_id[2]),
                             	    be16_to_cpu(*(__be16 *)&req->initiator_port_id[4]),
                            @@ -2491,7 +2592,8 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
                             	    be16_to_cpu(raw_port_gid[4]),
                             	    be16_to_cpu(raw_port_gid[5]),
                             	    be16_to_cpu(raw_port_gid[6]),
                            -	    be16_to_cpu(raw_port_gid[7]));
                            +	    be16_to_cpu(raw_port_gid[7]),
                            +	    be16_to_cpu(pkey));
                             
                             	nexus = srpt_get_nexus(srpt_tgt, req->initiator_port_id,
                             			       req->target_port_id);
                            @@ -2568,8 +2670,10 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
                             	ch->ioctx_ring = (struct srpt_send_ioctx **)
                             		srpt_alloc_ioctx_ring(ch->sport->sdev, ch->rq_size,
                             				      sizeof(*ch->ioctx_ring[0]),
                            -				      ch->max_rsp_size, DMA_TO_DEVICE);
                            +				      ch->max_rsp_size, 0, DMA_TO_DEVICE);
                             	if (!ch->ioctx_ring) {
                            +		PRINT_ERROR("rejected SRP_LOGIN_REQ because creating"
                            +			    " a new QP SQ ring failed.");
                             		rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES);
                             		goto free_ch;
                             	}
                            @@ -2579,6 +2683,23 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
                             		ch->ioctx_ring[i]->ch = ch;
                             		list_add_tail(&ch->ioctx_ring[i]->free_list, &ch->free_list);
                             	}
                            +	if (!sdev->use_srq) {
                            +		ch->ioctx_recv_ring = (struct srpt_recv_ioctx **)
                            +			srpt_alloc_ioctx_ring(ch->sport->sdev, ch->rq_size,
                            +					      sizeof(*ch->ioctx_recv_ring[0]),
                            +					      srp_max_req_size,
                            +					      DATA_ALIGNMENT_OFFSET,
                            +					      DMA_FROM_DEVICE);
                            +		if (!ch->ioctx_recv_ring) {
                            +			PRINT_ERROR("rejected SRP_LOGIN_REQ because creating"
                            +				    " a new QP RQ ring failed.");
                            +			rej->reason =
                            +			    cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES);
                            +			goto free_ring;
                            +		}
                            +		for (i = 0; i < ch->rq_size; i++)
                            +			INIT_LIST_HEAD(&ch->ioctx_recv_ring[i]->wait_list);
                            +	}
                             
                             	ch->comp_vector = srpt_next_comp_vector(srpt_tgt);
                             
                            @@ -2587,7 +2708,7 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
                             		rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES);
                             		PRINT_ERROR("rejected SRP_LOGIN_REQ because creating"
                             			    " a new RDMA channel failed.");
                            -		goto free_ring;
                            +		goto free_recv_ring;
                             	}
                             
                             	if (one_target_per_port) {
                            @@ -2683,11 +2804,12 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
                             	/* create srp_login_response */
                             	rsp->opcode = SRP_LOGIN_RSP;
                             	rsp->tag = req->tag;
                            -	rsp->max_it_iu_len = req->req_it_iu_len;
                            +	rsp->max_it_iu_len = cpu_to_be32(srp_max_req_size);
                             	rsp->max_ti_iu_len = req->req_it_iu_len;
                             	ch->max_ti_iu_len = it_iu_len;
                             	rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT |
                            -				   SRP_BUF_FORMAT_INDIRECT);
                            +				   SRP_BUF_FORMAT_INDIRECT |
                            +				   SRP_BUF_FORMAT_IMM);
                             	rsp->req_lim_delta = cpu_to_be32(ch->rq_size);
                             	ch->req_lim = ch->rq_size;
                             	ch->req_lim_delta = 0;
                            @@ -2747,6 +2869,11 @@ unreg_ch:
                             destroy_ib:
                             	srpt_destroy_ch_ib(ch);
                             
                            +free_recv_ring:
                            +	srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_recv_ring,
                            +			     ch->sport->sdev, ch->rq_size,
                            +			     srp_max_req_size, DMA_FROM_DEVICE);
                            +
                             free_ring:
                             	srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring,
                             			     ch->sport->sdev, ch->rq_size,
                            @@ -2859,10 +2986,22 @@ static int srpt_rdma_cm_req_recv(struct rdma_cm_id *cm_id,
                             				cm_id->route.path_rec->pkey, &req, src_addr);
                             }
                             
                            -static void srpt_cm_rej_recv(struct srpt_rdma_ch *ch)
                            +static void srpt_cm_rej_recv(struct srpt_rdma_ch *ch,
                            +			     enum ib_cm_rej_reason reason,
                            +			     const u8 *private_data,
                            +			     u8 private_data_len)
                             {
                            -	PRINT_INFO("Received CM REJ for ch %s-%d.", ch->sess_name,
                            -		   ch->qp->qp_num);
                            +	char *priv = kmalloc(private_data_len * 3 + 1, GFP_KERNEL);
                            +	int i;
                            +
                            +	if (priv) {
                            +		priv[0] = '\0';
                            +		for (i = 0; i < private_data_len; i++)
                            +			sprintf(priv + 3 * i, "%02x ", private_data[i]);
                            +	}
                            +	PRINT_INFO("Received CM REJ for ch %s-%d; reason %d; private data %s.",
                            +		   ch->sess_name, ch->qp->qp_num, reason, priv ? : "(?)");
                            +	kfree(priv);
                             }
                             
                             static void srpt_check_timeout(struct srpt_rdma_ch *ch)
                            @@ -2989,7 +3128,9 @@ static int srpt_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
                             					  event->private_data);
                             		break;
                             	case IB_CM_REJ_RECEIVED:
                            -		srpt_cm_rej_recv(ch);
                            +		srpt_cm_rej_recv(ch, event->param.rej_rcvd.reason,
                            +				 event->private_data,
                            +				 IB_CM_REJ_PRIVATE_DATA_SIZE);
                             		break;
                             	case IB_CM_RTU_RECEIVED:
                             	case IB_CM_USER_ESTABLISHED:
                            @@ -3033,7 +3174,9 @@ static int srpt_rdma_cm_handler(struct rdma_cm_id *cm_id,
                             		ret = srpt_rdma_cm_req_recv(cm_id, event);
                             		break;
                             	case RDMA_CM_EVENT_REJECTED:
                            -		srpt_cm_rej_recv(ch);
                            +		srpt_cm_rej_recv(ch, event->status,
                            +				 event->param.conn.private_data,
                            +				 event->param.conn.private_data_len);
                             		break;
                             	case RDMA_CM_EVENT_ESTABLISHED:
                             		srpt_cm_rtu_recv(ch);
                            @@ -3201,7 +3344,7 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch,
                             	dma_len = ib_sg_dma_len(dev, &sg[0]);
                             	dma_addr = ib_sg_dma_address(dev, &sg[0]);
                             
                            -	/* this second loop is really mapped sg_addres to rdma_iu->ib_sge */
                            +	/* this second loop is really mapped sg_address to rdma_iu->ib_sge */
                             	for (i = 0, j = 0, cur_sg = sg;
                             	     j < count && i < ioctx->n_rbuf && tsize > 0; ++i, ++riu, ++db) {
                             		rsize = be32_to_cpu(db->len);
                            @@ -3266,6 +3409,10 @@ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch,
                             
                             	EXTRACHECKS_BUG_ON(!ch);
                             	EXTRACHECKS_BUG_ON(!ioctx);
                            +
                            +	if (ioctx->imm_data)
                            +		return;
                            +
                             	EXTRACHECKS_BUG_ON(ioctx->n_rdma && !ioctx->rdma_ius);
                             
                             	if (ioctx->rdma_ius != (void *)ioctx->rdma_ius_buf)
                            @@ -3305,10 +3452,10 @@ static int srpt_perform_rdmas(struct srpt_rdma_ch *ch,
                             
                             	if (dir == SCST_DATA_WRITE) {
                             		ret = -ENOMEM;
                            -		sq_wr_avail = srpt_adjust_srq_wr_avail(ch, -n_rdma);
                            +		sq_wr_avail = srpt_adjust_sq_wr_avail(ch, -n_rdma);
                             		if (sq_wr_avail < 0) {
                            -			PRINT_WARNING("IB send queue full (needed %d)",
                            -				      n_rdma);
                            +			PRINT_WARNING("ch %s-%d send queue full (needed %d)",
                            +				      ch->sess_name, ch->qp->qp_num, n_rdma);
                             			goto out;
                             		}
                             	}
                            @@ -3374,7 +3521,7 @@ static int srpt_perform_rdmas(struct srpt_rdma_ch *ch,
                             
                             out:
                             	if (unlikely(dir == SCST_DATA_WRITE && ret < 0))
                            -		srpt_adjust_srq_wr_avail(ch, n_rdma);
                            +		srpt_adjust_sq_wr_avail(ch, n_rdma);
                             	return ret;
                             }
                             
                            @@ -3391,6 +3538,30 @@ static int srpt_xfer_data(struct srpt_rdma_ch *ch,
                             {
                             	int ret;
                             
                            +	if (ioctx->imm_data) {
                            +		BUG_ON(!srpt_test_and_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA,
                            +						    SRPT_STATE_DATA_IN));
                            +		if (unlikely(!scst_cmd_get_tgt_data_buff_alloced(scmnd))) {
                            +			unsigned offset = 0, len;
                            +			uint8_t *buf;
                            +
                            +			len = scst_get_buf_first(scmnd, &buf);
                            +			while (len > 0) {
                            +				memcpy(buf, ioctx->imm_data + offset, len);
                            +				offset += len;
                            +				len = scst_get_buf_next(scmnd, &buf);
                            +			}
                            +			WARN_ON_ONCE(offset !=
                            +				scst_cmd_get_expected_transfer_len(scmnd));
                            +		}
                            +		scst_rx_data(scmnd, SCST_RX_STATUS_SUCCESS,
                            +			     in_irq() ? SCST_CONTEXT_TASKLET :
                            +			     in_softirq() ? SCST_CONTEXT_DIRECT_ATOMIC :
                            +			     SCST_CONTEXT_DIRECT);
                            +		ret = SCST_TGT_RES_SUCCESS;
                            +		goto out;
                            +	}
                            +
                             	ret = srpt_map_sg_to_ib_sge(ch, ioctx, scmnd);
                             	if (ret) {
                             		PRINT_ERROR("%s[%d] ret=%d", __func__, __LINE__, ret);
                            @@ -4173,14 +4344,37 @@ static void srpt_add_one(struct ib_device *device)
                             	srq_attr.srq_type = IB_SRQT_BASIC;
                             #endif
                             
                            -	sdev->srq = ib_create_srq(sdev->pd, &srq_attr);
                            +	sdev->srq = use_srq ? ib_create_srq(sdev->pd, &srq_attr) :
                            +		ERR_PTR(-ENOSYS);
                             	if (IS_ERR(sdev->srq)) {
                            -		PRINT_ERROR("ib_create_srq() failed: %ld", PTR_ERR(sdev->srq));
                            -		goto err_mr;
                            -	}
                            +		TRACE_DBG("%s: ib_create_srq() failed: %ld", __func__,
                            +			  PTR_ERR(sdev->srq));
                             
                            -	TRACE_DBG("%s: create SRQ #wr= %d max_allow=%d dev= %s", __func__,
                            -		  sdev->srq_size, sdev->dev_attr.max_srq_wr, device->name);
                            +		/* SRQ not supported. */
                            +		sdev->use_srq = false;
                            +	} else {
                            +		TRACE_DBG("%s: create SRQ #wr= %d max_allow=%d dev= %s",
                            +			  __func__, sdev->srq_size, sdev->dev_attr.max_srq_wr,
                            +			  device->name);
                            +
                            +		sdev->use_srq = true;
                            +
                            +		sdev->ioctx_ring = (struct srpt_recv_ioctx **)
                            +			srpt_alloc_ioctx_ring(sdev, sdev->srq_size,
                            +					      sizeof(*sdev->ioctx_ring[0]),
                            +					      srp_max_req_size,
                            +					      DATA_ALIGNMENT_OFFSET,
                            +					      DMA_FROM_DEVICE);
                            +		if (!sdev->ioctx_ring) {
                            +			PRINT_ERROR("srpt_alloc_ioctx_ring() failed");
                            +			goto err_mr;
                            +		}
                            +
                            +		for (i = 0; i < sdev->srq_size; ++i) {
                            +			INIT_LIST_HEAD(&sdev->ioctx_ring[i]->wait_list);
                            +			srpt_post_recv(sdev, NULL, sdev->ioctx_ring[i]);
                            +		}
                            +	}
                             
                             	if (!srpt_service_guid)
                             		srpt_service_guid = be64_to_cpu(device->node_guid) &
                            @@ -4189,7 +4383,7 @@ static void srpt_add_one(struct ib_device *device)
                             	cm_id = ib_create_cm_id(device, srpt_cm_handler, sdev);
                             	if (IS_ERR(cm_id)) {
                             		PRINT_ERROR("ib_create_cm_id() failed: %ld", PTR_ERR(cm_id));
                            -		goto err_srq;
                            +		goto err_ring;
                             	}
                             	sdev->cm_id = cm_id;
                             
                            @@ -4220,20 +4414,6 @@ static void srpt_add_one(struct ib_device *device)
                             		goto err_cm;
                             	}
                             
                            -	sdev->ioctx_ring = (struct srpt_recv_ioctx **)
                            -		srpt_alloc_ioctx_ring(sdev, sdev->srq_size,
                            -				      sizeof(*sdev->ioctx_ring[0]),
                            -				      srp_max_req_size, DMA_FROM_DEVICE);
                            -	if (!sdev->ioctx_ring) {
                            -		PRINT_ERROR("srpt_alloc_ioctx_ring() failed");
                            -		goto err_event;
                            -	}
                            -
                            -	for (i = 0; i < sdev->srq_size; ++i) {
                            -		INIT_LIST_HEAD(&sdev->ioctx_ring[i]->wait_list);
                            -		srpt_post_recv(sdev, sdev->ioctx_ring[i]);
                            -	}
                            -
                             	WARN_ON(sdev->device->phys_port_cnt > ARRAY_SIZE(sdev->port));
                             
                             	for (i = 1; i <= sdev->device->phys_port_cnt; i++) {
                            @@ -4254,7 +4434,7 @@ static void srpt_add_one(struct ib_device *device)
                             		if (srpt_refresh_port(sport)) {
                             			PRINT_ERROR("MAD registration failed for %s-%d.",
                             				    sdev->device->name, i);
                            -			goto err_ring;
                            +			goto err_event;
                             		}
                             	}
                             
                            @@ -4265,16 +4445,16 @@ out:
                             	TRACE_EXIT();
                             	return;
                             
                            -err_ring:
                            -	srpt_free_ioctx_ring((struct srpt_ioctx **)sdev->ioctx_ring, sdev,
                            -			     sdev->srq_size, srp_max_req_size,
                            -			     DMA_FROM_DEVICE);
                             err_event:
                             	ib_unregister_event_handler(&sdev->event_handler);
                             err_cm:
                             	ib_destroy_cm_id(sdev->cm_id);
                            -err_srq:
                            -	ib_destroy_srq(sdev->srq);
                            +err_ring:
                            +	srpt_free_ioctx_ring((struct srpt_ioctx **)sdev->ioctx_ring, sdev,
                            +			     sdev->srq_size, srp_max_req_size,
                            +			     DMA_FROM_DEVICE);
                            +	if (sdev->use_srq)
                            +		ib_destroy_srq(sdev->srq);
                             err_mr:
                             	ib_dereg_mr(sdev->mr);
                             err_pd:
                            @@ -4347,13 +4527,15 @@ static void srpt_remove_one(struct ib_device *device)
                             		sdev->srpt_tgt.scst_tgt = NULL;
                             	}
                             
                            -	ib_destroy_srq(sdev->srq);
                            -	ib_dereg_mr(sdev->mr);
                            -	ib_dealloc_pd(sdev->pd);
                            -
                             	srpt_free_ioctx_ring((struct srpt_ioctx **)sdev->ioctx_ring, sdev,
                             			     sdev->srq_size, srp_max_req_size, DMA_FROM_DEVICE);
                             	sdev->ioctx_ring = NULL;
                            +
                            +	if (sdev->use_srq)
                            +		ib_destroy_srq(sdev->srq);
                            +	ib_dereg_mr(sdev->mr);
                            +	ib_dealloc_pd(sdev->pd);
                            +
                             	kfree(sdev);
                             
                             	TRACE_EXIT();
                            @@ -4428,6 +4610,8 @@ static int __init srpt_init_module(void)
                             {
                             	int ret;
                             
                            +	BUILD_BUG_ON(sizeof(struct srp_imm_buf) != 8);
                            +
                             	ret = -EINVAL;
                             	if (srp_max_req_size < MIN_MAX_REQ_SIZE) {
                             		PRINT_ERROR("invalid value %d for kernel module parameter"
                            diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h
                            index 49d2a9b9f..30abefbeb 100644
                            --- a/srpt/src/ib_srpt.h
                            +++ b/srpt/src/ib_srpt.h
                            @@ -56,6 +56,7 @@
                             #endif
                             #include 
                             #include 
                            +#include "srp-ext.h"
                             #include "ib_dm_mad.h"
                             
                             /*
                            @@ -128,10 +129,9 @@ enum {
                             	MAX_SRPT_SRQ_SIZE = 65535,
                             
                             	MIN_MAX_REQ_SIZE = 996,
                            -	DEFAULT_MAX_REQ_SIZE
                            -		= sizeof(struct srp_cmd)/*48*/
                            -		+ sizeof(struct srp_indirect_buf)/*20*/
                            -		+ 255 * sizeof(struct srp_direct_buf)/*16*/,
                            +	SRP_IMM_DATA_OUT_OFFSET = 80,
                            +	DEFAULT_MAX_REQ_SIZE = SRP_IMM_DATA_OUT_OFFSET + 8192,
                            +	DATA_ALIGNMENT_OFFSET = 512 - SRP_IMM_DATA_OUT_OFFSET,
                             
                             	MIN_MAX_RSP_SIZE = sizeof(struct srp_rsp)/*36*/ + 4,
                             	DEFAULT_MAX_RSP_SIZE = 256, /* leaves 220 bytes for sense data */
                            @@ -207,13 +207,15 @@ enum srpt_command_state {
                             
                             /**
                              * struct srpt_ioctx - Shared SRPT I/O context information.
                            - * @buf:   Pointer to the buffer.
                            - * @dma:   DMA address of the buffer.
                            - * @index: Index of the I/O context in its ioctx_ring array.
                            + * @buf:    Pointer to the buffer.
                            + * @dma:    DMA address of the buffer.
                            + * @offset: Offset of the first byte in @buf and @dma that is actually used.
                            + * @index:  Index of the I/O context in its ioctx_ring array.
                              */
                             struct srpt_ioctx {
                             	void			*buf;
                             	dma_addr_t		dma;
                            +	uint32_t		offset;
                             	uint32_t		index;
                             };
                             
                            @@ -221,10 +223,12 @@ struct srpt_ioctx {
                              * struct srpt_recv_ioctx - SRPT receive I/O context.
                              * @ioctx:     See above.
                              * @wait_list: Node for insertion in srpt_rdma_ch.cmd_wait_list.
                            + * @byte_len:  Number of bytes in @ioctx.buf.
                              */
                             struct srpt_recv_ioctx {
                             	struct srpt_ioctx	ioctx;
                             	struct list_head	wait_list;
                            +	int			byte_len;
                             };
                             
                             /**
                            @@ -239,7 +243,10 @@ struct srpt_tsk_mgmt {
                              * struct srpt_send_ioctx - SRPT send I/O context.
                              * @ioctx:       See above.
                              * @ch:          Channel pointer.
                            + * @recv_ioctx:  Receive I/O context associated with this send I/O context.
                              * @rdma_ius:    Array with information about the RDMA mapping.
                            + * @imm_data:    Pointer to immediate data when using the immediate data format.
                            + * @imm_sg:      Scatterlist for immediate data.
                              * @rbufs:       Pointer to SRP data buffer array.
                              * @single_rbuf: SRP data buffer if the command has only a single buffer.
                              * @sg:          Pointer to sg-list associated with this I/O context.
                            @@ -263,7 +270,10 @@ struct srpt_tsk_mgmt {
                             struct srpt_send_ioctx {
                             	struct srpt_ioctx	ioctx;
                             	struct srpt_rdma_ch	*ch;
                            +	struct srpt_recv_ioctx	*recv_ioctx;
                             	struct rdma_iu		*rdma_ius;
                            +	void			*imm_data;
                            +	struct scatterlist	imm_sg;
                             	struct srp_direct_buf	*rbufs;
                             	struct srp_direct_buf	single_rbuf;
                             	struct scatterlist	*sg;
                            @@ -366,6 +376,7 @@ struct srpt_rdma_ch {
                             	spinlock_t		spinlock;
                             	struct list_head	free_list;
                             	struct srpt_send_ioctx	**ioctx_ring;
                            +	struct srpt_recv_ioctx	**ioctx_recv_ring;
                             	struct ib_wc		wc[16];
                             	enum rdma_ch_state	state;
                             	struct list_head	list;
                            @@ -448,6 +459,7 @@ struct srpt_port {
                              * @dev_attr:      Attributes of the InfiniBand device as obtained during the
                              *                 ib_client.add() callback.
                              * @srq_size:      SRQ size.
                            + * @use_srq:       Whether or not to use SRQ.
                              * @ioctx_ring:    Per-HCA SRQ.
                              * @port:	   Information about the ports owned by this HCA.
                              * @event_handler: Per-HCA asynchronous IB event handler.
                            @@ -462,6 +474,7 @@ struct srpt_device {
                             	struct ib_cm_id		*cm_id;
                             	struct ib_device_attr	dev_attr;
                             	int			srq_size;
                            +	bool			use_srq;
                             	struct srpt_recv_ioctx	**ioctx_ring;
                             	struct srpt_port	port[2];
                             	struct ib_event_handler	event_handler;
                            diff --git a/srpt/src/srp-ext.h b/srpt/src/srp-ext.h
                            new file mode 100644
                            index 000000000..3e6752cec
                            --- /dev/null
                            +++ b/srpt/src/srp-ext.h
                            @@ -0,0 +1,22 @@
                            +/*
                            + * Extensions to the SRPr16a protocol
                            + *
                            + * Copyright (C) 2013 Fusion-io, Inc. All rights reserved.
                            + */
                            +
                            +#ifndef _SRP_EXT_H_
                            +#define _SRP_EXT_H_
                            +
                            +/*
                            + * Data is present as immediate data instead of being referred to via a
                            + * descriptor.
                            + */
                            +enum { SRP_DATA_DESC_IMM = 3 };
                            +enum { SRP_BUF_FORMAT_IMM = 1 << 3 };
                            +
                            +struct srp_imm_buf {
                            +	__be32	len;
                            +	__be32	offset;
                            +};
                            +
                            +#endif /* _SRP_EXT_H_ */
                            diff --git a/usr/fileio/common.c b/usr/fileio/common.c
                            index ac98120af..75d09a7d7 100644
                            --- a/usr/fileio/common.c
                            +++ b/usr/fileio/common.c
                            @@ -209,7 +209,7 @@ static int do_exec(struct vdisk_cmd *vcmd)
                             
                             #ifdef DEBUG_SENSE
                             	if ((random() % 100000) == 75) {
                            -		set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
                            +		set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_internal_failure));
                             		goto out;
                             	}
                             #endif
                            diff --git a/www/comparison.html b/www/comparison.html
                            index 561be60d9..5f00293ea 100644
                            --- a/www/comparison.html
                            +++ b/www/comparison.html
                            @@ -130,7 +130,7 @@ transfer values (Wide (parallel) SCSI, SAS)			 + 		 - 
                             
                             Automatic sessions reassignment (changes in the
                            -access control immediatelly seen by initiators)		 + 		 - 		 - 		 - 
                            +access control immediately seen by initiators)		 + 		 - 		 - 		 - 
                             
                                                                                                                         
                             Support for Asynchronous Event Notifications
                            @@ -243,22 +243,22 @@ apply changes in the config file on fly without any restarts	 scsta
                             iSCSI					 + 		 + 		 + 		 + 
                                                                                                                                                        
                                                                                                                         
                            -QLogic (Fibre Channel and FCoE)		 + 		 - 		 - 		 Preliminary
                            +QLogic (Fibre Channel and FCoE)		 + 		 - 		 - 		 +
                             
                             
                            -Emulex (Fibre Channel and FCoE)		 + 		 - 		 - 		 - 
                            +Emulex (Fibre Channel and FCoE)		 + 		 - 		 - 		 + 
                             
                                                                                                                                                        
                             SRP					 + 		 - 		 - 		 Preliminary 
                                                                                                                                                        
                                                                                                                                                         
                            -iSER					 - 		 + 		 - 		 Preliminary 		
                            +iSER					 + 		 + 		 - 		 + 		
                              
                                                                                                                                                       
                             Marvell (SAS)				 Beta 		 - 		 - 		 - 		
                             
                             
                            -FCoE					 Beta 		Under
                            +FCoE					 + 		Under
                             									 			 development	 - 		 Alpha 	
                                                                                                                                                                              
                                                                                                                                                         
                            
                            From f90984d6daa2a0add53560878f8f02ce776f01cc Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:14:43 +0000
                            Subject: [PATCH 104/128] isert: Fix connection resource leak
                            
                            Fix resource leak is iscsi-scstd read the new connection file,
                            but did not open it before it was killed
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5994 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/isert_login.c | 34 +++++++++++++++++-----
                             1 file changed, 26 insertions(+), 8 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index f697b4fd4..233aec5ce 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -98,9 +98,10 @@ static void isert_kref_release_dev(struct kref *kref)
                             						  kref);
                             	kref_init(&dev->kref);
                             	dev->occupied = 0;
                            -	list_del_init(&dev->conn_list_entry);
                             	dev->state = CS_INIT;
                             	atomic_set(&dev->available, 1);
                            +	if (!list_is_singular(&dev->conn_list_entry))
                            +		list_del_init(&dev->conn_list_entry);
                             }
                             
                             static void isert_dev_release(struct isert_conn_dev *dev)
                            @@ -297,6 +298,16 @@ static int isert_listen_open(struct inode *inode, struct file *filp)
                             	return 0;
                             }
                             
                            +static void isert_delete_conn_dev(struct isert_conn_dev *conn_dev)
                            +{
                            +	isert_del_timer(conn_dev);
                            +	if (conn_dev->conn) {
                            +		isert_close_connection(conn_dev->conn);
                            +		conn_dev->conn = NULL;
                            +	}
                            +	list_del(&conn_dev->conn_list_entry);
                            +}
                            +
                             static int isert_listen_release(struct inode *inode, struct file *filp)
                             {
                             	struct isert_listener_dev *dev = filp->private_data;
                            @@ -308,12 +319,15 @@ static int isert_listen_release(struct inode *inode, struct file *filp)
                             					    struct isert_conn_dev,
                             					    conn_list_entry);
                             
                            -		isert_del_timer(conn_dev);
                            -		if (conn_dev->conn) {
                            -			isert_close_connection(conn_dev->conn);
                            -			conn_dev->conn = NULL;
                            -		}
                            -		list_del(&conn_dev->conn_list_entry);
                            +		isert_delete_conn_dev(conn_dev);
                            +	}
                            +
                            +	while (!list_empty(&dev->curr_conn_list)) {
                            +		conn_dev = list_first_entry(&dev->curr_conn_list,
                            +					    struct isert_conn_dev,
                            +					    conn_list_entry);
                            +
                            +		isert_delete_conn_dev(conn_dev);
                             	}
                             
                             	atomic_inc(&dev->available);
                            @@ -468,12 +482,16 @@ static int isert_open(struct inode *inode, struct file *filp)
                             
                             	dev = container_of(inode->i_cdev, struct isert_conn_dev, cdev);
                             
                            -	if (!atomic_dec_and_test(&dev->available)) {
                            +	if (unlikely(!atomic_dec_and_test(&dev->available))) {
                             		atomic_inc(&dev->available);
                             		res = -EBUSY; /* already open */
                             		goto out;
                             	}
                             
                            +	spin_lock(&isert_listen_dev.conn_lock);
                            +	list_del_init(&dev->conn_list_entry);
                            +	spin_unlock(&isert_listen_dev.conn_lock);
                            +
                             	filp->private_data = dev; /* for other methods */
                             
                             out:
                            
                            From 950f800ac4680eb68bc6dba7896fb73d74278b40 Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:14:53 +0000
                            Subject: [PATCH 105/128] isert: Decrease connection timeout from 120 to 60
                             seconds
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5995 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/isert_login.c | 2 +-
                             1 file changed, 1 insertion(+), 1 deletion(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index 233aec5ce..7665a7e88 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -165,7 +165,7 @@ static int add_new_connection(struct isert_listener_dev *dev,
                             
                             	init_timer(&conn_dev->tmo_timer);
                             	conn_dev->tmo_timer.function = isert_conn_timer_fn;
                            -	conn_dev->tmo_timer.expires = jiffies + 120 * HZ;
                            +	conn_dev->tmo_timer.expires = jiffies + 60 * HZ;
                             	conn_dev->tmo_timer.data = (unsigned long)conn_dev;
                             	add_timer(&conn_dev->tmo_timer);
                             	conn_dev->timer_active = 1;
                            
                            From 5e91026bb661e8120b26edd48430f7b4d6d3d98f Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:15:03 +0000
                            Subject: [PATCH 106/128] iscsi-scstd: Fix error print in iser listener socker
                             create
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5996 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/usr/iscsi_scstd.c | 4 ++--
                             1 file changed, 2 insertions(+), 2 deletions(-)
                            
                            diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c
                            index b0875c761..69a67bb07 100644
                            --- a/iscsi-scst/usr/iscsi_scstd.c
                            +++ b/iscsi-scst/usr/iscsi_scstd.c
                            @@ -265,8 +265,8 @@ static void create_iser_listen_socket(struct pollfd *array)
                             
                             		rc = ioctl(iser_fd, SET_LISTEN_ADDR, &info);
                             		if (rc != 0) {
                            -			log_error("Unable to set address info (%s)!",
                            -				strerror(rc));
                            +			log_error("Unable to set listen address (%s)!",
                            +				strerror(errno));
                             		}
                             		++i;
                             	}
                            
                            From e03adc83edbc741d2d22a11c9ce5e94692624fdb Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:15:13 +0000
                            Subject: [PATCH 107/128] isert: Increase module reference as soon as possible
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5997 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/iser_rdma.c | 13 +++++++------
                             1 file changed, 7 insertions(+), 6 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            index 5571bddab..8e2efa904 100644
                            --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            @@ -1060,11 +1060,6 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id,
                             
                             	TRACE_ENTRY();
                             
                            -	if (unlikely(!try_module_get(THIS_MODULE))) {
                            -		err = -EINVAL;
                            -		goto fail_get;
                            -	}
                            -
                             	isert_conn = isert_conn_alloc();
                             	if (unlikely(!isert_conn)) {
                             		pr_err("Unable to allocate iser conn, cm_id:%p\n", cm_id);
                            @@ -1126,7 +1121,6 @@ fail_login_req_pdu:
                             	isert_conn_kfree(isert_conn);
                             fail_alloc:
                             	module_put(THIS_MODULE);
                            -fail_get:
                             	TRACE_EXIT_RES(err);
                             	return ERR_PTR(err);
                             }
                            @@ -1230,6 +1224,11 @@ static int isert_cm_conn_req_handler(struct rdma_cm_id *cm_id,
                             
                             	TRACE_ENTRY();
                             
                            +	if (unlikely(!try_module_get(THIS_MODULE))) {
                            +		err = -EINVAL;
                            +		goto fail_get;
                            +	}
                            +
                             	mutex_lock(&dev_list_mutex);
                             	isert_dev = isert_device_find(ib_dev);
                             	if (!isert_dev) {
                            @@ -1330,6 +1329,8 @@ fail_conn_create:
                             	mutex_unlock(&dev_list_mutex);
                             fail_dev_create:
                             	rdma_reject(cm_id, NULL, 0);
                            +fail_get:
                            +	module_put(THIS_MODULE);
                             	goto out;
                             }
                             
                            
                            From c9e71e4d7c24a00992d5ed8bcf6fb9326c6a1f5c Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:15:22 +0000
                            Subject: [PATCH 108/128] isert: Do not crash if we receive no data inside PDU
                            
                            Text continuation request PDUs will have zero data.
                            Avoid crashing in those cases.
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5998 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/iser_rdma.c   |  6 +++++-
                             iscsi-scst/kernel/isert-scst/isert_login.c | 18 ++++++++++++++++--
                             2 files changed, 21 insertions(+), 3 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            index 8e2efa904..bcafa771f 100644
                            --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            @@ -155,7 +155,11 @@ static int isert_pdu_handle_login_req(struct isert_cmnd *isert_pdu)
                             
                             static int isert_pdu_handle_text(struct isert_cmnd *pdu)
                             {
                            -	return isert_login_req_rx(&pdu->iscsi);
                            +	struct iscsi_cmnd *iscsi_cmnd = &pdu->iscsi;
                            +
                            +	iscsi_cmnd->sg_cnt = pdu->buf.sg_cnt;
                            +	iscsi_cmnd->sg = pdu->buf.sg;
                            +	return isert_login_req_rx(iscsi_cmnd);
                             }
                             
                             static int isert_pdu_handle_nop_out(struct isert_cmnd *pdu)
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index 7665a7e88..8ee988801 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -447,7 +447,9 @@ int isert_connection_closed(struct iscsi_conn *iscsi_conn)
                             			dev->state = CS_DISCONNECTED;
                             			if (dev->login_req) {
                             				res = isert_task_abort(dev->login_req);
                            +				spin_lock(&dev->pdu_lock);
                             				dev->login_req = NULL;
                            +				spin_unlock(&dev->pdu_lock);
                             			}
                             
                             			dev->conn = NULL;
                            @@ -464,10 +466,19 @@ int isert_connection_closed(struct iscsi_conn *iscsi_conn)
                             
                             static bool will_read_block(struct isert_conn_dev *dev)
                             {
                            -	bool res;
                            +	bool res = true;
                             
                             	spin_lock(&dev->pdu_lock);
                            -	res = (dev->login_req == NULL) && (dev->state != CS_DISCONNECTED);
                            +	if (dev->login_req != NULL) {
                            +		switch (dev->state) {
                            +		case CS_REQ_BHS:
                            +		case CS_REQ_DATA:
                            +			res = false;
                            +			break;
                            +		default:
                            +			;
                            +		}
                            +	}
                             	spin_unlock(&dev->pdu_lock);
                             
                             	return res;
                            @@ -700,6 +711,9 @@ static long isert_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                             			dev->login_rsp->bufflen -= dev->write_len;
                             
                             			if (!last || dev->is_discovery) {
                            +				spin_lock(&dev->pdu_lock);
                            +				dev->login_req = NULL;
                            +				spin_unlock(&dev->pdu_lock);
                             				res = isert_login_rsp_tx(dev->login_rsp,
                             							last,
                             							dev->is_discovery);
                            
                            From ba2083708acd33a7a1e4f40ed8f3aacbc9457e0e Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:15:35 +0000
                            Subject: [PATCH 109/128] isert: Make the login character device more posix
                             compliant
                            
                            This fixes a case where iscsi-scstd will go into busy-waiting loop
                            on some occasions because it would not detect disconnect correctly
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@5999 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/isert_login.c | 16 +++++++++-------
                             1 file changed, 9 insertions(+), 7 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index 8ee988801..eb764f2aa 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -555,6 +555,9 @@ static ssize_t isert_read(struct file *filp, char __user *buf, size_t count,
                             	struct isert_conn_dev *dev = filp->private_data;
                             	size_t to_read;
                             
                            +	if (dev->state == CS_DISCONNECTED)
                            +		return -EPIPE;
                            +
                             	if (will_read_block(dev)) {
                             		int ret;
                             		if (filp->f_flags & O_NONBLOCK)
                            @@ -565,9 +568,6 @@ static ssize_t isert_read(struct file *filp, char __user *buf, size_t count,
                             			return ret;
                             	}
                             
                            -	if (dev->state == CS_DISCONNECTED)
                            -		return -EPIPE;
                            -
                             	to_read = min(count, dev->read_len);
                             	if (copy_to_user(buf, dev->read_buf, to_read))
                             		return -EFAULT;
                            @@ -769,11 +769,13 @@ static unsigned int isert_poll(struct file *filp,
                             	poll_wait(filp, &dev->waitqueue, wait);
                             
                             	if (!dev->conn)
                            -		mask |= POLLHUP | POLLERR;
                            -	if (!will_read_block(dev))
                            -		mask |= POLLIN | POLLRDNORM;
                            +		mask |= POLLHUP | POLLIN;
                            +	else {
                            +		if (!will_read_block(dev))
                            +			mask |= POLLIN | POLLRDNORM;
                             
                            -	mask |= POLLOUT | POLLWRNORM;
                            +		mask |= POLLOUT | POLLWRNORM;
                            +	}
                             
                             	return mask;
                             }
                            
                            From 978ee4effa70fbdc5e66e1ac461277a036cfbc88 Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:15:46 +0000
                            Subject: [PATCH 110/128] isert: Micro-optimization of poll_cq WC loop
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6000 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/iser_rdma.c | 13 +++++++------
                             1 file changed, 7 insertions(+), 6 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            index bcafa771f..4ce204983 100644
                            --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            @@ -579,18 +579,19 @@ static void isert_handle_wc_error(struct ib_wc *wc)
                             
                             static int isert_poll_cq(struct isert_cq *cq)
                             {
                            -	int err, i;
                            +	int err;
                            +	struct ib_wc *wc, *last_wc;
                             
                             	TRACE_ENTRY();
                             
                             	do {
                             		err = ib_poll_cq(cq->cq, ARRAY_SIZE(cq->wc), cq->wc);
                            -
                            -		for (i = 0; i < err; ++i) {
                            -			if (likely(cq->wc[i].status == IB_WC_SUCCESS))
                            -				isert_handle_wc(&cq->wc[i]);
                            +		last_wc = &cq->wc[err];
                            +		for (wc = cq->wc; wc < last_wc; ++wc) {
                            +			if (likely(wc->status == IB_WC_SUCCESS))
                            +				isert_handle_wc(wc);
                             			else
                            -				isert_handle_wc_error(&cq->wc[i]);
                            +				isert_handle_wc_error(wc);
                             		}
                             
                             	} while (err > 0);
                            
                            From 06e42a7273b3bf8e391ec55d3c0e30e5c39bab23 Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:15:55 +0000
                            Subject: [PATCH 111/128] isert: Avoid starving connections in high connect
                             load
                            
                            Stack was used instead of queue for incoming connection management.
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6001 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/isert_login.c | 2 +-
                             1 file changed, 1 insertion(+), 1 deletion(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index eb764f2aa..80ebfdda9 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -74,7 +74,7 @@ static struct isert_conn_dev *get_available_dev(struct isert_listener_dev *dev,
                             			res->occupied = 1;
                             			res->conn = conn;
                             			isert_set_priv(conn, res);
                            -			list_add(&res->conn_list_entry, &dev->new_conn_list);
                            +			list_add_tail(&res->conn_list_entry, &dev->new_conn_list);
                             			break;
                             		}
                             	}
                            
                            From 4a8b416ff8297819cc5ddbc67e89a75a709adcfe Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:16:05 +0000
                            Subject: [PATCH 112/128] isert: Make sure we have a valid conn pointer
                            
                            Fix a race where disconnect was called while iscsi-scstd was doing ioctls
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6002 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/isert.h       |  3 +++
                             iscsi-scst/kernel/isert-scst/isert_login.c | 14 ++++++++------
                             2 files changed, 11 insertions(+), 6 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/isert.h b/iscsi-scst/kernel/isert-scst/isert.h
                            index c083d536d..af7c713af 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert.h
                            +++ b/iscsi-scst/kernel/isert-scst/isert.h
                            @@ -92,6 +92,8 @@ enum isert_conn_dev_state {
                             	CS_DISCONNECTED,
                             };
                             
                            +#define ISERT_CONN_PASSED	0
                            +
                             struct isert_conn_dev {
                             	struct device *dev;
                             	struct cdev cdev;
                            @@ -116,6 +118,7 @@ struct isert_conn_dev {
                             	struct timer_list tmo_timer;
                             	int timer_active;
                             	struct kref kref;
                            +	unsigned long flags;
                             };
                             
                             #define ISER_CONN_DEV_PREFIX "isert/conn"
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index 80ebfdda9..35c094433 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -102,6 +102,8 @@ static void isert_kref_release_dev(struct kref *kref)
                             	atomic_set(&dev->available, 1);
                             	if (!list_is_singular(&dev->conn_list_entry))
                             		list_del_init(&dev->conn_list_entry);
                            +	dev->flags = 0;
                            +	dev->conn = NULL;
                             }
                             
                             static void isert_dev_release(struct isert_conn_dev *dev)
                            @@ -219,7 +221,7 @@ int isert_conn_alloc(struct iscsi_session *session,
                             				       &session->tgt_params);
                             
                             	if (!res)
                            -		dev->conn = NULL;
                            +		set_bit(ISERT_CONN_PASSED, &dev->flags);
                             
                             	fput(filp);
                             
                            @@ -301,9 +303,10 @@ static int isert_listen_open(struct inode *inode, struct file *filp)
                             static void isert_delete_conn_dev(struct isert_conn_dev *conn_dev)
                             {
                             	isert_del_timer(conn_dev);
                            -	if (conn_dev->conn) {
                            +
                            +	if (!test_and_set_bit(ISERT_CONN_PASSED, &conn_dev->flags)) {
                            +		BUG_ON(conn_dev->conn == NULL);
                             		isert_close_connection(conn_dev->conn);
                            -		conn_dev->conn = NULL;
                             	}
                             	list_del(&conn_dev->conn_list_entry);
                             }
                            @@ -452,7 +455,6 @@ int isert_connection_closed(struct iscsi_conn *iscsi_conn)
                             				spin_unlock(&dev->pdu_lock);
                             			}
                             
                            -			dev->conn = NULL;
                             			wake_up(&dev->waitqueue);
                             			isert_dev_release(dev);
                             		}
                            @@ -521,9 +523,9 @@ static int isert_release(struct inode *inode, struct file *filp)
                             	dev->sg_virt = NULL;
                             	dev->is_discovery = 0;
                             
                            -	if (dev->conn) {
                            +	if (!test_and_set_bit(ISERT_CONN_PASSED, &dev->flags)) {
                            +		BUG_ON(dev->conn == NULL);
                             		isert_close_connection(dev->conn);
                            -		dev->conn = NULL;
                             	}
                             
                             	isert_del_timer(dev);
                            
                            From 0ce1128b190dd5dd5679692ef885e2c4b0a132f9 Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:16:15 +0000
                            Subject: [PATCH 113/128] isert: Fix discovery and login with actual 8192 bytes
                             of data
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6003 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/iser_hdr.h  | 2 ++
                             iscsi-scst/kernel/isert-scst/iser_rdma.c | 6 +++---
                             iscsi-scst/kernel/isert-scst/isert.h     | 2 +-
                             3 files changed, 6 insertions(+), 4 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/iser_hdr.h b/iscsi-scst/kernel/isert-scst/iser_hdr.h
                            index a49c13a6e..4cfd74a94 100644
                            --- a/iscsi-scst/kernel/isert-scst/iser_hdr.h
                            +++ b/iscsi-scst/kernel/isert-scst/iser_hdr.h
                            @@ -58,6 +58,8 @@ struct isert_hdr {
                             
                             #define ISER_HDRS_SZ		(sizeof(struct isert_hdr) + sizeof(struct iscsi_hdr))
                             
                            +#define ISER_MAX_LOGIN_RDSL	(ISCSI_LOGIN_MAX_RDSL + ISER_HDRS_SZ)
                            +
                             #define ISER_ZBVA_NOT_SUPPORTED         0x80
                             #define ISER_SEND_W_INV_NOT_SUPPORTED   0x40
                             
                            diff --git a/iscsi-scst/kernel/isert-scst/iser_rdma.c b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            index 4ce204983..ad0f402b5 100644
                            --- a/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            +++ b/iscsi-scst/kernel/isert-scst/iser_rdma.c
                            @@ -1013,7 +1013,7 @@ static int isert_conn_qp_create(struct isert_connection *isert_conn)
                             	WARN_ON(isert_conn->max_sge < 1);
                             
                             	qp_attr.cap.max_send_sge = isert_conn->max_sge;
                            -	qp_attr.cap.max_recv_sge = 2;
                            +	qp_attr.cap.max_recv_sge = 3;
                             	qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
                             	qp_attr.qp_type = IB_QPT_RC;
                             
                            @@ -1082,7 +1082,7 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id,
                             	spin_lock_init(&isert_conn->post_recv_lock);
                             
                             	isert_conn->login_req_pdu = isert_rx_pdu_alloc(isert_conn,
                            -						       ISCSI_LOGIN_MAX_RDSL);
                            +						       ISER_MAX_LOGIN_RDSL);
                             	if (unlikely(!isert_conn->login_req_pdu)) {
                             		pr_err("Failed to init login req rx pdu\n");
                             		err = -ENOMEM;
                            @@ -1090,7 +1090,7 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id,
                             	}
                             
                             	isert_conn->login_rsp_pdu = isert_tx_pdu_alloc(isert_conn,
                            -						       ISCSI_LOGIN_MAX_RDSL);
                            +						       ISER_MAX_LOGIN_RDSL);
                             	if (unlikely(!isert_conn->login_rsp_pdu)) {
                             		pr_err("Failed to init login rsp tx pdu\n");
                             		err = -ENOMEM;
                            diff --git a/iscsi-scst/kernel/isert-scst/isert.h b/iscsi-scst/kernel/isert-scst/isert.h
                            index af7c713af..178b2284f 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert.h
                            +++ b/iscsi-scst/kernel/isert-scst/isert.h
                            @@ -112,7 +112,7 @@ struct isert_conn_dev {
                             	size_t write_len;
                             	char *write_buf;
                             	void *sg_virt;
                            -	struct page *pages[DIV_ROUND_UP(ISCSI_LOGIN_MAX_RDSL, PAGE_SIZE)];
                            +	struct page *pages[DIV_ROUND_UP(ISER_MAX_LOGIN_RDSL, PAGE_SIZE)];
                             	enum isert_conn_dev_state state;
                             	int is_discovery;
                             	struct timer_list tmo_timer;
                            
                            From fab0ea06e231cd07a97a36859895d1d521dc6734 Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Wed, 28 Jan 2015 12:16:26 +0000
                            Subject: [PATCH 114/128] isert: Properly propagate portal creation error to
                             iscsi-scstd
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6004 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/iser_datamover.c | 7 +------
                             iscsi-scst/kernel/isert-scst/isert_login.c    | 4 ++--
                             2 files changed, 3 insertions(+), 8 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/iser_datamover.c b/iscsi-scst/kernel/isert-scst/iser_datamover.c
                            index 6ea9f8371..7c6809b6b 100644
                            --- a/iscsi-scst/kernel/isert-scst/iser_datamover.c
                            +++ b/iscsi-scst/kernel/isert-scst/iser_datamover.c
                            @@ -92,12 +92,7 @@ out:
                             
                             void *isert_portal_add(struct sockaddr *saddr, size_t addr_len)
                             {
                            -	struct isert_portal *portal = isert_portal_start(saddr, addr_len);
                            -
                            -	if (IS_ERR(portal))
                            -		portal = NULL;
                            -
                            -	return portal;
                            +	return isert_portal_start(saddr, addr_len);
                             }
                             
                             int isert_portal_remove(void *portal_h)
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index 35c094433..e6a4ff9d9 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -409,10 +409,10 @@ static long isert_listen_ioctl(struct file *filp, unsigned int cmd,
                             
                             		portal = isert_portal_add((struct sockaddr *)&dev->info.addr,
                             					  dev->info.addr_len);
                            -		if (!portal) {
                            +		if (IS_ERR(portal)) {
                             			PRINT_ERROR("Unable to add portal of size %zu\n",
                             				    dev->info.addr_len);
                            -			res = -EINVAL;
                            +			res = PTR_ERR(portal);
                             			goto out;
                             		}
                             		dev->portal_h[dev->free_portal_idx++] = portal;
                            
                            From 2b9e1f9be89bd7ed60243fa2a658899674c0df9f Mon Sep 17 00:00:00 2001
                            From: Yan Burman 
                            Date: Sun, 1 Feb 2015 11:01:21 +0000
                            Subject: [PATCH 115/128] isert: Fix listener release handling
                            
                            Signed-off-by: Yan Burman 
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6017 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             iscsi-scst/kernel/isert-scst/isert_login.c | 18 ++++--------------
                             1 file changed, 4 insertions(+), 14 deletions(-)
                            
                            diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c
                            index e6a4ff9d9..168d3834c 100644
                            --- a/iscsi-scst/kernel/isert-scst/isert_login.c
                            +++ b/iscsi-scst/kernel/isert-scst/isert_login.c
                            @@ -308,7 +308,6 @@ static void isert_delete_conn_dev(struct isert_conn_dev *conn_dev)
                             		BUG_ON(conn_dev->conn == NULL);
                             		isert_close_connection(conn_dev->conn);
                             	}
                            -	list_del(&conn_dev->conn_list_entry);
                             }
                             
                             static int isert_listen_release(struct inode *inode, struct file *filp)
                            @@ -316,22 +315,13 @@ static int isert_listen_release(struct inode *inode, struct file *filp)
                             	struct isert_listener_dev *dev = filp->private_data;
                             	struct isert_conn_dev *conn_dev;
                             
                            -	/* No need for locking here, since the chardev is being closed */
                            -	while (!list_empty(&dev->new_conn_list)) {
                            -		conn_dev = list_first_entry(&dev->new_conn_list,
                            -					    struct isert_conn_dev,
                            -					    conn_list_entry);
                            -
                            +	spin_lock(&isert_listen_dev.conn_lock);
                            +	list_for_each_entry(conn_dev, &dev->new_conn_list, conn_list_entry)
                             		isert_delete_conn_dev(conn_dev);
                            -	}
                            -
                            -	while (!list_empty(&dev->curr_conn_list)) {
                            -		conn_dev = list_first_entry(&dev->curr_conn_list,
                            -					    struct isert_conn_dev,
                            -					    conn_list_entry);
                             
                            +	list_for_each_entry(conn_dev, &dev->curr_conn_list, conn_list_entry)
                             		isert_delete_conn_dev(conn_dev);
                            -	}
                            +	spin_unlock(&isert_listen_dev.conn_lock);
                             
                             	atomic_inc(&dev->available);
                             	return 0;
                            
                            From b362b6453eb5d6167f30a00748b2aa15536ed8a0 Mon Sep 17 00:00:00 2001
                            From: Bart Van Assche 
                            Date: Tue, 10 Feb 2015 08:07:10 +0000
                            Subject: [PATCH 116/128] Docs updates (merge r6015 from trunk)
                            
                            git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6029 d57e44dd-8a1f-0410-8b47-8ef2f437770f
                            ---
                             doc/scst_user_spec.sgml | 2 +-
                             www/index.html          | 1 +
                             2 files changed, 2 insertions(+), 1 deletion(-)
                            
                            diff --git a/doc/scst_user_spec.sgml b/doc/scst_user_spec.sgml
                            index e50fc92fa..467f9e8e1 100644
                            --- a/doc/scst_user_spec.sgml
                            +++ b/doc/scst_user_spec.sgml
                            @@ -3,7 +3,7 @@
                             
                            -SCST user space device handler module interface description +SCST user space device handler interface description diff --git a/www/index.html b/www/index.html index 8b2c9e5e3..4f5314802 100644 --- a/www/index.html +++ b/www/index.html @@ -154,6 +154,7 @@

                            Gentoo HOWTO For iSCSI-SCST

                            HOWTO For QLogic Target Driver

                            SCST SGV Cache Description

                            +

                            SCST user space device handler interface description

                            Articles

                            By Marc Smith:

                            Accelerating VDI Using SCST and SSDs

                            From 4696f90f077bf867fbf568b1009c758bce3119ad Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 08:08:51 +0000 Subject: [PATCH 117/128] ib_srpt: Merge r5990:6028 from trunk git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6030 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- srpt/README | 19 ++++++++++++++++++- srpt/src/ib_srpt.c | 35 ++++++++++++++--------------------- srpt/src/ib_srpt.h | 15 ++++----------- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/srpt/README b/srpt/README index b051f835f..72a5929db 100644 --- a/srpt/README +++ b/srpt/README @@ -380,12 +380,29 @@ Performance Notes - Target Side Performance Notes - Initiator Side ---------------------------------- +* Using multiple RDMA connections between initator and target results in a + significant performance improvement. To benefit from this feature, use + kernel 3.19 or later at the initiator side and enable scsi-mq either by + setting SCSI_MQ_DEFAULT=y in the kernel config or via the following command: + + echo Y > /sys/module/scsi_mod/parameters/use_blk_mq + + If the HCA model in your initiator system supports multiple MSI-X interrupts + the next step is either to stop the irqbalance service or to write a policy + script that stops irqbalance from modifying the IB interrupt CPU + affinity. Once this has been done spread the IB interrupts uniformly over + CPU cores via e.g. scripts/spread-mlx4-ib-interrupts. + + For more information about scsi-mq see also Michael Larabel, SCSI + Multi-Queue Performance Appears Great For Linux 3.17, Phoronix, June 18, + 2014 (http://www.phoronix.com/scan.php?page=news_item&px=MTcyMjk). + * Choose a proper value for the ib_srp kernel module parameter cmd_sg_entries. The default value 12 works well for buffered reads while the throughput for write-dominated workloads improves by changing this value into 255. One way to set this kernel module parameter is as follows: - echo options ib_srp cmd_sg_entries=255 >>/etc/modprobe.d/ib_srp.conf + echo options ib_srp cmd_sg_entries=255 >/etc/modprobe.d/ib_srp.conf * For multithreaded workloads using small block sizes changing rq_affinity into 2 improves IOPS significantly (Linux kernel 3.1 and later; see also diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 63a861e36..9cbfeb835 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -92,7 +92,6 @@ MODULE_LICENSE("Dual BSD/GPL"); */ static u64 srpt_service_guid; -/* List of srpt_device structures. */ static atomic_t srpt_device_count; #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) static unsigned long trace_flag = DEFAULT_SRPT_TRACE_FLAGS; @@ -200,7 +199,7 @@ static struct scst_tgt_template srpt_template; static void srpt_unregister_mad_agent(struct srpt_device *sdev); #ifdef CONFIG_SCST_PROC static void srpt_unregister_procfs_entry(struct scst_tgt_template *tgt); -#endif /*CONFIG_SCST_PROC*/ +#endif /* CONFIG_SCST_PROC */ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch, struct srpt_send_ioctx *ioctx); static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch); @@ -2043,12 +2042,13 @@ static void srpt_process_send_completion(struct ib_cq *cq, srpt_send_context); } else if (opcode == SRPT_RDMA_READ_LAST || opcode == SRPT_RDMA_WRITE_LAST) { - PRINT_INFO("RDMA t %d for idx %u failed with status %d." - "%s", opcode, index, wc->status, + PRINT_INFO("RDMA t %d for idx %u failed with status %d.%s", + opcode, index, wc->status, + wc->status == IB_WC_RETRY_EXC_ERR ? + " If this has not been triggered by a cable pull, please consider to increase the subnet timeout parameter on the IB switch." : wc->status == IB_WC_WR_FLUSH_ERR ? - " If this has not been triggered by a cable" - " pull, please check the involved IB HCA's" - " and cables." : ""); + " If this has not been triggered by a cable pull, please check the involved IB HCA's and cables." : + ""); srpt_handle_rdma_err_comp(ch, ch->ioctx_ring[index], opcode, srpt_xmt_rsp_context); } else if (opcode == SRPT_RDMA_ZEROLENGTH_WRITE) { @@ -2496,7 +2496,7 @@ static bool srpt_is_target_enabled(struct scst_tgt *scst_tgt) return srpt_tgt && srpt_tgt->enabled; } -#endif +#endif /* CONFIG_SCST_PROC */ /* * srpt_next_comp_vector() - Next completion vector >= srpt_tgt->comp_vector @@ -4199,7 +4199,7 @@ static const struct attribute *srpt_sess_attrs[] = { #endif NULL }; -#endif +#endif /* CONFIG_SCST_PROC */ /* SCST target template for the SRP target implementation. */ static struct scst_tgt_template srpt_template = { @@ -4250,7 +4250,7 @@ static struct scst_proc_data srpt_log_proc_data = { }; #endif -#endif /*CONFIG_SCST_PROC*/ +#endif /* CONFIG_SCST_PROC */ /* Note: the caller must have zero-initialized *@srpt_tgt. */ static void srpt_init_tgt(struct srpt_tgt *srpt_tgt) @@ -4596,7 +4596,7 @@ static void srpt_unregister_procfs_entry(struct scst_tgt_template *tgt) #endif } -#endif /*CONFIG_SCST_PROC*/ +#endif /* CONFIG_SCST_PROC */ /** * srpt_init_module() - Kernel module initialization. @@ -4706,14 +4706,14 @@ static int __init srpt_init_module(void) PRINT_ERROR("couldn't register procfs entry"); goto out_rdma_cm; } -#endif /*CONFIG_SCST_PROC*/ +#endif /* CONFIG_SCST_PROC */ return 0; #ifdef CONFIG_SCST_PROC out_rdma_cm: rdma_destroy_id(rdma_cm_id); -#endif /*CONFIG_SCST_PROC*/ +#endif /* CONFIG_SCST_PROC */ out_unregister_client: ib_unregister_client(&srpt_client); out_unregister_target: @@ -4731,7 +4731,7 @@ static void __exit srpt_cleanup_module(void) ib_unregister_client(&srpt_client); #ifdef CONFIG_SCST_PROC srpt_unregister_procfs_entry(&srpt_template); -#endif /*CONFIG_SCST_PROC*/ +#endif /* CONFIG_SCST_PROC */ scst_unregister_target_template(&srpt_template); TRACE_EXIT(); @@ -4739,10 +4739,3 @@ static void __exit srpt_cleanup_module(void) module_init(srpt_init_module); module_exit(srpt_cleanup_module); - -/* - * Local variables: - * c-basic-offset: 8 - * indent-tabs-mode: t - * End: - */ diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 30abefbeb..7d78a7d47 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -175,10 +175,10 @@ static inline u32 idx_from_wr_id(u64 wr_id) } struct rdma_iu { - u64 raddr; - u32 rkey; - struct ib_sge *sge; - u32 sge_cnt; + u64 raddr; + u32 rkey; + struct ib_sge *sge; + u32 sge_cnt; }; /** @@ -501,10 +501,3 @@ struct srp_login_req_rdma { }; #endif /* IB_SRPT_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * indent-tabs-mode: t - * End: - */ From 8fbfa14da69b77fd5ff934d3dd8d8005406ee335 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 08:10:08 +0000 Subject: [PATCH 118/128] scst_targ: Source code comment spellig fix (merge r6025 from trunk) git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6031 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/scst_targ.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 8980b9b5e..d6a8fb6d0 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -2853,7 +2853,7 @@ EXPORT_SYMBOL_GPL(__scst_check_local_events); * * !! At this point cmd can be processed in parallel by some other thread! * !! As consecuence, no pointer in cmd, except cur_order_data and - * !! sn_slot, can be touched here! The same is for aasignments to cmd's + * !! sn_slot, can be touched here! The same is for assignments to cmd's * !! fields. As protection cmd declared as const. * * Overall, cmd is passed here only for extra correctness checking. From 739c9025dbe84f54c47f5cc960e4d90dc830e2fe Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 08:11:35 +0000 Subject: [PATCH 119/128] scst_scsi_exec_async(): Fix a recently introduced memory leak (merge r6016 from trunk) git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6032 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/scst_lib.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 5bd950d06..8b9b4e9fb 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -6797,7 +6797,7 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data, if (!test_bit(QUEUE_FLAG_BIDI, &q->queue_flags)) { res = -EOPNOTSUPP; - goto out; + goto out_free_sioc; } rq = blk_map_kern_sg(q, cmd->out_sg, cmd->out_sg_cnt, gfp, @@ -6805,7 +6805,7 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data, if (IS_ERR(rq)) { res = PTR_ERR(rq); TRACE_DBG("blk_map_kern_sg() failed: %d", res); - goto out; + goto out_free_sioc; } next_rq = blk_map_kern_sg(q, cmd->sg, cmd->sg_cnt, gfp, false); @@ -6820,7 +6820,7 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data, if (IS_ERR(rq)) { res = PTR_ERR(rq); TRACE_DBG("blk_map_kern_sg() failed: %d", res); - goto out; + goto out_free_sioc; } } @@ -6861,6 +6861,9 @@ out_free_unmap: rq->bio = NULL; blk_put_request(rq); + +out_free_sioc: + kmem_cache_free(scsi_io_context_cache, sioc); goto out; } EXPORT_SYMBOL(scst_scsi_exec_async); From a565e4881957690e5a871d5f9b291b5efeb973d8 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 08:12:58 +0000 Subject: [PATCH 120/128] scst_vdisk: Source code comment spellig fixes (merge r6022:6024 from trunk) git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6033 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/dev_handlers/scst_vdisk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 165ec00cc..af66c288a 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -144,7 +144,7 @@ struct scst_vdisk_dev { /* * This lock can be taken on both SIRQ and thread context, but in - * all cases for each particular instance it's taken consistenly either + * all cases for each particular instance it's taken consistently either * on SIRQ or thread context. Mix of them is forbidden. */ spinlock_t flags_lock; @@ -3302,7 +3302,7 @@ static int vdisk_sup_vpd(uint8_t *buf, struct scst_cmd *cmd, *p++ = 0x86; /* extended inquiry */ if (cmd->dev->type == TYPE_DISK) { *p++ = 0xB0; /* block limits */ - *p++ = 0xB1; /* block device charachteristics */ + *p++ = 0xB1; /* block device characteristics */ if (virt_dev->thin_provisioned) { *p++ = 0xB2; /* thin provisioning */ } From 97711db2b6b359e0877219b1092f4e98a60a5862 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 08:14:28 +0000 Subject: [PATCH 121/128] iscsi-scst/usr/param.c: Remove an empty trailing line git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6034 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/usr/param.c | 1 - 1 file changed, 1 deletion(-) diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index 27a3f8ad1..8a3f1cc8a 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -391,4 +391,3 @@ struct iscsi_key session_keys[] = { {"MaxOutstandingUnexpectedPDUs", 0, 0, 0, -1, 0, &minimum_ops}, {NULL,}, }; - From d3e283bc444527ebe8f0b51bd920ff90dfb4f2e0 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 08:26:34 +0000 Subject: [PATCH 122/128] scst_debug.h: Remove duplicate pr_warn() definition A backport of pr_warn() is already present in so remove the pr_warn() definition from . git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6035 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/include/scst_debug.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index 11a4e5fff..d5b343241 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -67,15 +67,6 @@ printk(KERN_CONT fmt, ##__VA_ARGS__) #endif #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) -/* - * See also patch "kernel.h: add pr_warn for symmetry to dev_warn, - * netdev_warn" (commit fc62f2f19edf46c9bdbd1a54725b56b18c43e94f). - */ -#ifndef pr_warn -#define pr_warn pr_warning -#endif -#endif #if !defined(INSIDE_KERNEL_TREE) #ifdef CONFIG_SCST_DEBUG From 3c1a3ce2593ed4e7f8dc3072f3a3407229023b30 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 08:30:26 +0000 Subject: [PATCH 123/128] nightly.conf: Merge from trunk git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6036 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- nightly/conf/nightly.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index def42025c..2fbcbc4b6 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,15 +3,15 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.18.3 \ +3.18.5 \ 3.17.8-nc \ 3.16.7-nc \ 3.15.10-nc \ -3.14.29-nc \ +3.14.31-nc \ 3.13.11-nc \ 3.12.36-nc \ 3.11.10-nc \ -3.10.65-nc \ +3.10.67-nc \ 3.9.11-nc \ 3.8.13-nc \ 3.7.10-nc \ From 160e772a2fae0b3bc875b72934b647af87601524 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 10 Feb 2015 10:42:39 +0000 Subject: [PATCH 124/128] iSCSI-SCST: Include iSER source files in nightly build git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6037 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scripts/generate-kernel-patch | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/scripts/generate-kernel-patch b/scripts/generate-kernel-patch index dbcf623fe..23135edd8 100755 --- a/scripts/generate-kernel-patch +++ b/scripts/generate-kernel-patch @@ -458,9 +458,16 @@ make -s -C iscsi-scst include/iscsi_scst_itf_ver.h ( for f in $(ls iscsi-scst/include/*h 2>/dev/null) do - if [ "${f}" != "iscsi-scst/include/iscsi_scst_itf_ver.h" ]; then - add_file "${f}" "include/scst/${f#iscsi-scst/include/}" - fi + case "${f}" in + "iscsi-scst/include/iscsi_scst_itf_ver.h") + ;; + "iscsi-scst/include/iscsit_transport.h") + add_file "${f}" "drivers/scst/iscsi-scst/${f#iscsi-scst/include/}" + ;; + *) + add_file "${f}" "include/scst/${f#iscsi-scst/include/}" + ;; + esac done add_file "iscsi-scst/include/iscsi_scst_itf_ver.h" "include/scst/iscsi_scst_itf_ver.h" @@ -474,6 +481,11 @@ for f in $(ls iscsi-scst/kernel/*.[ch] 2>/dev/null) do add_file "${f}" "drivers/scst/iscsi-scst/${f#iscsi-scst/kernel/}" done + +for f in $(ls iscsi-scst/kernel/isert-scst/*.[ch] 2>/dev/null) +do + add_file "${f}" "drivers/scst/iscsi-scst/${f#iscsi-scst/kernel/isert-scst/}" +done ) \ | process_patch "iscsi-scst.diff" From 90faade1ccdaf07e5db29cdc01f0346af979b3c4 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 11 Feb 2015 13:05:20 +0000 Subject: [PATCH 125/128] isert: Fix case where we got disconnect before login character device was opened We may receive disconnect before iscsi-scstd had the chance to open the character device. This would result in resource leak, since the device will not be freed. Fix this by only increasing reference once the device is actually opened. Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6080 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 168d3834c..b18020f8a 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -356,7 +356,6 @@ wait_for_connection: conn_dev = list_first_entry(&dev->new_conn_list, struct isert_conn_dev, conn_list_entry); list_move(&conn_dev->conn_list_entry, &dev->curr_conn_list); - kref_get(&conn_dev->kref); spin_unlock(&dev->conn_lock); res = snprintf(k_buff, sizeof(k_buff), "/dev/"ISER_CONN_DEV_PREFIX"%d", @@ -485,6 +484,14 @@ static int isert_open(struct inode *inode, struct file *filp) dev = container_of(inode->i_cdev, struct isert_conn_dev, cdev); + spin_lock(&isert_listen_dev.conn_lock); + if (unlikely(dev->occupied == 0)) { + spin_unlock(&isert_listen_dev.conn_lock); + res = -ENODEV; /* already closed */ + goto out; + } + spin_unlock(&isert_listen_dev.conn_lock); + if (unlikely(!atomic_dec_and_test(&dev->available))) { atomic_inc(&dev->available); res = -EBUSY; /* already open */ @@ -493,6 +500,7 @@ static int isert_open(struct inode *inode, struct file *filp) spin_lock(&isert_listen_dev.conn_lock); list_del_init(&dev->conn_list_entry); + kref_get(&dev->kref); spin_unlock(&isert_listen_dev.conn_lock); filp->private_data = dev; /* for other methods */ From ea5e08e74b2bd7c485a6f229de5341fe28ffd5c6 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Wed, 11 Feb 2015 13:05:34 +0000 Subject: [PATCH 126/128] isert: Fix crash on service stop when under heavy login/logout load Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6081 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index b18020f8a..4455e9473 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -768,7 +768,7 @@ static unsigned int isert_poll(struct file *filp, poll_wait(filp, &dev->waitqueue, wait); - if (!dev->conn) + if (!dev->conn || dev->state == CS_DISCONNECTED) mask |= POLLHUP | POLLIN; else { if (!will_read_block(dev)) From ed3a17ca7a1928bb6d1d25c15751591c1ad381d3 Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Mon, 16 Feb 2015 08:59:32 +0000 Subject: [PATCH 127/128] isert: Fix crash on listener close while in login/logout loop Signed-off-by: Yan Burman git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6088 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/isert-scst/isert_login.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/iscsi-scst/kernel/isert-scst/isert_login.c b/iscsi-scst/kernel/isert-scst/isert_login.c index 4455e9473..a392679d3 100644 --- a/iscsi-scst/kernel/isert-scst/isert_login.c +++ b/iscsi-scst/kernel/isert-scst/isert_login.c @@ -100,8 +100,7 @@ static void isert_kref_release_dev(struct kref *kref) dev->occupied = 0; dev->state = CS_INIT; atomic_set(&dev->available, 1); - if (!list_is_singular(&dev->conn_list_entry)) - list_del_init(&dev->conn_list_entry); + list_del_init(&dev->conn_list_entry); dev->flags = 0; dev->conn = NULL; } @@ -499,7 +498,6 @@ static int isert_open(struct inode *inode, struct file *filp) } spin_lock(&isert_listen_dev.conn_lock); - list_del_init(&dev->conn_list_entry); kref_get(&dev->kref); spin_unlock(&isert_listen_dev.conn_lock); From 025574018e32b6b9e11a8dc8b761918c0aa8bcea Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 3 Mar 2015 11:25:37 +0000 Subject: [PATCH 128/128] isert: Support building against MOFED without patching the kernel build system Additionally, print OFED flavor name while building and embed the OFED flavor name in the module description. Signed-off-by: Bart Van Assche git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/iser@6139 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/Makefile | 7 ++++--- iscsi-scst/README.iser | 2 +- iscsi-scst/README.iser_ofed | 10 ---------- iscsi-scst/kernel/isert-scst/Makefile | 1 + iscsi-scst/kernel/isert-scst/isert.c | 3 ++- 5 files changed, 8 insertions(+), 15 deletions(-) diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index 50c10a0a4..e04eeccca 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -54,9 +54,9 @@ all: include/iscsi_scst_itf_ver.h progs mods ISER_SYMVERS:=$(KMOD)/Module.symvers OFED_CFLAGS:= -MLNX_OFED:=$(shell if ofed_info -s | grep MLNX >/dev/null 2>/dev/null; then echo true; else echo false; fi) +OFED_FLAVOR=$(shell if [ -e /usr/bin/ofed_info ]; then /usr/bin/ofed_info 2>/dev/null | head -n1 | sed -n 's/^\(MLNX_OFED\|OFED-internal\).*/MOFED/p;s/^OFED-.*/OFED/p'; else echo in-tree; fi) -ifeq ($(MLNX_OFED),true) +ifeq ($(OFED_FLAVOR),MOFED) # Whether MLNX_OFED for ubuntu has been installed MLNX_OFED_IB_UBUNTU_INSTALLED:=$(shell if dpkg -s mlnx-ofed-kernel-dkms >/dev/null 2>/dev/null; then echo true; else echo false; fi) @@ -100,8 +100,9 @@ else endif mods: Modules.symvers Module.symvers + echo " Building against $(OFED_FLAVOR) InfiniBand kernel headers." $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(KMOD) modules - $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) PRE_CFLAGS="$(OFED_CFLAGS)" KBUILD_EXTRA_SYMBOLS=$(ISER_SYMVERS) modules + $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) PRE_CFLAGS="$(OFED_CFLAGS) -DOFED_FLAVOR=$(OFED_FLAVOR)" KBUILD_EXTRA_SYMBOLS=$(ISER_SYMVERS) modules progs: $(MAKE) -C usr SCST_INC_DIR=$(SCST_INC_DIR) diff --git a/iscsi-scst/README.iser b/iscsi-scst/README.iser index 751213edc..a52d1a8b1 100644 --- a/iscsi-scst/README.iser +++ b/iscsi-scst/README.iser @@ -38,7 +38,7 @@ Troubleshooting: The cause of this is often compilation issues if you have OFED or MLNX_OFED installed: If you are compiling for OFED/MLNX_OFED, make sure OFED is installed for the kernel you are running. Also, make sure you followed ALL steps described - in README.iser_ofed including patching the kernel. + in README.iser_ofed. If you are compiling for non-OFED kernel, make sure you don't have OFED/MLNX_OFED installed. diff --git a/iscsi-scst/README.iser_ofed b/iscsi-scst/README.iser_ofed index fa578e558..7ff2a930e 100644 --- a/iscsi-scst/README.iser_ofed +++ b/iscsi-scst/README.iser_ofed @@ -58,16 +58,6 @@ Remove any distro-provided InfiniBand drivers: rm -rf /lib/modules/$(uname -r)/kernel/drivers/infiniband rm -rf /lib/modules/$(uname -r)/kernel/drivers/net/mlx4 -Now locate the file Makefile.lib and patch it such that it supports -the variable PRE_CFLAGS: - - if [ -e /lib/modules/$(uname -r)/build/scripts/Makefile.lib ]; then - cd /lib/modules/$(uname -r)/build - else - cd /usr/src/linux-$(uname -r) - fi - patch -p1 < ${SCST_DIR}/srpt/patches/kernel-${KV}-pre-cflags.patch - Next, download and install an OFED pacakge. For MLNX_OFED, just run the mlnxofedinstall script inside the MLNX_OFED directory. diff --git a/iscsi-scst/kernel/isert-scst/Makefile b/iscsi-scst/kernel/isert-scst/Makefile index 5c3cebda8..b2a08e8c0 100644 --- a/iscsi-scst/kernel/isert-scst/Makefile +++ b/iscsi-scst/kernel/isert-scst/Makefile @@ -24,6 +24,7 @@ cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) enable-Wextra = $(shell uname_r="$$(uname -r)"; if [ "$${uname_r%.el5}" = "$${uname_r}" ]; then echo "$(1)"; fi) +LINUXINCLUDE := $(PRE_CFLAGS) $(LINUXINCLUDE) EXTRA_CFLAGS += -I$(src)/../../include -I$(src)/../ -I$(SCST_INC_DIR) EXTRA_CFLAGS += $(call enable-Wextra,-Wextra \ $(call cc-option,-Wno-old-style-declaration) \ diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c index c81f903b3..21fd9658a 100644 --- a/iscsi-scst/kernel/isert-scst/isert.c +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -492,7 +492,8 @@ out: MODULE_AUTHOR("Yan Burman"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("iSER target transport driver"); +MODULE_DESCRIPTION("iSER target transport driver v3.0.1-pre#" + __stringify(OFED_FLAVOR)); module_init(isert_init_module); module_exit(isert_cleanup_module);