A bunch of pending changes:
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Mon, 25 Feb 2008 18:48:02 +0000 (18:48 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Mon, 25 Feb 2008 18:48:02 +0000 (18:48 +0000)
 - Response data send timeout implemented: now if initiator doesn't accept data for too long (7 sec), target closes connection
 - Connction closing improved: now session cleared without need to wait for all outstanding commands data fully transmitted
 - On session unregistration now all outstanding commands are implicitely aborted
 - TM processing in SCST core made independant from other TM commands (before they were serialized)
 - Few bug fixes
 - Other minor improvements and cleanups
 - Docs update

git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@295 d57e44dd-8a1f-0410-8b47-8ef2f437770f

21 files changed:
iscsi-scst/include/iscsi_u.h
iscsi-scst/kernel/conn.c
iscsi-scst/kernel/iscsi.c
iscsi-scst/kernel/iscsi.h
iscsi-scst/kernel/nthread.c
iscsi-scst/kernel/session.c
iscsi-scst/kernel/target.c
scst/README
scst/include/scsi_tgt.h
scst/include/scst_const.h
scst/include/scst_debug.h
scst/src/dev_handlers/scst_disk.c
scst/src/dev_handlers/scst_modisk.c
scst/src/dev_handlers/scst_tape.c
scst/src/dev_handlers/scst_user.c
scst/src/dev_handlers/scst_vdisk.c
scst/src/scst_lib.c
scst/src/scst_main.c
scst/src/scst_mem.c
scst/src/scst_priv.h
scst/src/scst_targ.c

index a574f6d..81a1f72 100644 (file)
@@ -20,7 +20,7 @@
 #include <sys/uio.h>
 #endif
 
-#define ISCSI_VERSION_STRING   "0.9.6/0.4.15r147"
+#define ISCSI_VERSION_STRING   "0.9.6/0.4.15r148"
 
 /* The maximum length of 223 bytes in the RFC. */
 #define ISCSI_NAME_LEN 256
index 5cabfb1..c05a4cf 100644 (file)
@@ -158,15 +158,21 @@ void iscsi_make_conn_wr_active(struct iscsi_conn *conn)
        return;
 }
 
-void mark_conn_closed(struct iscsi_conn *conn)
+void __mark_conn_closed(struct iscsi_conn *conn, bool force)
 {
        spin_lock_bh(&iscsi_rd_lock);
        conn->closing = 1;
+       conn->force_close = force;
        spin_unlock_bh(&iscsi_rd_lock);
 
        iscsi_make_conn_rd_active(conn);
 }
 
+void mark_conn_closed(struct iscsi_conn *conn)
+{
+       __mark_conn_closed(conn, 0);
+}
+
 static void iscsi_state_change(struct sock *sk)
 {
        struct iscsi_conn *conn = sk->sk_user_data;
@@ -178,7 +184,7 @@ static void iscsi_state_change(struct sock *sk)
                        PRINT_ERROR("Connection with initiator %s (%p) "
                                "unexpectedly closed!",
                                conn->session->initiator_name, conn);
-                       mark_conn_closed(conn);
+                       __mark_conn_closed(conn, 1);
                }
        } else
                iscsi_make_conn_rd_active(conn);
@@ -226,6 +232,45 @@ static void iscsi_write_space_ready(struct sock *sk)
        return;
 }
 
+static void conn_rsp_timer_fn(unsigned long arg)
+{
+       struct iscsi_conn *conn = (struct iscsi_conn *)arg;
+
+       TRACE_ENTRY();
+
+       TRACE_DBG("Timer (conn %p)", conn);
+
+       spin_lock_bh(&conn->write_list_lock);
+
+       if (!list_empty(&conn->written_list)) {
+               struct iscsi_cmnd *wr_cmd = list_entry(conn->written_list.next,
+                               struct iscsi_cmnd, write_list_entry);
+
+               if (unlikely(time_after_eq(jiffies, wr_cmd->write_timeout))) {
+                       if (!conn->closing) {
+                               PRINT_ERROR("Timeout sending data to initiator "
+                                       "%s (SID %Lx), closing connection",
+                                       conn->session->initiator_name,
+                                       conn->session->sid);
+                               __mark_conn_closed(conn, 1);
+                       }
+               } else {
+                       TRACE_DBG("Restarting timer on %ld (conn %p)",
+                               wr_cmd->write_timeout, conn);
+                       /*
+                        * Timer might have been restarted while we were
+                        * entering here.
+                        */
+                       mod_timer(&conn->rsp_timer, wr_cmd->write_timeout);
+               }
+       }
+
+       spin_unlock_bh(&conn->write_list_lock);
+
+       TRACE_EXIT();
+       return;
+}
+
 static int iscsi_socket_bind(struct iscsi_conn *conn)
 {
        int res = 0;
@@ -279,6 +324,8 @@ int conn_free(struct iscsi_conn *conn)
                conn->session, (unsigned long long)conn->session->sid,
                conn->cid);
 
+       del_timer_sync(&conn->rsp_timer);
+
        sBUG_ON(atomic_read(&conn->conn_ref_cnt) != 0);
        sBUG_ON(!list_empty(&conn->cmd_list));
        sBUG_ON(!list_empty(&conn->write_list));
@@ -337,6 +384,8 @@ static int iscsi_conn_alloc(struct iscsi_session *session, struct conn_info *inf
        INIT_LIST_HEAD(&conn->cmd_list);
        spin_lock_init(&conn->write_list_lock);
        INIT_LIST_HEAD(&conn->write_list);
+       INIT_LIST_HEAD(&conn->written_list);
+       setup_timer(&conn->rsp_timer, conn_rsp_timer_fn, (unsigned long)conn);
 
        conn->file = fget(info->fd);
 
index 0e2e5a7..fe55175 100644 (file)
@@ -179,7 +179,7 @@ void cmnd_free(struct iscsi_cmnd *cmnd)
 
        kfree(cmnd->pdu.ahs);
 
-       if (unlikely(cmnd->on_write_list)) {
+       if (unlikely(cmnd->on_write_list || cmnd->on_written_list)) {
                struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
 
                PRINT_ERROR("cmnd %p still on some list?, %x, %x, %x, %x, %x, %x, %x",
@@ -211,6 +211,16 @@ void cmnd_done(struct iscsi_cmnd *cmnd)
 
        EXTRACHECKS_BUG_ON(cmnd->on_rx_digest_list);
 
+       if (cmnd->on_written_list) {
+               struct iscsi_conn *conn = cmnd->conn;
+               TRACE_DBG("Deleting cmd %p from conn %p written_list", cmnd,
+                       conn);
+               spin_lock_bh(&conn->write_list_lock);
+               list_del(&cmnd->write_list_entry);
+               cmnd->on_written_list = 0;
+               spin_unlock_bh(&conn->write_list_lock);
+       }
+
        if (cmnd->parent_req == NULL) {
                struct iscsi_conn *conn = cmnd->conn;
                TRACE_DBG("Deleting req %p from conn %p", cmnd, conn);
@@ -305,7 +315,7 @@ void req_cmnd_release_force(struct iscsi_cmnd *req, int flags)
        sBUG_ON(req == conn->read_cmnd);
 
        if (flags & ISCSI_FORCE_RELEASE_WRITE) {
-               spin_lock(&conn->write_list_lock);
+               spin_lock_bh(&conn->write_list_lock);
                list_for_each_entry_safe(rsp, t, &conn->write_list,
                                                write_list_entry) {
                        if (rsp->parent_req != req)
@@ -315,7 +325,7 @@ void req_cmnd_release_force(struct iscsi_cmnd *req, int flags)
 
                        list_add_tail(&rsp->write_list_entry, &cmds_list);
                }
-               spin_unlock(&conn->write_list_lock);
+               spin_unlock_bh(&conn->write_list_lock);
 
                list_for_each_entry_safe(rsp, t, &cmds_list, write_list_entry) {
                        list_del(&rsp->write_list_entry);
@@ -338,18 +348,18 @@ again_rsp:
 
                spin_unlock_bh(&req->rsp_cmd_lock);
 
-               spin_lock(&conn->write_list_lock);
+               spin_lock_bh(&conn->write_list_lock);
                r = rsp->on_write_list || rsp->write_processing_started;
-               spin_unlock(&conn->write_list_lock);
+               spin_unlock_bh(&conn->write_list_lock);
 
                cmnd_put(rsp);
 
                if (r)
-                       continue;
+                       goto again_rsp;
 
                /*
                 * If both on_write_list and write_processing_started not set,
-                * we can safely put() cmnd
+                * we can safely put() rsp.
                 */
                cmnd_put(rsp);
                goto again_rsp;
@@ -488,6 +498,20 @@ static void iscsi_cmnds_init_write(struct list_head *send, int flags)
            (rsp->parent_req->outstanding_r2t == 0))
                cmnd_remove_hash(rsp->parent_req);
 
+       if (!(conn->ddigest_type & DIGEST_NONE)) {
+               list_for_each(pos, send) {
+                       rsp = list_entry(pos, struct iscsi_cmnd,
+                                               write_list_entry);
+
+                       if (rsp->pdu.datasize != 0) {
+                               TRACE_DBG("Doing data digest (%p:%x)", rsp,
+                                       cmnd_opcode(rsp));
+                               digest_tx_data(rsp);
+                       }
+               }
+       }
+
+       spin_lock_bh(&conn->write_list_lock);
        list_for_each_safe(pos, next, send) {
                rsp = list_entry(pos, struct iscsi_cmnd, write_list_entry);
 
@@ -495,16 +519,10 @@ static void iscsi_cmnds_init_write(struct list_head *send, int flags)
 
                sBUG_ON(conn != rsp->conn);
 
-               if (!(conn->ddigest_type & DIGEST_NONE) &&
-                   (rsp->pdu.datasize != 0))
-                       digest_tx_data(rsp);
-
                list_del(&rsp->write_list_entry);
-
-               spin_lock(&conn->write_list_lock);
                cmd_add_on_write_list(conn, rsp);
-               spin_unlock(&conn->write_list_lock);
        }
+       spin_unlock_bh(&conn->write_list_lock);
 
        if (flags & ISCSI_INIT_WRITE_WAKE)
                iscsi_make_conn_wr_active(conn);
@@ -1090,6 +1108,17 @@ static int iscsi_pre_exec(struct scst_cmd *scst_cmd)
 
        EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd));
 
+       if (scst_cmd_get_data_direction(scst_cmd) == SCST_DATA_READ) {
+               if (!(req->conn->ddigest_type & DIGEST_NONE))
+                       scst_set_long_xmit(scst_cmd);
+#ifndef NET_PAGE_CALLBACKS_DEFINED
+               else if (cmnd_hdr(req)->data_length > 8*1024)
+                       scst_set_long_xmit(scst_cmd);
+#endif
+               EXTRACHECKS_BUG_ON(!list_empty(&req->rx_ddigest_cmd_list));
+               goto out;
+       }
+
        /* If data digest isn't used this list will be empty */
        list_for_each_entry_safe(c, t, &req->rx_ddigest_cmd_list,
                                rx_ddigest_cmd_list_entry) {
@@ -2366,7 +2395,8 @@ static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
  * upon entrance in this function, because otherwise it could be destroyed
  * inside as a result of iscsi_send(), which releases sent commands.
  */
-static void iscsi_try_local_processing(struct iscsi_conn *conn)
+static void iscsi_try_local_processing(struct iscsi_conn *conn,
+       bool single_only)
 {
        int local;
 
@@ -2395,7 +2425,7 @@ static void iscsi_try_local_processing(struct iscsi_conn *conn)
                int rc = 1;
                while(test_write_ready(conn)) {
                        rc = iscsi_send(conn);
-                       if (rc <= 0) {
+                       if ((rc <= 0) || single_only) {
                                break;
                        }
                }
@@ -2427,6 +2457,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
        u8 *sense = scst_cmd_get_sense_buffer(scst_cmd);
        int sense_len = scst_cmd_get_sense_buffer_len(scst_cmd);
        int old_state = req->scst_state;
+       bool single_only = !scst_get_long_xmit(scst_cmd);
 
        scst_cmd_set_tgt_priv(scst_cmd, NULL);
 
@@ -2525,7 +2556,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
 
        conn_get_ordered(conn);
        req_cmnd_release(req);
-       iscsi_try_local_processing(conn);
+       iscsi_try_local_processing(conn, single_only);
        conn_put(conn);
 
 out:
@@ -2667,12 +2698,15 @@ static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
                                scst_mgmt_cmd_get_tgt_priv(scst_mcmd);
        int status = iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd));
 
-       TRACE_MGMT_DBG("req %p, scst_mcmd %p, scst status %d", req, scst_mcmd, 
+       TRACE_MGMT_DBG("req %p, scst_mcmd %p, fn %d, scst status %d",
+               req, scst_mcmd, scst_mgmt_cmd_get_fn(scst_mcmd),
                scst_mgmt_cmd_get_status(scst_mcmd));
 
        iscsi_send_task_mgmt_resp(req, status);
 
        scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL);
+
+       return;
 }
 
 static int iscsi_target_detect(struct scst_tgt_template *templ)
index a084e9d..7af76bb 100644 (file)
@@ -100,6 +100,7 @@ struct iscsi_session {
 
        /* All 3 protected by sn_lock */
        unsigned int tm_active:1;
+       unsigned int shutting_down:1; /* Let's save some cache footprint by putting it here */
        u32 tm_sn;
        struct iscsi_cmnd *tm_rsp;
 
@@ -113,6 +114,8 @@ struct iscsi_session {
 
        struct list_head session_list_entry;
 
+       struct completion unreg_compl;
+
        /* Both don't need any protection */
        char *initiator_name;
        u64 sid;
@@ -145,7 +148,11 @@ struct iscsi_conn {
 
        spinlock_t write_list_lock;
        /* List of data pdus to be sent, protected by write_list_lock */
-       struct list_head write_list; 
+       struct list_head write_list;
+       /* List of data pdus being sent, protected by write_list_lock */
+       struct list_head written_list;
+
+       struct timer_list rsp_timer;
 
        /* All 2 protected by iscsi_wr_lock */
        unsigned short wr_state;
@@ -182,6 +189,7 @@ struct iscsi_conn {
        unsigned short rd_state;
        unsigned short rd_data_ready:1;
        unsigned short closing:1; /* Let's save some cache footprint by putting it here */
+       unsigned short force_close:1; /* Let's save some cache footprint by putting it here */
 
        struct list_head rd_list_entry;
 
@@ -238,6 +246,7 @@ struct iscsi_cmnd {
        unsigned int force_cleanup_done:1;
        unsigned int dec_active_cmnds:1;
        unsigned int ddigest_checked:1;
+       unsigned int on_written_list:1;
 #ifdef EXTRACHECKS
        unsigned int on_rx_digest_list:1;
        unsigned int release_called:1;
@@ -262,6 +271,8 @@ struct iscsi_cmnd {
                struct list_head write_list_entry;
        };
 
+       unsigned long write_timeout;
+
        /*
         * Unprotected, since could be accessed from only a single 
         * thread at time
@@ -306,6 +317,8 @@ struct iscsi_cmnd {
 /* Flags for req_cmnd_release_force() */
 #define ISCSI_FORCE_RELEASE_WRITE      1
 
+#define ISCSI_RSP_TIMEOUT      (7*HZ)
+
 extern struct mutex target_mgmt_mutex;
 
 extern struct file_operations ctr_fops;
@@ -336,6 +349,7 @@ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
 extern int conn_add(struct iscsi_session *, struct conn_info *);
 extern int conn_del(struct iscsi_session *, struct conn_info *);
 extern int conn_free(struct iscsi_conn *);
+extern void __mark_conn_closed(struct iscsi_conn *, bool);
 extern void mark_conn_closed(struct iscsi_conn *);
 extern void iscsi_make_conn_wr_active(struct iscsi_conn *);
 extern void conn_info_show(struct seq_file *, struct iscsi_session *);
@@ -447,7 +461,7 @@ static inline void cmnd_put(struct iscsi_cmnd *cmnd)
                cmnd_done(cmnd);
 }
 
-/* conn->write_list_lock supposed to be locked */
+/* conn->write_list_lock supposed to be locked and BHs off */
 static inline void cmd_add_on_write_list(struct iscsi_conn *conn,
        struct iscsi_cmnd *cmnd)
 {
@@ -456,7 +470,7 @@ static inline void cmd_add_on_write_list(struct iscsi_conn *conn,
        cmnd->on_write_list = 1;
 }
 
-/* conn->write_list_lock supposed to be locked */
+/* conn->write_list_lock supposed to be locked and BHs off */
 static inline void cmd_del_from_write_list(struct iscsi_cmnd *cmnd)
 {
        TRACE_DBG("%p", cmnd);
index b05c34f..2058991 100644 (file)
@@ -165,6 +165,22 @@ out:
 static inline void iscsi_check_closewait(struct iscsi_conn *conn) {};
 #endif
 
+static void iscsi_unreg_cmds_done_fn(struct scst_session *scst_sess)
+{
+       struct iscsi_session *sess =
+               (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
+
+       TRACE_ENTRY();
+
+       TRACE_CONN_CLOSE("sess %p (scst_sess %p)", sess, scst_sess);
+
+       sess->shutting_down = 1;
+       complete_all(&sess->unreg_compl);
+
+       TRACE_EXIT();
+       return;
+}
+
 /* No locks */
 static void close_conn(struct iscsi_conn *conn)
 {
@@ -181,8 +197,13 @@ static void close_conn(struct iscsi_conn *conn)
 
        sBUG_ON(!conn->closing);
 
-       /* We want all our already send operations to complete */
-       conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN);
+       if (conn->force_close) {
+               conn->sock->ops->shutdown(conn->sock,
+                       RCV_SHUTDOWN|SEND_SHUTDOWN);
+       } else {
+               /* We want all our already send operations to complete */
+               conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN);
+       }
 
        /*
         * We need to call scst_unregister_session() ASAP to make SCST start
@@ -190,7 +211,8 @@ static void close_conn(struct iscsi_conn *conn)
         *
         * ToDo: this is incompatible with MC/S
         */
-       scst_unregister_session(session->scst_sess, 0, NULL);
+       scst_unregister_session_ex(session->scst_sess, 0,
+               NULL, iscsi_unreg_cmds_done_fn);
        session->scst_sess = NULL;
 
        if (conn->read_state != RX_INIT_BHS) {
@@ -269,8 +291,8 @@ static void close_conn(struct iscsi_conn *conn)
                        } while(req_freed);
                        spin_unlock(&session->sn_lock);
 
-                       if (time_after(jiffies, start_waiting + 5*HZ)) {
-                               TRACE_CONN_CLOSE("%s", "Wait time expired");
+                       if (time_after(jiffies, start_waiting + 10*HZ)) {
+                               TRACE_CONN_CLOSE("%s", "Pending wait time expired");
                                spin_lock(&session->sn_lock);
                                do {
                                        req_freed = 0;
@@ -305,6 +327,12 @@ static void close_conn(struct iscsi_conn *conn)
                }
 
                iscsi_make_conn_wr_active(conn);
+
+               if (time_after(jiffies, start_waiting + 7*HZ)) {
+                       TRACE_CONN_CLOSE("%s", "Wait time expired");
+                       conn->sock->ops->shutdown(conn->sock, SEND_SHUTDOWN);
+               }
+
                msleep(200);
 
                TRACE_CONN_CLOSE("conn %p, conn_ref_cnt %d left, wr_state %d, "
@@ -388,6 +416,8 @@ static void close_conn(struct iscsi_conn *conn)
        TRACE_CONN_CLOSE("Notifying user space about closing connection %p", conn);
        event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0);
 
+       wait_for_completion(&session->unreg_compl);
+
        mutex_lock(&target->target_mutex);
        conn_free(conn);
        /* ToDo: this is incompatible with MC/S */
@@ -453,14 +483,14 @@ static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn)
 {
        struct iscsi_cmnd *cmnd = NULL;
 
-       spin_lock(&conn->write_list_lock);
+       spin_lock_bh(&conn->write_list_lock);
        if (!list_empty(&conn->write_list)) {
                cmnd = list_entry(conn->write_list.next, struct iscsi_cmnd,
                                write_list_entry);
                cmd_del_from_write_list(cmnd);
                cmnd->write_processing_started = 1;
        }
-       spin_unlock(&conn->write_list_lock);
+       spin_unlock_bh(&conn->write_list_lock);
 
        return cmnd;
 }
@@ -862,6 +892,28 @@ static int write_data(struct iscsi_conn *conn)
        else
                ref_cmd = write_cmnd;
 
+       if (!ref_cmd->on_written_list) {
+               TRACE_DBG("Adding cmd %p to conn %p written_list", ref_cmd,
+                       conn);
+               spin_lock_bh(&conn->write_list_lock);
+               ref_cmd->on_written_list = 1;
+               ref_cmd->write_timeout = jiffies + ISCSI_RSP_TIMEOUT;
+               list_add_tail(&ref_cmd->write_list_entry, &conn->written_list);
+               spin_unlock_bh(&conn->write_list_lock);
+       }
+
+       if (!timer_pending(&conn->rsp_timer)) {
+               sBUG_ON(!ref_cmd->write_timeout);
+               spin_lock_bh(&conn->write_list_lock);
+               if (likely(!timer_pending(&conn->rsp_timer))) {
+                       TRACE_DBG("Starting timer on %ld (conn %p)",
+                               ref_cmd->write_timeout, conn);
+                       conn->rsp_timer.expires = ref_cmd->write_timeout;
+                       add_timer(&conn->rsp_timer);
+               }
+               spin_unlock_bh(&conn->write_list_lock);
+       }
+
        file = conn->file;
        saved_size = size = conn->write_size;
        iop = conn->write_iop;
index 1218035..6b8fcef 100644 (file)
@@ -22,7 +22,7 @@ struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
        struct iscsi_session *session;
 
        list_for_each_entry(session, &target->session_list, session_list_entry) {
-               if (session->sid == sid)
+               if ((session->sid == sid) && !session->shutting_down)
                        return session;
        }
        return NULL;
@@ -35,9 +35,6 @@ static int iscsi_session_alloc(struct iscsi_target *target, struct session_info
        struct iscsi_session *session;
        char *name = NULL;
 
-       TRACE_MGMT_DBG("Creating session: target %p, tid %u, sid %#Lx",
-               target, target->tid, (unsigned long long) info->sid);
-
        if (!(session = kzalloc(sizeof(*session), GFP_KERNEL)))
                return -ENOMEM;
 
@@ -91,8 +88,14 @@ static int iscsi_session_alloc(struct iscsi_target *target, struct session_info
 
        kfree(name);
 
+       scst_sess_set_tgt_priv(session->scst_sess, session);
+       init_completion(&session->unreg_compl);
+
        list_add(&session->session_list_entry, &target->session_list);
 
+       TRACE_MGMT_DBG("Session %p created: target %p, tid %u, sid %#Lx",
+               session, target, target->tid, (unsigned long long) info->sid);
+
        return 0;
 err:
        if (session) {
@@ -110,8 +113,11 @@ int session_add(struct iscsi_target *target, struct session_info *info)
        int err = -EEXIST;
 
        session = session_lookup(target, info->sid);
-       if (session)
+       if (session) {
+               PRINT_ERROR("Attempt to add session with existing SID %Lx",
+                       info->sid);
                return err;
+       }
 
        err = iscsi_session_alloc(target, info);
 
index 1f2f830..c0515dc 100644 (file)
@@ -264,6 +264,8 @@ void target_del_all(void)
 
        mutex_unlock(&target_mgmt_mutex);
 
+       TRACE_MGMT_DBG("%s", "Deleting all targets finished");
+
        TRACE_EXIT();
        return;
 }
index 533afcd..85ba096 100644 (file)
@@ -471,8 +471,11 @@ subdirectories "vdisk" and "vcdrom". They have similar layout:
 
     * "change NAME [PATH]" - changes a virtual CD in the VDISK CDROM.
 
+By default, if neither BLOCKIO, nor NULLIO option is supplied, FILEIO
+mode is used.
+
 For example, "echo "open disk1 /vdisks/disk1" >/proc/scsi_tgt/vdisk/vdisk"
-will open file /vdisks/disk1 as virtual VDISK disk with name "disk1".
+will open file /vdisks/disk1 as virtual FILEIO disk with name "disk1".
 
 IMPORTANT: By default for performance reasons VDISK FILEIO devices use write
 =========  back caching policy. This is generally safe from the consistence of
index 3905e58..cd314f2 100644 (file)
@@ -140,13 +140,16 @@ typedef _Bool bool;
  *************************************************************/
 
 /* LUN translation (mcmd->tgt_dev assignment) */
-#define SCST_MGMT_CMD_STATE_INIT     1
+#define SCST_MGMT_CMD_STATE_INIT     0
 
 /* Mgmt cmd is ready for processing */
-#define SCST_MGMT_CMD_STATE_READY    2
+#define SCST_MGMT_CMD_STATE_READY    1
 
 /* Mgmt cmd is being executing */
-#define SCST_MGMT_CMD_STATE_EXECUTING 3
+#define SCST_MGMT_CMD_STATE_EXECUTING 2
+
+/* Reservations are going to be cleared, if necessary */
+#define SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS 3
 
 /* Target driver's task_mgmt_fn_done() is going to be called */
 #define SCST_MGMT_CMD_STATE_DONE     4
@@ -167,15 +170,15 @@ typedef _Bool bool;
 
 /* 
  * Direct cmd's processing (i.e. regular function calls in the current 
- * context), sleeping is allowed, no restrictions
+ * context) sleeping is not allowed
  */
-#define SCST_CONTEXT_DIRECT          0
+#define SCST_CONTEXT_DIRECT_ATOMIC   0
 
 /* 
  * Direct cmd's processing (i.e. regular function calls in the current 
- * context) sleeping is not allowed
+ * context), sleeping is allowed, no restrictions
  */
-#define SCST_CONTEXT_DIRECT_ATOMIC   1
+#define SCST_CONTEXT_DIRECT          1
 
 /* Tasklet or thread context required for cmd's processing */
 #define SCST_CONTEXT_TASKLET         2
@@ -183,6 +186,14 @@ typedef _Bool bool;
 /* Thread context required for cmd's processing */
 #define SCST_CONTEXT_THREAD          3
 
+/*
+ * SCST internal flag, which specifies that context is processable, i.e. the
+ * next command in the active list will be processed after the current one.
+ *
+ * Target drivers must never use it!!
+ */
+#define SCST_CONTEXT_PROCESSABLE     0x100
+
 /************************************************************* 
  ** Values for status parameter of scst_rx_data() 
  *************************************************************/
@@ -677,6 +688,9 @@ struct scst_tgt_template
 
 struct scst_dev_type
 {
+       /* SCSI type of the supported device. MUST HAVE */
+       int type;
+
        /*
         * True, if corresponding function supports execution in
         * the atomic (non-sleeping) context
@@ -688,6 +702,9 @@ struct scst_dev_type
        /* Set, if no /proc files should be automatically created by SCST */
        unsigned no_proc:1;
 
+       /* Set, if exec() is synchronous */
+       unsigned exec_sync:1;
+
        /* 
         * Called to parse CDB from the cmd and initialize 
         * cmd->bufflen and cmd->data_direction (both - REQUIRED).
@@ -721,6 +738,10 @@ struct scst_dev_type
         * by scst_cmd_atomic(): it is true if the function called in the
         * atomic (non-sleeping) context.
         *
+        * If this function provides sync execution, you must set above
+        * exec_sync flag and should consider to setup dedicated threads by
+        * setting threads_num > 0.
+        *
         * !! If this function is implemented, scst_check_local_events() shall !!
         * !! be called inside it just before the actual command's execution.  !!
         *
@@ -808,9 +829,6 @@ struct scst_dev_type
        /* Name of the dev handler. Must be unique. MUST HAVE */
        char name[15];
 
-       /* SCSI type of the supported device. MUST HAVE */
-       int type;
-
        /*
         * Number of dedicated threads. If 0 - no dedicated threads will 
         * be created, if <0 - creation of dedicated threads is prohibited.
@@ -951,8 +969,9 @@ struct scst_session
        void (*init_result_fn) (struct scst_session *sess, void *data,
                                int result);
        void (*unreg_done_fn) (struct scst_session *sess);
+       void (*unreg_cmds_done_fn) (struct scst_session *sess);
 
-#ifdef MEASURE_LATENCY
+#ifdef MEASURE_LATENCY /* must be last */
        spinlock_t meas_lock;
        uint64_t scst_time, processing_time;
        unsigned int processed_cmds;
@@ -1003,6 +1022,12 @@ struct scst_cmd
        /* Set if cmd is being processed in atomic context */
        unsigned int atomic:1;
 
+       /*
+        * Set if the cmd is being processed in the processable context. See
+        * comment for SCST_CONTEXT_PROCESSABLE for what it means.
+        */
+       unsigned int context_processable:1;
+
        /* Set if cmd is internally generated */
        unsigned int internal:1;
 
@@ -1085,18 +1110,25 @@ struct scst_cmd
        /* Set if the cmd was done or aborted out of its SN */
        unsigned int out_of_sn:1;
 
-       /* Set if the cmd is deferred HEAD OF QUEUE */
-       unsigned int hq_deferred:1;
+       /* Set if increment expected_sn in cmd->scst_cmd_done() */
+       unsigned int inc_expected_sn_on_done:1;
 
        /*
-        * Set if increment expected_sn in cmd->scst_cmd_done() (to save
-        * extra dereferences)
+        * Set if xmit_response() is going to need a considerable processing
+        * time. Processing time is considerable, if it's > context switch time
+        * (about 1 usec on modern systems). It's needed to trigger other
+        * threads to start processing other outstanding commands without
+        * waiting XMIT for the current one to finish. E.g., it should be set
+        * if iSCSI data digest used and cmd has READ direction.
         */
-       unsigned int inc_expected_sn_on_done:1; 
+       unsigned int long_xmit:1;
 
        /* Set if tgt_sn field is valid */
        unsigned int tgt_sn_set:1;
 
+       /* Set if cmd is done */
+       unsigned int done:1;
+
        /* Set if cmd is finished */
        unsigned int finished:1;
 
@@ -1115,9 +1147,6 @@ struct scst_cmd
 
        lun_t lun;                      /* LUN for this cmd */
 
-       /* The corresponding mgmt cmd, if any, protected by sess_list_lock */
-       struct scst_mgmt_cmd *mgmt_cmnd;
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
        struct scsi_request *scsi_req;  /* SCSI request */
 #endif
@@ -1211,12 +1240,15 @@ struct scst_cmd
         */
        int orig_sg_cnt, orig_sg_entry, orig_entry_len;
 
+       /* List corresponding mgmt cmd, if any, protected by sess_list_lock */
+       struct list_head mgmt_cmd_list;
+
        /* List entry for dev's blocked_cmd_list */
        struct list_head blocked_cmd_list_entry;
 
        struct scst_cmd *orig_cmd; /* Used to issue REQUEST SENSE */
 
-#ifdef MEASURE_LATENCY
+#ifdef MEASURE_LATENCY /* must be last */
        uint64_t start, pre_exec_finish, post_exec_start;
 #endif
 };
@@ -1235,6 +1267,14 @@ struct scst_rx_mgmt_params
        unsigned char cmd_sn_set;
 };
 
+struct scst_mgmt_cmd_stub
+{
+       struct scst_mgmt_cmd *mcmd;
+
+       /* List entry in cmd->mgmt_cmd_list */
+       struct list_head cmd_mgmt_cmd_list_entry;
+};
+
 struct scst_mgmt_cmd
 {
        /* List entry for *_mgmt_cmd_list */
@@ -1253,12 +1293,20 @@ 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 nexus_loss_check_active:1; /* set, if nexus loss check is active */
+       unsigned int nexus_loss_check_done:1; /* set, if nexus loss check is done */
 
        /*
-        * Number of commands to complete before sending response,
+        * Number of commands to finish before sending response,
         * protected by scst_mcmd_lock
         */
-       int cmd_wait_count;
+       int cmd_finish_wait_count;
+
+       /*
+        * Number of commands to complete (done) before resetting reservation,
+        * protected by scst_mcmd_lock
+        */
+       int cmd_done_wait_count;
 
        /* Number of completed commands, protected by scst_mcmd_lock */
        int completed_cmd_count;
@@ -1633,6 +1681,11 @@ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic,
  *      the session is about to be completely freed. Can be NULL. 
  *      Parameter:
  *       - sess - session
+ *   unreg_cmds_done_fn - pointer to the function that will be 
+ *      asynchronously called when the last session's command completes, i.e.
+ *      goes to XMIT stage. Can be NULL. 
+ *      Parameter:
+ *       - sess - session
  *
  * Notes:
  *
@@ -1652,8 +1705,15 @@ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic,
  *   but it also starts recovering stuck commands, if there are any.
  *   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 scst_unregister_session_ex(struct scst_session *sess, int wait,
+       void (*unreg_done_fn) (struct scst_session *sess),
+       void (*unreg_cmds_done_fn) (struct scst_session *sess));
+
+static inline void scst_unregister_session(struct scst_session *sess, int wait,
+       void (*unreg_done_fn) (struct scst_session *sess))
+{
+       scst_unregister_session_ex(sess, wait, unreg_done_fn, NULL);
+}
 
 /* 
  * Registers dev handler driver
@@ -2220,6 +2280,24 @@ static inline void scst_set_delivery_status(struct scst_cmd *cmd,
        cmd->delivery_status = delivery_status;
 }
 
+/*
+ * Get/set/clear functions for cmd's long XMIT flag.
+ */
+static inline int scst_get_long_xmit(struct scst_cmd *cmd)
+{
+       return cmd->long_xmit;
+}
+
+static inline void scst_set_long_xmit(struct scst_cmd *cmd)
+{
+       cmd->long_xmit = 1;
+}
+
+static inline void scst_clear_long_xmit(struct scst_cmd *cmd)
+{
+       cmd->long_xmit = 0;
+}
+
 /*
  * Get/Set function for mgmt cmd's target private data
  */
@@ -2242,6 +2320,14 @@ 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;
+}
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
 
 static inline struct page *sg_page(struct scatterlist *sg)
@@ -2390,7 +2476,8 @@ void scst_resume_activity(void);
 /*
  * Main SCST commands processing routing. Must be used only by dev handlers.
  * Argument context sets the execution context, only SCST_CONTEXT_DIRECT and
- * SCST_CONTEXT_DIRECT_ATOMIC are allowed.
+ * SCST_CONTEXT_DIRECT_ATOMIC with optional SCST_CONTEXT_PROCESSABLE flag
+ * are allowed.
  */
 void scst_process_active_cmd(struct scst_cmd *cmd, int context);
 
index 31e42d4..4f6abee 100644 (file)
 /* Aborts all tasks in all sessions of the tgt */
 #define SCST_ABORT_ALL_TASKS         9
 
+/*
+ * Internal TM command issued by SCST in scst_unregister_session(). It is the
+ * same as SCST_NEXUS_LOSS_SESS, except it calls unreg_cmds_done_fn().
+ *
+ * Target driver shall NEVER use it!!
+ */
+#define SCST_UNREG_SESS_TM           10
+
 /************************************************************* 
  ** Values for mgmt cmd's status field. Codes taken from iSCSI
  *************************************************************/
index c5475cc..3184288 100644 (file)
@@ -93,7 +93,6 @@
 #ifdef DEBUG
 //#  define LOG_FLAG KERN_DEBUG
 #  define LOG_FLAG KERN_INFO
-
 #  define INFO_FLAG KERN_INFO
 #  define ERROR_FLAG KERN_INFO
 #else
 # define ERROR_FLAG KERN_ERR
 #endif
 
+#define CRIT_FLAG KERN_CRIT
+
 #define NO_FLAG ""
 
 #define TRACE_NULL           0x00000000
@@ -260,6 +261,16 @@ do {                                                                \
   PRINT_LOG_FLAG(ERROR_FLAG, "***ERROR*** " format, args);          \
 } while(0)
 
+#define PRINT_CRIT_ERROR(format, args...)                          \
+do {                                                                \
+  if (strcmp(CRIT_FLAG, LOG_FLAG))                                  \
+  {                                                                 \
+    PRINT_LOG_FLAG(LOG_FLAG, "***CRITICAL ERROR*** " format, args); \
+  }                                                                 \
+  PRINT_LOG_FLAG(CRIT_FLAG, "***CRITICAL ERROR*** " format, args);  \
+} while(0)
+
+
 #define PRINT_INFO(format, args...)                   \
 do {                                                  \
   if (strcmp(INFO_FLAG, LOG_FLAG))                    \
@@ -361,6 +372,12 @@ do {                                            \
         format, LOG_PREFIX, args);              \
 } while(0)
 
+#define PRINT_CRIT_ERROR(format, args...)       \
+do {                                            \
+  PRINT(CRIT_FLAG, "%s: ***CRITICAL ERROR*** "  \
+        format, LOG_PREFIX, args);              \
+} while(0)
+
 #else
 
 #define PRINT_INFO(format, args...)            \
@@ -374,6 +391,12 @@ do {                                            \
         format, args);                          \
 } while(0)
 
+#define PRINT_CRIT_ERROR(format, args...)       \
+do {                                            \
+  PRINT(CRIT_FLAG, "***CRITICAL ERROR*** "      \
+        format, args);                          \
+} while(0)
+
 #endif /* LOG_PREFIX */
 
 #endif /* DEBUG */
index 7d2754d..e9ad15c 100644 (file)
@@ -377,15 +377,17 @@ int disk_exec(struct scst_cmd *cmd)
        case READ_12:
        case READ_16:
                cmd->completed = 1;
-               break;
+               goto out_done;
        }
 
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
 out_done:
        res = SCST_EXEC_COMPLETED;
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
-
-       TRACE_EXIT_RES(res);
-       return res;
+       goto out;
 }
 
 MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
index c7edea3..2ba9f19 100644 (file)
@@ -392,15 +392,17 @@ int modisk_exec(struct scst_cmd *cmd)
        case READ_12:
        case READ_16:
                cmd->completed = 1;
-               break;
+               goto out_done;
        }
 
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
 out_done:
        res = SCST_EXEC_COMPLETED;
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
-
-       TRACE_EXIT_RES(res);
-       return res;
+       goto out;
 }
 
 MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
index 083d2db..1c39e6a 100644 (file)
@@ -416,15 +416,17 @@ int tape_exec(struct scst_cmd *cmd)
        case WRITE_6:
        case READ_6:
                cmd->completed = 1;
-               break;
+               goto out_done;
        }
 
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
 out_done:
        res = SCST_EXEC_COMPLETED;
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
-
-       TRACE_EXIT_RES(res);
-       return res;
+       goto out;
 }
 
 MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
index 45a5c21..80dfe58 100644 (file)
@@ -1463,7 +1463,8 @@ static int dev_user_process_scst_commands(struct scst_user_dev *dev)
                TRACE_DBG("Deleting cmd %p from active cmd list", cmd);
                list_del(&cmd->cmd_list_entry);
                spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-               scst_process_active_cmd(cmd, SCST_CONTEXT_DIRECT);
+               scst_process_active_cmd(cmd, SCST_CONTEXT_DIRECT |
+                                                SCST_CONTEXT_PROCESSABLE);
                spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
                res++;
        }
@@ -3101,16 +3102,15 @@ static int __init init_scst_user(void)
 
        dev_user_sysfs_class = class_create(THIS_MODULE, DEV_USER_NAME);
        if (IS_ERR(dev_user_sysfs_class)) {
-               printk(KERN_ERR "Unable create sysfs class for SCST user "
-                       "space handler\n");
+               PRINT_ERROR("%s", "Unable create sysfs class for SCST user "
+                       "space handler");
                res = PTR_ERR(dev_user_sysfs_class);
                goto out_proc;
        }
 
        res = register_chrdev(DEV_USER_MAJOR, DEV_USER_NAME, &dev_user_fops);
        if (res) {
-               printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
-                      DEV_USER_MAJOR);
+               PRINT_ERROR("Unable to get major %d for SCSI tapes", DEV_USER_MAJOR);
                goto out_class;
        }
 
index 30a8bdb..e1c8cbe 100644 (file)
@@ -256,6 +256,7 @@ static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
 #define VDISK_TYPE {                   \
   name:         VDISK_NAME,            \
   type:         TYPE_DISK,             \
+  exec_sync:   1,                      \
   threads_num: -1,                     \
   parse_atomic: 1,                     \
   exec_atomic:  0,                     \
@@ -291,6 +292,7 @@ static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
 #define VCDROM_TYPE {                  \
   name:         VCDROM_NAME,           \
   type:         TYPE_ROM,              \
+  exec_sync:   1,                      \
   threads_num: -1,                     \
   parse_atomic: 1,                     \
   exec_atomic:  0,                     \
@@ -1563,8 +1565,9 @@ static void vdisk_exec_mode_select(struct scst_cmd *cmd)
        }
 
        if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) {
-               PRINT_ERROR("MODE SELECT: PF and/or SP are wrongly set "
-                       "(cdb[1]=%x)", cmd->cdb[1]);
+               TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: Unsupported "
+                       "value(s) of PF and/or SP bits (cdb[1]=%x)",
+                       cmd->cdb[1]);
                scst_set_cmd_error(cmd,
                    SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
                goto out_put;
@@ -1852,17 +1855,12 @@ static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd)
 
        TRACE_DBG("PERSIST/PREVENT 0x%02x", cmd->cdb[4]);
 
-       spin_lock(&virt_dev->flags_lock);
-       if (cmd->dev->handler->type == TYPE_ROM)
+       if (cmd->dev->handler->type == TYPE_ROM) {
+               spin_lock(&virt_dev->flags_lock);
                virt_dev->prevent_allow_medium_removal = 
                        cmd->cdb[4] & 0x01 ? 1 : 0;
-       else {
-               PRINT_ERROR("%s", "Prevent allow medium removal for "
-                       "non-CDROM device");
-               scst_set_cmd_error(cmd,
-                       SCST_LOAD_SENSE(scst_sense_invalid_opcode));
+               spin_unlock(&virt_dev->flags_lock);
        }
-       spin_unlock(&virt_dev->flags_lock);
 
        return;
 }
index 147f913..86b40ad 100644 (file)
@@ -52,7 +52,7 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic)
 
        cmd->sense = mempool_alloc(scst_sense_mempool, gfp_mask);
        if (cmd->sense == NULL) {
-               PRINT_ERROR("FATAL!!! Sense memory allocation failed (op %x). "
+               PRINT_CRIT_ERROR("Sense memory allocation failed (op %x). "
                        "The sense data will be lost!!", cmd->cdb[0]);
                res = -ENOMEM;
                goto out;
@@ -1010,7 +1010,7 @@ static void scst_req_done(struct scsi_cmnd *scsi_cmd)
        return;
 }
 
-static void scst_send_release(struct scst_tgt_dev *tgt_dev)
+static void scst_send_release(struct scst_device *dev)
 {
        struct scsi_request *req;
        struct scsi_device *scsi_dev;
@@ -1018,10 +1018,10 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
 
        TRACE_ENTRY();
        
-       if (tgt_dev->dev->scsi_dev == NULL)
+       if (dev->scsi_dev == NULL)
                goto out;
 
-       scsi_dev = tgt_dev->dev->scsi_dev;
+       scsi_dev = dev->scsi_dev;
 
        req = scsi_allocate_request(scsi_dev, GFP_KERNEL);
        if (req == NULL) {
@@ -1042,7 +1042,7 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
        req->sr_use_sg = 0;
        req->sr_bufflen = 0;
        req->sr_buffer = NULL;
-       req->sr_request->rq_disk = tgt_dev->dev->rq_disk;
+       req->sr_request->rq_disk = dev->rq_disk;
        req->sr_sense_buffer[0] = 0;
 
        TRACE(TRACE_DEBUG | TRACE_SCSI, "Sending RELEASE req %p to SCSI "
@@ -1055,7 +1055,7 @@ out:
        return;
 }
 #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) */
-static void scst_send_release(struct scst_tgt_dev *tgt_dev)
+static void scst_send_release(struct scst_device *dev)
 {
        struct scsi_device *scsi_dev;
        unsigned char cdb[6];
@@ -1064,13 +1064,13 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
 
        TRACE_ENTRY();
        
-       if (tgt_dev->dev->scsi_dev == NULL)
+       if (dev->scsi_dev == NULL)
                goto out;
 
        /* We can't afford missing RELEASE due to memory shortage */
        sense = kmalloc(SCST_SENSE_BUFFERSIZE, GFP_KERNEL|__GFP_NOFAIL);
 
-       scsi_dev = tgt_dev->dev->scsi_dev;
+       scsi_dev = dev->scsi_dev;
 
        for(i = 0; i < 5; i++) {
                memset(cdb, 0, sizeof(cdb));
@@ -1092,7 +1092,7 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
                        PRINT_ERROR("RELEASE failed: %d", rc);
                        PRINT_BUFFER("RELEASE sense", sense,
                                SCST_SENSE_BUFFERSIZE);
-                       scst_check_internal_sense(tgt_dev->dev, rc,
+                       scst_check_internal_sense(dev, rc,
                                        sense, SCST_SENSE_BUFFERSIZE);
                }
        }
@@ -1124,11 +1124,12 @@ static void scst_clear_reservation(struct scst_tgt_dev *tgt_dev)
                                    &tgt_dev_tmp->tgt_dev_flags);
                }
                dev->dev_reserved = 0;
+               release = 1;
        }
        spin_unlock_bh(&dev->dev_lock);
 
        if (release)
-               scst_send_release(tgt_dev);
+               scst_send_release(dev);
 
        TRACE_EXIT();
        return;
@@ -1293,6 +1294,7 @@ struct scst_cmd *scst_alloc_cmd(int gfp_mask)
        cmd->state = SCST_CMD_STATE_INIT_WAIT;
        atomic_set(&cmd->cmd_ref, 1);
        cmd->cmd_lists = &scst_main_cmd_lists;
+       INIT_LIST_HEAD(&cmd->mgmt_cmd_list);
        cmd->queue_type = SCST_CMD_QUEUE_SIMPLE;
        cmd->timeout = SCST_DEFAULT_TIMEOUT;
        cmd->retries = 0;
@@ -1473,7 +1475,7 @@ struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(int gfp_mask)
 
        mcmd = mempool_alloc(scst_mgmt_mempool, gfp_mask);
        if (mcmd == NULL) {
-               PRINT_ERROR("%s", "Allocation of management command "
+               PRINT_CRIT_ERROR("%s", "Allocation of management command "
                        "failed, some commands and their data could leak");
                goto out;
        }
@@ -1555,6 +1557,7 @@ int scst_alloc_space(struct scst_cmd *cmd)
        int atomic = scst_cmd_atomic(cmd);
        int flags;
        struct scst_tgt_dev *tgt_dev = cmd->tgt_dev;
+       int bufflen = cmd->bufflen;
 
        TRACE_ENTRY();
 
@@ -1563,7 +1566,21 @@ int scst_alloc_space(struct scst_cmd *cmd)
        flags = atomic ? SCST_POOL_NO_ALLOC_ON_CACHE_MISS : 0;
        if (cmd->no_sgv)
                flags |= SCST_POOL_ALLOC_NO_CACHED;
-       cmd->sg = sgv_pool_alloc(tgt_dev->pool, cmd->bufflen, gfp_mask, flags,
+
+       if (unlikely(cmd->bufflen == 0)) {
+               TRACE(TRACE_MGMT_MINOR, "Data direction %d or/and zero buffer "
+                       "length. Opcode 0x%x, handler %s, target %s",
+                       cmd->data_direction, cmd->cdb[0],
+                       cmd->dev->handler->name, cmd->tgtt->name);
+               /*
+                * Be on the safe side and alloc stub buffer. Neither target
+                * drivers, nor user space will touch it, since bufflen
+                * remains 0.
+                */
+               bufflen = PAGE_SIZE;
+       }
+
+       cmd->sg = sgv_pool_alloc(tgt_dev->pool, bufflen, gfp_mask, flags,
                        &cmd->sg_cnt, &cmd->sgv, NULL);
        if (cmd->sg == NULL)
                goto out;
@@ -1749,16 +1766,6 @@ int scst_get_cdb_info(const uint8_t *cdb_p, int dev_type,
        *op_flags = ptr->flags;
        *transfer_len = (*ptr->get_trans_len)(cdb_p, ptr->off);
 
-#ifdef EXTRACHECKS
-       if (unlikely((*transfer_len == 0) &&
-                    (*direction != SCST_DATA_NONE) &&
-           ((*op_flags & SCST_UNKNOWN_LENGTH) == 0))) {
-               PRINT_ERROR("transfer_len 0, direction %d, flags %x, changing "
-                       "direction on NONE", *direction, *op_flags);
-               *direction = SCST_DATA_NONE;
-       }
-#endif
-
 out:
        TRACE_EXIT();
        return res;
@@ -2488,9 +2495,10 @@ void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev,
 
        UA_entry = mempool_alloc(scst_ua_mempool, GFP_ATOMIC);
        if (UA_entry == NULL) {
-               PRINT_ERROR("%s", "UNIT ATTENTION memory "
+               PRINT_CRIT_ERROR("%s", "UNIT ATTENTION memory "
                     "allocation failed. The UNIT ATTENTION "
                     "on some sessions will be missed");
+               PRINT_BUFFER("Lost UA", sense, sense_len);
                goto out;
        }
        memset(UA_entry, 0, sizeof(*UA_entry));
@@ -2609,6 +2617,9 @@ struct scst_cmd *__scst_check_deferred_commands(struct scst_tgt_dev *tgt_dev)
 
        spin_lock_irq(&tgt_dev->sn_lock);
 
+       if (unlikely(tgt_dev->hq_cmd_count != 0))
+               goto out_unlock;
+
 restart:
        list_for_each_entry_safe(cmd, t, &tgt_dev->deferred_cmd_list,
                                sn_cmd_list_entry) {
@@ -2935,16 +2946,14 @@ void scst_unblock_cmds(struct scst_device *dev)
        return;
 }
 
-static struct scst_cmd *__scst_unblock_deferred(
-       struct scst_tgt_dev *tgt_dev, struct scst_cmd *out_of_sn_cmd)
+static void __scst_unblock_deferred(struct scst_tgt_dev *tgt_dev,
+       struct scst_cmd *out_of_sn_cmd)
 {
-       struct scst_cmd *res = NULL;
-
        EXTRACHECKS_BUG_ON(!out_of_sn_cmd->sn_set);
 
        if (out_of_sn_cmd->sn == tgt_dev->expected_sn) {
                scst_inc_expected_sn(tgt_dev, out_of_sn_cmd->sn_slot);
-               res = scst_check_deferred_commands(tgt_dev);
+               scst_make_deferred_commands_active(tgt_dev, out_of_sn_cmd);
        } else {
                out_of_sn_cmd->out_of_sn = 1;
                spin_lock_irq(&tgt_dev->sn_lock);
@@ -2957,14 +2966,12 @@ static struct scst_cmd *__scst_unblock_deferred(
                spin_unlock_irq(&tgt_dev->sn_lock);
        }
 
-       return res;
+       return;
 }
 
 void scst_unblock_deferred(struct scst_tgt_dev *tgt_dev,
        struct scst_cmd *out_of_sn_cmd)
 {
-       struct scst_cmd *cmd;
-
        TRACE_ENTRY();
 
        if (!out_of_sn_cmd->sn_set) {
@@ -2972,16 +2979,7 @@ void scst_unblock_deferred(struct scst_tgt_dev *tgt_dev,
                goto out;
        }
 
-       cmd = __scst_unblock_deferred(tgt_dev, out_of_sn_cmd);
-       if (cmd != NULL) {
-               unsigned long flags;
-               spin_lock_irqsave(&cmd->cmd_lists->cmd_list_lock, flags);
-               TRACE_SN("cmd %p with sn %ld added to the head of active cmd "
-                       "list", cmd, cmd->sn);
-               list_add(&cmd->cmd_list_entry, &cmd->cmd_lists->active_cmd_list);
-               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-               spin_unlock_irqrestore(&cmd->cmd_lists->cmd_list_lock, flags);
-       }
+       __scst_unblock_deferred(tgt_dev, out_of_sn_cmd);
 
 out:
        TRACE_EXIT();
@@ -3005,18 +3003,8 @@ void scst_on_hq_cmd_response(struct scst_cmd *cmd)
         * non-locked state. In the worst case we will only have
         * unneeded run of the deferred commands.
         */
-       if (tgt_dev->hq_cmd_count == 0) {
-               struct scst_cmd *c =
-                       scst_check_deferred_commands(tgt_dev);
-               if (c != NULL) {
-                       spin_lock_irq(&c->cmd_lists->cmd_list_lock);
-                       TRACE_SN("Adding cmd %p to active cmd list", c);
-                       list_add_tail(&c->cmd_list_entry,
-                               &c->cmd_lists->active_cmd_list);
-                       wake_up(&c->cmd_lists->cmd_list_waitQ);
-                       spin_unlock_irq(&c->cmd_lists->cmd_list_lock);
-               }
-       }
+       if (tgt_dev->hq_cmd_count == 0)
+               scst_make_deferred_commands_active(tgt_dev, cmd);
 
        TRACE_EXIT();
        return;
@@ -3026,6 +3014,12 @@ void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd)
 {
        TRACE_ENTRY();
 
+       TRACE_MGMT_DBG("Aborted cmd %p done (cmd_ref %d, "
+               "scst_cmd_count %d)", cmd, atomic_read(&cmd->cmd_ref),
+               atomic_read(&scst_cmd_count));
+
+       scst_done_cmd_mgmt(cmd);
+
        smp_rmb();
        if (test_bit(SCST_CMD_ABORTED_OTHER, &cmd->cmd_flags)) {
                if (cmd->completed) {
index 1a1f7e2..40bd15b 100644 (file)
@@ -76,6 +76,8 @@ spinlock_t scst_main_lock;
 
 struct kmem_cache *scst_mgmt_cachep;
 mempool_t *scst_mgmt_mempool;
+struct kmem_cache *scst_mgmt_stub_cachep;
+mempool_t *scst_mgmt_stub_mempool;
 struct kmem_cache *scst_ua_cachep;
 mempool_t *scst_ua_mempool;
 struct kmem_cache *scst_sense_cachep;
@@ -1558,7 +1560,10 @@ static int __init init_scst(void)
        } while (0)
          
        INIT_CACHEP(scst_mgmt_cachep, scst_mgmt_cmd, out);
-       INIT_CACHEP(scst_ua_cachep, scst_tgt_dev_UA, out_destroy_mgmt_cache);
+       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);
        {
                struct scst_sense { uint8_t s[SCST_SENSE_BUFFERSIZE]; };
                INIT_CACHEP(scst_sense_cachep, scst_sense, out_destroy_ua_cache);
@@ -1568,18 +1573,25 @@ static int __init init_scst(void)
        INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev, out_destroy_sess_cache);
        INIT_CACHEP(scst_acgd_cachep, scst_acg_dev, out_destroy_tgt_cache);
 
-       scst_mgmt_mempool = mempool_create(10, mempool_alloc_slab,
+       scst_mgmt_mempool = mempool_create(64, mempool_alloc_slab,
                mempool_free_slab, scst_mgmt_cachep);
        if (scst_mgmt_mempool == NULL) {
                res = -ENOMEM;
                goto out_destroy_acg_cache;
        }
 
-       scst_ua_mempool = mempool_create(25, mempool_alloc_slab,
+       scst_mgmt_stub_mempool = mempool_create(1024, mempool_alloc_slab,
+               mempool_free_slab, scst_mgmt_stub_cachep);
+       if (scst_mgmt_stub_mempool == NULL) {
+               res = -ENOMEM;
+               goto out_destroy_mgmt_mempool;
+       }
+
+       scst_ua_mempool = mempool_create(64, mempool_alloc_slab,
                mempool_free_slab, scst_ua_cachep);
        if (scst_ua_mempool == NULL) {
                res = -ENOMEM;
-               goto out_destroy_mgmt_mempool;
+               goto out_destroy_mgmt_stub_mempool;
        }
 
        /* Loosing sense may have fatal consequences, so let's have a big pool */
@@ -1663,6 +1675,9 @@ out_destroy_sense_mempool:
 out_destroy_ua_mempool:
        mempool_destroy(scst_ua_mempool);
 
+out_destroy_mgmt_stub_mempool:
+       mempool_destroy(scst_mgmt_stub_mempool);
+
 out_destroy_mgmt_mempool:
        mempool_destroy(scst_mgmt_mempool);
 
@@ -1684,6 +1699,9 @@ out_destroy_sense_cache:
 out_destroy_ua_cache:
        kmem_cache_destroy(scst_ua_cachep);
 
+out_destroy_mgmt_stub_cache:
+       kmem_cache_destroy(scst_mgmt_stub_cachep);
+
 out_destroy_mgmt_cache:
        kmem_cache_destroy(scst_mgmt_cachep);
        goto out;
@@ -1715,10 +1733,12 @@ static void __exit exit_scst(void)
        } while (0)
 
        mempool_destroy(scst_mgmt_mempool);
+       mempool_destroy(scst_mgmt_stub_mempool);
        mempool_destroy(scst_ua_mempool);
        mempool_destroy(scst_sense_mempool);
 
        DEINIT_CACHEP(scst_mgmt_cachep);
+       DEINIT_CACHEP(scst_mgmt_stub_cachep);
        DEINIT_CACHEP(scst_ua_cachep);
        DEINIT_CACHEP(scst_sense_cachep);
        DEINIT_CACHEP(scst_cmd_cachep);
@@ -1760,7 +1780,7 @@ EXPORT_SYMBOL(scst_process_active_cmd);
  * Target Driver Side (i.e. HBA)
  */
 EXPORT_SYMBOL(scst_register_session);
-EXPORT_SYMBOL(scst_unregister_session);
+EXPORT_SYMBOL(scst_unregister_session_ex);
 
 EXPORT_SYMBOL(__scst_register_target_template);
 EXPORT_SYMBOL(scst_unregister_target_template);
index 65d446b..cfd969f 100644 (file)
@@ -493,7 +493,7 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
 {
        struct sgv_pool_obj *obj;
        int order, pages, cnt;
-       struct scatterlist *res;
+       struct scatterlist *res = NULL;
        int pages_to_alloc;
        struct kmem_cache *cache;
        int no_cached = flags & SCST_POOL_ALLOC_NO_CACHED;
@@ -501,7 +501,8 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
 
        TRACE_ENTRY();
 
-       sBUG_ON(size == 0);
+       if (unlikely(size == 0))
+               goto out;
 
        pages = ((size + PAGE_SIZE - 1) >> PAGE_SHIFT);
        order = get_order(size);
index 16b4460..f92857e 100644 (file)
@@ -96,9 +96,6 @@ extern unsigned long scst_trace_flag;
 /* Set if new commands initialization is suspended for a while */
 #define SCST_FLAG_SUSPENDED                 1
 
-/* Set if a TM command is being performed */
-#define SCST_FLAG_TM_ACTIVE                  2
-
 /** 
  ** Return codes for cmd state process functions 
  **/
@@ -139,6 +136,7 @@ static inline int scst_get_context(void)
 extern unsigned long scst_max_cmd_mem;
 
 extern mempool_t *scst_mgmt_mempool;
+extern mempool_t *scst_mgmt_stub_mempool;
 extern mempool_t *scst_ua_mempool;
 extern mempool_t *scst_sense_mempool;
 
@@ -228,6 +226,28 @@ static inline struct scst_cmd *scst_check_deferred_commands(
                return __scst_check_deferred_commands(tgt_dev);
 }
 
+static inline void scst_make_deferred_commands_active(
+       struct scst_tgt_dev *tgt_dev, struct scst_cmd *curr_cmd)
+{
+       struct scst_cmd *c;
+
+       c = __scst_check_deferred_commands(tgt_dev);
+       if (c != NULL) {
+               TRACE_SN("Adding cmd %p to active cmd list", c);
+
+               EXTRACHECKS_BUG_ON(c->cmd_lists != curr_cmd->cmd_lists);
+
+               spin_lock_irq(&c->cmd_lists->cmd_list_lock);
+               list_add_tail(&c->cmd_list_entry,
+                       &c->cmd_lists->active_cmd_list);
+               if (!curr_cmd->context_processable || curr_cmd->long_xmit)
+                       wake_up(&c->cmd_lists->cmd_list_waitQ);
+               spin_unlock_irq(&c->cmd_lists->cmd_list_lock);
+       }
+
+       return;
+}
+
 void scst_inc_expected_sn(struct scst_tgt_dev *tgt_dev, atomic_t *slot);
 int scst_check_hq_cmd(struct scst_cmd *cmd);
 
@@ -339,7 +359,7 @@ struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess,
 
 struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(int gfp_mask);
 void scst_free_mgmt_cmd(struct scst_mgmt_cmd *mcmd);
-void scst_complete_cmd_mgmt(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd);
+void scst_done_cmd_mgmt(struct scst_cmd *cmd);
 
 /* /proc support */
 int scst_proc_init_module(void);
index 1c5a9f5..42609a3 100644 (file)
@@ -33,6 +33,7 @@
 
 static void scst_cmd_set_sn(struct scst_cmd *cmd);
 static int __scst_init_cmd(struct scst_cmd *cmd);
+static void scst_finish_cmd_mgmt(struct scst_cmd *cmd);
 
 static inline void scst_schedule_tasklet(struct scst_cmd *cmd)
 {
@@ -49,7 +50,7 @@ static inline void scst_schedule_tasklet(struct scst_cmd *cmd)
 }
 
 /* 
- * Must not be called in parallel with scst_unregister_session() for the 
+ * Must not be called in parallel with scst_unregister_session_ex() for the 
  * same sess
  */
 struct scst_cmd *scst_rx_cmd(struct scst_session *sess,
@@ -316,8 +317,10 @@ static int scst_pre_parse(struct scst_cmd *cmd)
 
        TRACE_ENTRY();
 
-       cmd->inc_expected_sn_on_done = !dev->has_own_order_mgmt &&
-               (dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER);
+       cmd->inc_expected_sn_on_done = dev->handler->exec_sync ||
+               (!dev->has_own_order_mgmt &&
+                ((dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) ||
+                 (cmd->queue_type == SCST_CMD_QUEUE_ORDERED)));
 
        sBUG_ON(cmd->internal);
 
@@ -425,7 +428,6 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
        struct scst_device *dev = cmd->dev;
        int atomic = scst_cmd_atomic(cmd);
        int orig_bufflen = cmd->bufflen;
-       scst_data_direction orig_data_direction = cmd->data_direction;
 
        TRACE_ENTRY();
 
@@ -514,36 +516,35 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
                cmd->data_direction = cmd->expected_data_direction;
                cmd->bufflen = cmd->expected_transfer_len;
 #else
-               if (unlikely(cmd->data_direction != orig_data_direction)) {
+               if (unlikely(cmd->data_direction != cmd->expected_data_direction)) {
                        PRINT_ERROR("Expected data direction %d for opcode "
                                "0x%02x (handler %s, target %s) doesn't match "
                                "decoded value %d", cmd->data_direction,
                                cmd->cdb[0], dev->handler->name,
-                               cmd->tgtt->name, orig_data_direction);
+                               cmd->tgtt->name, cmd->expected_data_direction);
                        scst_set_cmd_error(cmd,
                                SCST_LOAD_SENSE(scst_sense_invalid_message));
                        goto out_dev_done;
                }
                if (unlikely(cmd->bufflen != cmd->expected_transfer_len)) {
-                       PRINT_INFO("Warning: expected transfer length %d for "
-                               "opcode 0x%02x (handler %s, target %s) doesn't "
-                               "match decoded value %d. Faulty initiator "
-                               "(e.g. VMware is known to be such) or "
+                       TRACE(TRACE_MINOR, "Warning: expected transfer length "
+                               "%d for opcode 0x%02x (handler %s, target %s) "
+                               "doesn't match decoded value %d. Faulty "
+                               "initiator (e.g. VMware is known to be such) or "
                                "scst_scsi_op_table should be updated?",
                                cmd->expected_transfer_len, cmd->cdb[0],
                                dev->handler->name, cmd->tgtt->name,
                                cmd->bufflen);
-                       PRINT_BUFFER("Suspicious CDB", cmd->cdb, cmd->cdb_len);
+                       PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb,
+                               cmd->cdb_len);
                }
 #endif
        }
 
-       if ((cmd->data_direction == SCST_DATA_UNKNOWN) ||
-           ((cmd->bufflen == 0) && (cmd->data_direction != SCST_DATA_NONE))) {
-               PRINT_ERROR("Wrong data direction (%d) or/and buffer "
-                       "length (%d). Opcode 0x%x, handler %s, target %s",
-                       cmd->data_direction, cmd->bufflen, cmd->cdb[0],
-                       dev->handler->name, cmd->tgtt->name);
+       if (unlikely(cmd->data_direction == SCST_DATA_UNKNOWN)) {
+               PRINT_ERROR("Unknown data direction. Opcode 0x%x, handler %s, "
+                       "target %s", cmd->cdb[0], dev->handler->name,
+                       cmd->tgtt->name);
                goto out_error;
        }
 
@@ -618,6 +619,11 @@ static int scst_prepare_space(struct scst_cmd *cmd)
                if (r > 0)
                        goto alloc;
                else if (r == 0) {
+                       if (unlikely(cmd->bufflen == 0)) {
+                               /* See comment in scst_alloc_space() */
+                               if (cmd->sg == NULL)
+                                       goto alloc;
+                       }
                        cmd->data_buf_alloced = 1;
                        if (unlikely(orig_bufflen < cmd->bufflen)) {
                                PRINT_ERROR("Target driver allocated data "
@@ -626,16 +632,13 @@ static int scst_prepare_space(struct scst_cmd *cmd)
                                        cmd->bufflen);
                                goto out_error;
                        }
-               } else
-                       goto check;
+                       TRACE_MEM("%s", "data_buf_alloced, returning");
+               }
+               goto check;
        }
 
 alloc:
-       if (!cmd->data_buf_alloced) {
-               r = scst_alloc_space(cmd);
-       } else {
-               TRACE_MEM("%s", "data_buf_alloced set, returning");
-       }
+       r = scst_alloc_space(cmd);
        
 check:
        if (r != 0) {
@@ -912,11 +915,13 @@ void scst_proccess_redirect_cmd(struct scst_cmd *cmd, int context,
 
        TRACE_ENTRY();
 
-       TRACE_DBG("Context: %d", context);
+       TRACE_DBG("Context: %x", context);
 
-       switch(context) {
-       case SCST_CONTEXT_DIRECT:
+       switch(context & ~SCST_CONTEXT_PROCESSABLE) {
        case SCST_CONTEXT_DIRECT_ATOMIC:
+               context &= ~SCST_CONTEXT_PROCESSABLE;
+               /* go through */
+       case SCST_CONTEXT_DIRECT:
                if (check_retries)
                        scst_check_retries(cmd->tgt);
                scst_process_active_cmd(cmd, context);
@@ -1188,6 +1193,8 @@ out:
 
 static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state)
 {
+       int context;
+
        TRACE_ENTRY();
 
 #ifdef MEASURE_LATENCY
@@ -1235,8 +1242,10 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state)
 #endif
        cmd->state = next_state;
 
-       scst_proccess_redirect_cmd(cmd,
-               scst_optimize_post_exec_context(cmd, scst_get_context()), 0);
+       context = scst_optimize_post_exec_context(cmd, scst_get_context());
+       if (cmd->context_processable)
+               context |= SCST_CONTEXT_PROCESSABLE;
+       scst_proccess_redirect_cmd(cmd, context, 0);
 
        TRACE_EXIT();
        return;
@@ -1668,14 +1677,16 @@ static inline int scst_local_exec(struct scst_cmd *cmd)
 static int scst_do_send_to_midlev(struct scst_cmd *cmd)
 {
        int rc = SCST_EXEC_NOT_COMPLETED;
+       struct scst_device *dev = cmd->dev;
+       struct scst_dev_type *handler = dev->handler;
 
        TRACE_ENTRY();
 
        /* Check here to let an out of SN cmd be queued w/o context switch */
-       if (scst_cmd_atomic(cmd) && !cmd->dev->handler->exec_atomic) {
+       if (scst_cmd_atomic(cmd) && !handler->exec_atomic) {
                TRACE_DBG("Dev handler %s exec() can not be "
                      "called in atomic context, rescheduling to the thread",
-                     cmd->dev->handler->name);
+                     handler->name);
                rc = SCST_EXEC_NEED_THREAD;
                goto out;
        }
@@ -1704,15 +1715,17 @@ static int scst_do_send_to_midlev(struct scst_cmd *cmd)
                        goto out_rc_error;
        }
 
-       if (cmd->dev->handler->exec) {
-               struct scst_device *dev = cmd->dev;
+       if (!handler->exec_sync)
+               cmd->context_processable = 0;
+
+       if (handler->exec) {
                TRACE_DBG("Calling dev handler %s exec(%p)",
-                     dev->handler->name, cmd);
+                     handler->name, cmd);
                TRACE_BUFF_FLAG(TRACE_SND_TOP, "Execing: ", cmd->cdb, cmd->cdb_len);
                cmd->scst_cmd_done = scst_cmd_done_local;
-               rc = dev->handler->exec(cmd);
+               rc = handler->exec(cmd);
                TRACE_DBG("Dev handler %s exec() returned %d",
-                     dev->handler->name, rc);
+                     handler->name, rc);
                if (rc == SCST_EXEC_COMPLETED)
                        goto out;
                else if (rc == SCST_EXEC_NEED_THREAD)
@@ -1723,7 +1736,7 @@ static int scst_do_send_to_midlev(struct scst_cmd *cmd)
 
        TRACE_DBG("Sending cmd %p to SCSI mid-level", cmd);
        
-       if (unlikely(cmd->dev->scsi_dev == NULL)) {
+       if (unlikely(dev->scsi_dev == NULL)) {
                PRINT_ERROR("Command for virtual device must be "
                        "processed by device handler (lun %Ld)!",
                        (uint64_t)cmd->lun);
@@ -1751,7 +1764,7 @@ static int scst_do_send_to_midlev(struct scst_cmd *cmd)
                    cmd->scsi_req->sr_bufflen, scst_cmd_done, cmd->timeout,
                    cmd->retries);
 #else
-       rc = scst_exec_req(cmd->dev->scsi_dev, cmd->cdb, cmd->cdb_len,
+       rc = scst_exec_req(dev->scsi_dev, cmd->cdb, cmd->cdb_len,
                        cmd->data_direction, cmd->sg, cmd->bufflen, cmd->sg_cnt,
                        cmd->timeout, cmd->retries, cmd, scst_cmd_done,
                        scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL);
@@ -1780,7 +1793,7 @@ out_clear:
 
 out_rc_error:
        PRINT_ERROR("Dev handler %s exec() or scst_local_exec() returned "
-                   "invalid code %d", cmd->dev->handler->name, rc);
+                   "invalid code %d", handler->name, rc);
        /* go through */
 
 out_error:
@@ -1953,6 +1966,7 @@ exec:
        count = 0;
        while(1) {
                atomic_t *slot = cmd->sn_slot;
+               /* For HQ commands SN is not set */
                int inc_expected_sn = !cmd->inc_expected_sn_on_done &&
                                      cmd->sn_set;
 
@@ -2359,21 +2373,10 @@ out:
 
 static void scst_inc_check_expected_sn(struct scst_cmd *cmd)
 {
-       struct scst_cmd *c;
-
        if (likely(cmd->sn_set))
                scst_inc_expected_sn(cmd->tgt_dev, cmd->sn_slot);
 
-       c = scst_check_deferred_commands(cmd->tgt_dev);
-       if (c != NULL) {
-               unsigned long flags;
-               spin_lock_irqsave(&c->cmd_lists->cmd_list_lock, flags);
-               TRACE_SN("Adding cmd %p to active cmd list", c);
-               list_add_tail(&c->cmd_list_entry,
-                       &c->cmd_lists->active_cmd_list);
-               wake_up(&c->cmd_lists->cmd_list_waitQ);
-               spin_unlock_irqrestore(&c->cmd_lists->cmd_list_lock, flags);
-       }
+       scst_make_deferred_commands_active(cmd->tgt_dev, cmd);
 }
 
 static int scst_dev_done(struct scst_cmd *cmd)
@@ -2503,6 +2506,9 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd)
        list_del(&cmd->search_cmd_list_entry);
        spin_unlock_irq(&cmd->sess->sess_list_lock);
 
+       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);
 
@@ -2662,16 +2668,11 @@ static int scst_finish_cmd(struct scst_cmd *cmd)
        smp_mb(); /* to sync with scst_abort_cmd() */
 
        if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
-               unsigned long flags;
-
                TRACE_MGMT_DBG("Aborted cmd %p finished (cmd_ref %d, "
                        "scst_cmd_count %d)", cmd, atomic_read(&cmd->cmd_ref),
                        atomic_read(&scst_cmd_count));
 
-               spin_lock_irqsave(&scst_mcmd_lock, flags);
-               if (cmd->mgmt_cmnd)
-                       scst_complete_cmd_mgmt(cmd, cmd->mgmt_cmnd);
-               spin_unlock_irqrestore(&scst_mcmd_lock, flags);
+               scst_finish_cmd_mgmt(cmd);
        }
 
        if (unlikely(cmd->delivery_status != SCST_CMD_DELIVERY_SUCCESS)) {
@@ -2808,6 +2809,7 @@ ordered:
                tgt_dev->cur_sn_slot-tgt_dev->sn_slots);
 
        cmd->sn_set = 1;
+
 out:
        return;
 }
@@ -3060,8 +3062,13 @@ void scst_process_active_cmd(struct scst_cmd *cmd, int context)
 
        EXTRACHECKS_BUG_ON(in_irq());
 
+       cmd->context_processable = context | SCST_CONTEXT_PROCESSABLE;
+       context &= ~SCST_CONTEXT_PROCESSABLE;
        cmd->atomic = (context == SCST_CONTEXT_DIRECT_ATOMIC);
 
+       TRACE_DBG("cmd %p, context_processable %d, atomic %d", cmd,
+               cmd->context_processable, cmd->atomic);
+
        do {
                switch (cmd->state) {
                case SCST_CMD_STATE_PRE_PARSE:
@@ -3189,8 +3196,11 @@ static void scst_do_job_active(struct list_head *cmd_list,
        TRACE_ENTRY();
 
 #ifdef EXTRACHECKS
-       WARN_ON((context != SCST_CONTEXT_DIRECT_ATOMIC) && 
-               (context != SCST_CONTEXT_DIRECT));
+       {
+               int c = context & ~SCST_CONTEXT_PROCESSABLE;
+               sBUG_ON((c != SCST_CONTEXT_DIRECT_ATOMIC) && 
+                       (c != SCST_CONTEXT_DIRECT));
+       }
 #endif
 
        while (!list_empty(cmd_list)) {
@@ -3255,7 +3265,8 @@ int scst_cmd_thread(void *arg)
                }
 
                scst_do_job_active(&p_cmd_lists->active_cmd_list,
-                       &p_cmd_lists->cmd_list_lock, SCST_CONTEXT_DIRECT);
+                       &p_cmd_lists->cmd_list_lock, SCST_CONTEXT_DIRECT |
+                                               SCST_CONTEXT_PROCESSABLE);
        }
        spin_unlock_irq(&p_cmd_lists->cmd_list_lock);
 
@@ -3337,45 +3348,104 @@ out:
        return res;
 }
 
-/* scst_mcmd_lock supposed to be held and IRQ off */
-void scst_complete_cmd_mgmt(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd)
+/* No locks */
+void scst_done_cmd_mgmt(struct scst_cmd *cmd)
 {
+       struct scst_mgmt_cmd_stub *mstb;
+       bool wake = 0;
+       unsigned long flags;
+
        TRACE_ENTRY();
 
-       TRACE_MGMT_DBG("cmd %p completed (tag %llu, mcmd %p, "
-               "mcmd->cmd_wait_count %d)", cmd, cmd->tag, mcmd,
-               mcmd->cmd_wait_count);
+       TRACE_MGMT_DBG("cmd %p done (tag %llu)", cmd, cmd->tag);
 
-       cmd->mgmt_cmnd = NULL;
+       spin_lock_irqsave(&scst_mcmd_lock, flags);
 
-       if (cmd->completed)
-               mcmd->completed_cmd_count++;
+       list_for_each_entry(mstb, &cmd->mgmt_cmd_list,
+                       cmd_mgmt_cmd_list_entry) {
+               struct scst_mgmt_cmd *mcmd = mstb->mcmd;
 
-       mcmd->cmd_wait_count--;
-       if (mcmd->cmd_wait_count > 0) {
-               TRACE_MGMT_DBG("cmd_wait_count(%d) not 0, skipping",
-                       mcmd->cmd_wait_count);
-               goto out_unlock;
-       }
+               TRACE_MGMT_DBG("mcmd %p, mcmd->cmd_done_wait_count %d", 
+                       mcmd, mcmd->cmd_done_wait_count);
 
-       mcmd->state = SCST_MGMT_CMD_STATE_DONE;
+               mcmd->cmd_done_wait_count--;
+               if (mcmd->cmd_done_wait_count > 0) {
+                       TRACE_MGMT_DBG("cmd_done_wait_count(%d) not 0, "
+                               "skipping", mcmd->cmd_done_wait_count);
+                       continue;
+               }
 
-       if (mcmd->completed) {
-               TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd list",
-                       mcmd);
-               list_add_tail(&mcmd->mgmt_cmd_list_entry,
-                       &scst_active_mgmt_cmd_list);
+               if (mcmd->completed) {
+                       sBUG_ON(mcmd->nexus_loss_check_done);
+                       mcmd->nexus_loss_check_active = 1;
+                       mcmd->state = SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS;
+                       TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd "
+                               "list", mcmd);
+                       list_add_tail(&mcmd->mgmt_cmd_list_entry,
+                               &scst_active_mgmt_cmd_list);
+                       wake = 1;
+               }
        }
 
-       wake_up(&scst_mgmt_cmd_list_waitQ);
+       spin_unlock_irqrestore(&scst_mcmd_lock, flags);
+
+       if (wake)
+               wake_up(&scst_mgmt_cmd_list_waitQ);
 
-out:
        TRACE_EXIT();
        return;
+}
 
-out_unlock:
-       spin_unlock_irq(&scst_mcmd_lock);
-       goto out;
+/* No locks */
+static void scst_finish_cmd_mgmt(struct scst_cmd *cmd)
+{
+       struct scst_mgmt_cmd_stub *mstb, *t;
+       bool wake = 0;
+       unsigned long flags;
+
+       TRACE_ENTRY();
+
+       TRACE_MGMT_DBG("cmd %p finished (tag %llu)", cmd, cmd->tag);
+
+       spin_lock_irqsave(&scst_mcmd_lock, flags);
+
+       list_for_each_entry_safe(mstb, t, &cmd->mgmt_cmd_list,
+                       cmd_mgmt_cmd_list_entry) {
+               struct scst_mgmt_cmd *mcmd = mstb->mcmd;
+
+               TRACE_MGMT_DBG("mcmd %p, mcmd->cmd_finish_wait_count %d", 
+                       mcmd, mcmd->cmd_finish_wait_count);
+
+               list_del(&mstb->cmd_mgmt_cmd_list_entry);
+               mempool_free(mstb, scst_mgmt_stub_mempool);
+
+               if (cmd->completed)
+                       mcmd->completed_cmd_count++;
+
+               mcmd->cmd_finish_wait_count--;
+               if (mcmd->cmd_finish_wait_count > 0) {
+                       TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, "
+                               "skipping", mcmd->cmd_finish_wait_count);
+                       continue;
+               }
+
+               if (mcmd->completed && !mcmd->nexus_loss_check_active) {
+                       mcmd->state = SCST_MGMT_CMD_STATE_DONE;
+                       TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd "
+                               "list", mcmd);
+                       list_add_tail(&mcmd->mgmt_cmd_list_entry,
+                               &scst_active_mgmt_cmd_list);
+                       wake = 1;
+               }
+       }
+
+       spin_unlock_irqrestore(&scst_mcmd_lock, flags);
+
+       if (wake)
+               wake_up(&scst_mgmt_cmd_list_waitQ);
+
+       TRACE_EXIT();
+       return;
 }
 
 static int scst_call_dev_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
@@ -3435,7 +3505,10 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
                clear_bit(SCST_CMD_ABORTED_OTHER, &cmd->cmd_flags);
        }
        set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags);
-       /* To sync with cmd->finished set in scst_finish_cmd() */
+       /* 
+        * To sync with cmd->finished/done set in
+        * scst_finish_cmd()/scst_pre_xmit_response()
+        */
        smp_mb__after_set_bit(); 
 
        if (cmd->tgt_dev == NULL) {
@@ -3453,6 +3526,23 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
 
        spin_lock_irqsave(&scst_mcmd_lock, flags);
        if ((mcmd != NULL) && !cmd->finished) {
+               struct scst_mgmt_cmd_stub *mstb;
+
+               mstb = mempool_alloc(scst_mgmt_stub_mempool, GFP_ATOMIC);
+               if (mstb == NULL) {
+                       PRINT_CRIT_ERROR("Allocation of management command "
+                               "stub failed (mcmd %p, cmd %p)", mcmd, cmd);
+                       goto unlock;
+               }
+               mstb->mcmd = mcmd;
+
+               /*
+                * cmd can't die here or sess_list_lock already taken and
+                * cmd is in the search list
+                */
+               list_add_tail(&mstb->cmd_mgmt_cmd_list_entry,
+                       &cmd->mgmt_cmd_list);
+
                /*
                 * Delay the response until the command's finish in
                 * order to guarantee that "no further responses from
@@ -3461,27 +3551,19 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
                 * we must wait here to be sure that we won't receive
                 * double commands with the same tag.
                 */
-               TRACE_MGMT_DBG("cmd %p (tag %llu) being executed/xmitted "
-                       "(state %d), deferring ABORT...", cmd, cmd->tag,
-                       cmd->state);
-#ifdef EXTRACHECKS
-               if (cmd->mgmt_cmnd) {
-                       printk(KERN_ALERT "cmd %p (tag %llu, state %d) "
-                               "has non-NULL mgmt_cmnd %p!!! Current "
-                               "mcmd %p\n", cmd, cmd->tag, cmd->state,
-                               cmd->mgmt_cmnd, mcmd);
-               }
-#endif
-               sBUG_ON(cmd->mgmt_cmnd);
+               TRACE_MGMT_DBG("cmd %p (tag %llu) being executed/"
+                       "xmitted (state %d), deferring ABORT...",
+                       cmd, cmd->tag, cmd->state);
 
-               mcmd->cmd_wait_count++;
+               mcmd->cmd_finish_wait_count++;
 
-               /*
-                * cmd can't die here or sess_list_lock already taken and cmd is
-                * in the search list
-                */
-               cmd->mgmt_cmnd = mcmd;
+               if (!cmd->done) {
+                       TRACE_MGMT_DBG("cmd %p (tag %llu) not done yet",
+                               cmd, cmd->tag);
+                       mcmd->cmd_done_wait_count++;
+               }
        }
+unlock:
        spin_unlock_irqrestore(&scst_mcmd_lock, flags);
 
        tm_dbg_release_cmd(cmd);
@@ -3494,17 +3576,31 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
 static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
 {
        int res;
+
        spin_lock_irq(&scst_mcmd_lock);
-       if (mcmd->cmd_wait_count != 0) {
-               TRACE_MGMT_DBG("cmd_wait_count(%d) not 0, preparing to "
-                       "wait", mcmd->cmd_wait_count);
+
+       if (mcmd->cmd_finish_wait_count == 0) {
+               if (!mcmd->nexus_loss_check_done)
+                       mcmd->state = SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS;
+               else
+                       mcmd->state = SCST_MGMT_CMD_STATE_DONE;
+               res = 0;
+       } else if ((mcmd->cmd_done_wait_count == 0) &&
+                  (!mcmd->nexus_loss_check_done)) {
+               mcmd->state = SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS;
+               res = 0;
+               goto out_unlock;
+       } else {
+               TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, preparing to "
+                       "wait", mcmd->cmd_finish_wait_count);
                mcmd->state = SCST_MGMT_CMD_STATE_EXECUTING;
                res = -1;
-       } else {
-               mcmd->state = SCST_MGMT_CMD_STATE_DONE;
-               res = 0;
        }
+
+       mcmd->nexus_loss_check_active = 0;
        mcmd->completed = 1;
+
+out_unlock:
        spin_unlock_irq(&scst_mcmd_lock);
        return res;
 }
@@ -3578,7 +3674,7 @@ static void scst_unblock_aborted_cmds(int scst_mutex_held)
 }
 
 static void __scst_abort_task_set(struct scst_mgmt_cmd *mcmd,
-       struct scst_tgt_dev *tgt_dev, int other_ini, int scst_mutex_held)
+       struct scst_tgt_dev *tgt_dev, int other_ini)
 {
        struct scst_cmd *cmd;
        struct scst_session *sess = tgt_dev->sess;
@@ -3604,8 +3700,6 @@ static void __scst_abort_task_set(struct scst_mgmt_cmd *mcmd,
        }
        spin_unlock_irq(&sess->sess_list_lock);
 
-       scst_unblock_aborted_cmds(scst_mutex_held);
-
        TRACE_EXIT();
        return;
 }
@@ -3626,7 +3720,10 @@ static int scst_abort_task_set(struct scst_mgmt_cmd *mcmd)
        __scst_block_dev(dev);
        spin_unlock_bh(&dev->dev_lock);
 
-       __scst_abort_task_set(mcmd, tgt_dev, 0, 0);
+       __scst_abort_task_set(mcmd, tgt_dev, 0);
+
+       scst_unblock_aborted_cmds(0);
+
        scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 0);
 
        res = scst_set_mcmd_next_state(mcmd);
@@ -3682,7 +3779,7 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
        __scst_block_dev(dev);
        spin_unlock_bh(&dev->dev_lock);
 
-       __scst_abort_task_set(mcmd, mcmd->mcmd_tgt_dev, 0, 0);
+       __scst_abort_task_set(mcmd, mcmd->mcmd_tgt_dev, 0);
 
        mutex_lock(&scst_mutex);
 
@@ -3714,9 +3811,9 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
                                        &UA_tgt_devs);
        }
 
-       mutex_unlock(&scst_mutex);
+       scst_unblock_aborted_cmds(1);
 
-       scst_unblock_aborted_cmds(0);
+       mutex_unlock(&scst_mutex);
 
        if (!dev->tas) {
                list_for_each_entry(tgt_dev, &UA_tgt_devs, extra_tgt_dev_list_entry) {
@@ -3736,23 +3833,6 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
        return res;
 }
 
-static int scst_check_delay_mgmt_cmd(struct scst_mgmt_cmd *mcmd)
-{
-       if (test_bit(SCST_FLAG_TM_ACTIVE, &scst_flags) && !mcmd->active) {
-               TRACE_MGMT_DBG("Adding mgmt cmd %p to delayed mgmt cmd list",
-                       mcmd);
-               spin_lock_irq(&scst_mcmd_lock);
-               list_add_tail(&mcmd->mgmt_cmd_list_entry, 
-                       &scst_delayed_mgmt_cmd_list);
-               spin_unlock_irq(&scst_mcmd_lock);
-               return -1;
-       } else {
-               mcmd->active = 1;
-               set_bit(SCST_FLAG_TM_ACTIVE, &scst_flags);
-               return 0;
-       }
-}
-
 /* Returns 0 if the command processing should be continued, 
  * >0, if it should be requeued, <0 otherwise */
 static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
@@ -3761,10 +3841,6 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
 
        TRACE_ENTRY();
 
-       res = scst_check_delay_mgmt_cmd(mcmd);
-       if (res != 0)
-               goto out;
-
        mcmd->state = SCST_MGMT_CMD_STATE_READY;
 
        switch (mcmd->fn) {
@@ -3895,6 +3971,8 @@ static int scst_target_reset(struct scst_mgmt_cmd *mcmd)
                tm_dbg_task_mgmt(dev, "TARGET RESET", 0);
        }
 
+       scst_unblock_aborted_cmds(1);
+
        /*
         * We suppose here that for all commands that already on devices
         * on/after scsi_reset_provider() completion callbacks will be called.
@@ -3964,6 +4042,8 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
                dev->scsi_dev->was_reset = 0;
        }
 
+       scst_unblock_aborted_cmds(0);
+
 out_tm_dbg:
        tm_dbg_task_mgmt(mcmd->mcmd_tgt_dev->dev, "LUN RESET", 0);
 
@@ -3973,6 +4053,28 @@ out_tm_dbg:
        return res;
 }
 
+/* scst_mutex supposed to be held */
+static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
+{
+       int i;
+       struct scst_session *sess = mcmd->sess;
+       struct scst_tgt_dev *tgt_dev;
+
+       TRACE_ENTRY();
+
+       for(i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+               struct list_head *sess_tgt_dev_list_head =
+                       &sess->sess_tgt_dev_list_hash[i];
+               list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
+                               sess_tgt_dev_list_entry) {
+                       scst_nexus_loss(tgt_dev);
+               }
+       }
+
+       TRACE_EXIT();
+       return;
+}
+
 /* 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)
@@ -3992,9 +4094,11 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
                        mcmd);
        }
 
-       mcmd->needs_unblocking = 1;
+       if (mcmd->fn != SCST_UNREG_SESS_TM)
+               mcmd->needs_unblocking = 1;
 
        mutex_lock(&scst_mutex);
+
        for(i = 0; i < TGT_DEV_HASH_SIZE; i++) {
                struct list_head *sess_tgt_dev_list_head =
                        &sess->sess_tgt_dev_list_hash[i];
@@ -4002,20 +4106,23 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
                                sess_tgt_dev_list_entry) {
                        struct scst_device *dev = tgt_dev->dev;
                        int rc;
+
+                       if (mcmd->fn != SCST_UNREG_SESS_TM) {
+                               spin_lock_bh(&dev->dev_lock);
+                               __scst_block_dev(dev);
+                               spin_unlock_bh(&dev->dev_lock);
+                       }
        
-                       spin_lock_bh(&dev->dev_lock);
-                       __scst_block_dev(dev);
-                       spin_unlock_bh(&dev->dev_lock);
-       
-                       __scst_abort_task_set(mcmd, tgt_dev, 0, 1);
-                       if (nexus_loss)
-                               scst_nexus_loss(tgt_dev);
+                       __scst_abort_task_set(mcmd, tgt_dev, 0);
        
                        rc = scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 0);
                        if ((rc < 0) && (mcmd->status == SCST_MGMT_STATUS_SUCCESS))
                                mcmd->status = rc;              
                }
        }
+
+       scst_unblock_aborted_cmds(1);
+
        mutex_unlock(&scst_mutex);
 
        res = scst_set_mcmd_next_state(mcmd);
@@ -4024,7 +4131,31 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
        return res;
 }
 
-/* Returns 0 if the command processing should be continued, <0 otherwise */
+/* scst_mutex supposed to be held */
+static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
+{
+       int i;
+       struct scst_tgt *tgt = mcmd->sess->tgt;
+       struct scst_session *sess;
+
+       TRACE_ENTRY();
+
+       list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
+               for(i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+                       struct list_head *sess_tgt_dev_list_head =
+                               &sess->sess_tgt_dev_list_hash[i];
+                       struct scst_tgt_dev *tgt_dev;
+                       list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
+                                       sess_tgt_dev_list_entry) {
+                               scst_nexus_loss(tgt_dev);
+                       }
+               }
+       }
+
+       TRACE_EXIT();
+       return;
+}
+
 static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
        int nexus_loss)
 {
@@ -4066,7 +4197,7 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
                                        sess_tgt_dev_list_entry) {
                                int rc;
 
-                               __scst_abort_task_set(mcmd, tgt_dev, 0, 1);
+                               __scst_abort_task_set(mcmd, tgt_dev, 0);
 
                                if (nexus_loss)
                                        scst_nexus_loss(tgt_dev);
@@ -4079,6 +4210,8 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
                }
        }
 
+       scst_unblock_aborted_cmds(1);
+
        mutex_unlock(&scst_mutex);
 
        res = scst_set_mcmd_next_state(mcmd);
@@ -4121,6 +4254,7 @@ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd)
                break;
 
        case SCST_NEXUS_LOSS_SESS:
+       case SCST_UNREG_SESS_TM:
                res = scst_abort_all_nexus_loss_sess(mcmd, 1);
                break;
 
@@ -4155,6 +4289,43 @@ out_done:
        goto out;
 }
 
+static int scst_mgmt_cmd_check_nexus_loss(struct scst_mgmt_cmd *mcmd)
+{
+       int res;
+
+       TRACE_ENTRY();
+
+       mutex_lock(&scst_mutex);
+
+       switch (mcmd->fn) {
+       case SCST_NEXUS_LOSS_SESS:
+       case SCST_UNREG_SESS_TM:
+               scst_do_nexus_loss_sess(mcmd);
+               break;
+
+       case SCST_NEXUS_LOSS:
+               scst_do_nexus_loss_tgt(mcmd);
+               break;
+       }
+
+       mutex_unlock(&scst_mutex);
+
+       if ((mcmd->fn == SCST_UNREG_SESS_TM) &&
+           (mcmd->sess->unreg_cmds_done_fn != NULL)) {
+               struct scst_session *sess = mcmd->sess;
+               TRACE_MGMT_DBG("Calling unreg_cmds_done_fn(%p)", sess);
+               sess->unreg_cmds_done_fn(sess);
+               TRACE_MGMT_DBG("task_mgmt_all_cmds_done(%p) returned", sess);
+       }
+
+       mcmd->nexus_loss_check_done = 1;
+
+       res = scst_set_mcmd_next_state(mcmd);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
 static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd)
 {
        struct scst_device *dev;
@@ -4162,18 +4333,6 @@ static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd)
 
        TRACE_ENTRY();
 
-       clear_bit(SCST_FLAG_TM_ACTIVE, &scst_flags);
-       spin_lock_irq(&scst_mcmd_lock);
-       if (!list_empty(&scst_delayed_mgmt_cmd_list)) {
-               struct scst_mgmt_cmd *m;
-               m = list_entry(scst_delayed_mgmt_cmd_list.next, typeof(*m),
-                               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);
-       }
-       spin_unlock_irq(&scst_mcmd_lock);
-
        mcmd->state = SCST_MGMT_CMD_STATE_FINISHED;
        if (scst_is_strict_mgmt_fn(mcmd->fn) && (mcmd->completed_cmd_count > 0))
                mcmd->status = SCST_MGMT_STATUS_TASK_NOT_EXIST;
@@ -4181,7 +4340,8 @@ static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd)
        TRACE(TRACE_MGMT_MINOR, "TM command fn %d finished, status %x",
                mcmd->fn, mcmd->status);
 
-       if (mcmd->sess->tgt->tgtt->task_mgmt_fn_done) {
+       if (mcmd->sess->tgt->tgtt->task_mgmt_fn_done &&
+           (mcmd->fn != SCST_UNREG_SESS_TM)) {
                TRACE_DBG("Calling target %s task_mgmt_fn_done()",
                      mcmd->sess->tgt->tgtt->name);
                mcmd->sess->tgt->tgtt->task_mgmt_fn_done(mcmd);
@@ -4266,6 +4426,11 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd)
                                goto out;
                        break;
 
+               case SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS:
+                       if (scst_mgmt_cmd_check_nexus_loss(mcmd))
+                               goto out;
+                       break;
+
                case SCST_MGMT_CMD_STATE_DONE:
                        scst_mgmt_cmd_send_done(mcmd);
                        break;
@@ -4383,8 +4548,11 @@ static struct scst_mgmt_cmd *scst_pre_rx_mgmt_cmd(struct scst_session
        }
 
        mcmd = scst_alloc_mgmt_cmd(atomic ? GFP_ATOMIC : GFP_KERNEL);
-       if (mcmd == NULL)
+       if (mcmd == NULL) {
+               PRINT_CRIT_ERROR("Lost TM fn %x, initiator %s", fn,
+                       sess->initiator_name);
                goto out;
+       }
 
        mcmd->sess = sess;
        mcmd->fn = fn;
@@ -4406,19 +4574,17 @@ static int scst_post_rx_mgmt_cmd(struct scst_session *sess,
 
        scst_sess_get(sess);
 
+       if (unlikely(sess->shut_phase != SCST_SESS_SPH_READY)) {
+               PRINT_ERROR("New mgmt cmd while shutting down the session %p "
+                       "shut_phase %ld", sess, sess->shut_phase);
+               sBUG();
+       }
+
        local_irq_save(flags);
 
        spin_lock(&sess->sess_list_lock);
        atomic_inc(&sess->sess_cmd_count);
 
-#ifdef EXTRACHECKS
-       if (unlikely(sess->shut_phase != SCST_SESS_SPH_READY)) {
-               PRINT_ERROR("%s",
-                       "New mgmt cmd while shutting down the session");
-               sBUG();
-       }
-#endif
-
        if (unlikely(sess->init_phase != SCST_SESS_IPH_READY)) {
                switch(sess->init_phase) {
                case SCST_SESS_IPH_INITING:
@@ -4459,7 +4625,7 @@ out_unlock:
 }
 
 /* 
- * Must not be called in parallel with scst_unregister_session() for the 
+ * Must not be called in parallel with scst_unregister_session_ex() for the 
  * same sess
  */
 int scst_rx_mgmt_fn(struct scst_session *sess,
@@ -4504,9 +4670,10 @@ int scst_rx_mgmt_fn(struct scst_session *sess,
                "TM fn %x", params->fn);
 
        TRACE_MGMT_DBG("sess=%p, tag_set %d, tag %Ld, lun_set %d, "
-               "lun=%Ld, cmd_sn_set %d, cmd_sn %d", sess,
+               "lun=%Ld, cmd_sn_set %d, cmd_sn %d, priv %p", sess,
                params->tag_set, params->tag, params->lun_set,
-               (uint64_t)mcmd->lun, params->cmd_sn_set, params->cmd_sn);
+               (uint64_t)mcmd->lun, params->cmd_sn_set, params->cmd_sn,
+               params->tgt_priv);
 
        if (scst_post_rx_mgmt_cmd(sess, mcmd) != 0)
                goto out_free;
@@ -4708,14 +4875,16 @@ out_free:
  * Must not be called in parallel with scst_rx_cmd() or 
  * scst_rx_mgmt_fn_*() for the same sess
  */
-void scst_unregister_session(struct scst_session *sess, int wait,
-       void (*unreg_done_fn) (struct scst_session *sess))
+void scst_unregister_session_ex(struct scst_session *sess, int wait,
+       void (*unreg_done_fn) (struct scst_session *sess),
+       void (*unreg_cmds_done_fn) (struct scst_session *sess))
 {
        unsigned long flags;
        struct completion *pc;
 #ifndef CONFIG_LOCKDEP
        DECLARE_COMPLETION(c);
 #endif
+       int rc, lun;
 
        TRACE_ENTRY();
 
@@ -4727,11 +4896,22 @@ void scst_unregister_session(struct scst_session *sess, int wait,
        pc = &c;
 #endif
 
+       sess->unreg_done_fn = unreg_done_fn;
+       sess->unreg_cmds_done_fn = unreg_cmds_done_fn;
+
+       /* Abort all outstanding commands and clear reservation, if necessary */
+       lun = 0;
+       rc = scst_rx_mgmt_fn_lun(sess, SCST_UNREG_SESS_TM,
+               (uint8_t*)&lun, sizeof(lun), SCST_ATOMIC, NULL);
+       if (rc != 0) {
+               PRINT_ERROR("SCST_UNREG_SESS_TM failed %d (sess %p)",
+                       rc, sess);
+       }
+
        sess->shut_phase = SCST_SESS_SPH_PRE_UNREG;
 
        spin_lock_irqsave(&scst_mgmt_lock, flags);
 
-       sess->unreg_done_fn = unreg_done_fn;
        if (wait)
                sess->shutdown_compl = pc;
 #ifdef CONFIG_LOCKDEP