- Call of pre_unreg_sess() moved to scst_mgmt_thread, because of scst_mutex deadlock...
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 14 Aug 2007 16:54:54 +0000 (16:54 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 14 Aug 2007 16:54:54 +0000 (16:54 +0000)
 - scst_check_local_events() cleanups
 - In scst_user notification about aborted commands added, user space interface changed
 - Other minor fixes and cleanups

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

12 files changed:
scst/include/scsi_tgt.h
scst/include/scst_user.h
scst/src/Makefile
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.c
scst/src/scst_lib.c
scst/src/scst_priv.h
scst/src/scst_targ.c

index eebe43e..7bbbb2b 100644 (file)
 /* Set if session is initialized and ready */
 #define SCST_SESS_IPH_READY          3
 
+/************************************************************* 
+ ** Session shutdown phases
+ *************************************************************/
+
+/* Set if session is initialized and ready */
+#define SCST_SESS_SPH_READY          0
+
+/* Set if session is on calling pre_unreg_sess() phase */
+#define SCST_SESS_SPH_PRE_UNREG      1
+
+/* Set if session is shutting down */
+#define SCST_SESS_SPH_SHUTDOWN       2
+
 /*************************************************************
  ** Cmd's async (atomic) flags 
  *************************************************************/
@@ -833,19 +846,26 @@ struct scst_tgt
 
 struct scst_session
 {
-       /* Initialization phase, one of SCST_SESS_IPH_* constants */
+       /*
+        * Initialization phase, one of SCST_SESS_IPH_* constants, protected by
+        * sess_list_lock
+        */
        int init_phase;
 
        atomic_t refcnt;                /* get/put counter */
 
-       /************************************************************* 
-        ** Session's flags. Serialized by scst_mgmt_lock
-        *************************************************************/
+       /**************************************************************/
 
-       /* Set if the session is shutting down */
-       unsigned int shutting_down:1;
+       /* Alive commands for this session. ToDo: make it part of the common IO flow control */
+       atomic_t sess_cmd_count;                
 
-       /**************************************************************/
+       spinlock_t sess_list_lock; /* protects search_cmd_list, etc */
+
+       /* 
+        * List of cmds in this session. Used to find a cmd in the
+        * session. Protected by sess_list_lock.
+        */
+       struct list_head search_cmd_list;
 
        /*
         * Hash list of tgt_dev's for this session, protected by scst_mutex
@@ -859,22 +879,11 @@ struct scst_session
        /* List entry for the sessions list inside ACG */
        struct list_head acg_sess_list_entry;
 
+       struct scst_tgt *tgt;   /* corresponding target */
+
        /* Used for storage of target driver private stuff */
        void *tgt_priv;
 
-       /* Alive commands for this session. ToDo: make it part of common IO flow control */
-       atomic_t sess_cmd_count;                
-
-       spinlock_t sess_list_lock; /* protects search_cmd_list, etc */
-
-       /* 
-        * List of cmds in this session. Used to find a cmd in the
-        * session. Protected by sess_list_lock.
-        */
-       struct list_head search_cmd_list;
-       
-       struct scst_tgt *tgt;   /* corresponding target */
-
        /* Name of attached initiator */
        const char *initiator_name;
 
@@ -882,7 +891,10 @@ struct scst_session
        struct list_head sess_list_entry;
 
        /* List entry for the list that keeps session, waiting for the init */
-       struct list_head sess_mgmt_list_entry;
+       struct list_head sess_init_list_entry;
+
+       /* List entry for the list that keeps session, waiting for the shutdown */
+       struct list_head sess_shut_list_entry;
 
        /* 
         * Lists of deffered during session initialization commands.
@@ -891,12 +903,16 @@ struct scst_session
        struct list_head init_deferred_cmd_list;
        struct list_head init_deferred_mcmd_list;
 
+       /*
+        * Shutdown phase, one of SCST_SESS_SPH_* constants, unprotected.
+        * Async. relating to init_phase, must be a separate variable, because
+        * session could be unregistered before async. registration is finished.
+        */
+       unsigned long shut_phase;
+
        /* Used if scst_unregister_session() called in wait mode */
        struct completion *shutdown_compl;
 
-       /* Used to push some unregister_session() works out of IRQ */
-       struct work_struct unreg_work;
-
        /*
         * Functions and data for user callbacks from scst_register_session()
         * and scst_unregister_session()
index dfcac09..1289886 100644 (file)
@@ -23,7 +23,7 @@
 
 #define DEV_USER_NAME                "scst_user"
 #define DEV_USER_PATH                  "/dev/"
-#define DEV_USER_VERSION               96
+#define DEV_USER_VERSION               961
 
 /* 
  * Chosen so sizeof(scst_user_sess) <= sizeof(scst_user_scsi_cmd_exec) 
@@ -95,7 +95,7 @@ struct scst_user_opt
 
 struct scst_user_dev_desc
 {
-       uint8_t version;
+       uint32_t version;
        uint8_t type;
        struct scst_user_opt opt;
        uint32_t block_size;
@@ -168,6 +168,7 @@ struct scst_user_scsi_on_free_cmd
        aligned_u64 pbuf;
        int32_t resp_data_len;
        uint8_t buffer_cached;
+       uint8_t aborted;
        uint8_t status;
 };
 
index 2bca091..5d46eee 100644 (file)
@@ -81,6 +81,12 @@ ifneq ($(MOD_VERS),)
        install -m 644 Module.symvers $(INSTALL_DIR_H)
 endif
        -depmod -a $(KVER)
+       @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
+       @echo "!!     Now don't forget to rebuild and reinstall all your     !!"
+       @echo "!!   target drivers, custom dev handlers and necessary user   !!"
+       @echo "!!   space applications. Otherwise, because of the versions   !!"
+       @echo "!!     mismatch, you could have many problems and crashes!    !!"
+       @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
 
 uninstall:
        cd $(DEV_HANDLERS_DIR) && $(MAKE) $@
index 0a6e587..b28ffe5 100644 (file)
@@ -350,12 +350,13 @@ int disk_exec(struct scst_cmd *cmd)
        TRACE_ENTRY();
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto out_compl;
-               else
-                       goto out_uncompl;
-       }
+       if (unlikely(rc != 0))
+               goto out_done;
+
+       cmd->status = 0;
+       cmd->msg_status = 0;
+       cmd->host_status = DID_OK;
+       cmd->driver_status = 0;
 
        switch (opcode) {
        case WRITE_6:
@@ -366,24 +367,16 @@ int disk_exec(struct scst_cmd *cmd)
        case READ_10:
        case READ_12:
        case READ_16:
-               goto out_compl;
+               cmd->completed = 1;
+               break;
        }
 
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-
-out_compl:
-       cmd->completed = 1;
-       cmd->status = 0;
-       cmd->msg_status = 0;
-       cmd->host_status = DID_OK;
-       cmd->driver_status = 0;
-
-out_uncompl:
+out_done:
        res = SCST_EXEC_COMPLETED;
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
-       goto out;
+
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
 MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
index 42f0a83..e1c0df9 100644 (file)
@@ -364,12 +364,13 @@ int modisk_exec(struct scst_cmd *cmd)
        TRACE_ENTRY();
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto out_compl;
-               else
-                       goto out_uncompl;
-       }
+       if (unlikely(rc != 0))
+               goto out_done;
+
+       cmd->status = 0;
+       cmd->msg_status = 0;
+       cmd->host_status = DID_OK;
+       cmd->driver_status = 0;
 
        switch (opcode) {
        case WRITE_6:
@@ -380,24 +381,16 @@ int modisk_exec(struct scst_cmd *cmd)
        case READ_10:
        case READ_12:
        case READ_16:
-               goto out_compl;
+               cmd->completed = 1;
+               break;
        }
 
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-
-out_compl:
-       cmd->completed = 1;
-       cmd->status = 0;
-       cmd->msg_status = 0;
-       cmd->host_status = DID_OK;
-       cmd->driver_status = 0;
-
-out_uncompl:
+out_done:
        res = SCST_EXEC_COMPLETED;
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
-       goto out;
+
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
 MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
index bd9a0d3..8850f67 100644 (file)
@@ -395,34 +395,27 @@ int tape_exec(struct scst_cmd *cmd)
        TRACE_ENTRY();
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto out_compl;
-               else
-                       goto out_uncompl;
-       }
-
-       switch (opcode) {
-       case WRITE_6:
-       case READ_6:
-               goto out_compl;
-       }
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
+       if (unlikely(rc != 0))
+               goto out_done;
 
-out_compl:
-       cmd->completed = 1;
        cmd->status = 0;
        cmd->msg_status = 0;
        cmd->host_status = DID_OK;
        cmd->driver_status = 0;
 
-out_uncompl:
+       switch (opcode) {
+       case WRITE_6:
+       case READ_6:
+               cmd->completed = 1;
+               break;
+       }
+
+out_done:
        res = SCST_EXEC_COMPLETED;
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
-       goto out;
+
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
 MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
index 7aad5e2..e8283af 100644 (file)
@@ -124,6 +124,7 @@ struct dev_user_cmd
        unsigned int buf_dirty:1;
        unsigned int background_exec:1;
        unsigned int internal_reset_tm:1;
+       unsigned int aborted:1;
 
        struct dev_user_cmd *buf_ucmd;
 
@@ -842,6 +843,7 @@ static void dev_user_on_free_cmd(struct scst_cmd *cmd)
        ucmd->user_cmd.on_free_cmd.pbuf = ucmd->ubuff;
        ucmd->user_cmd.on_free_cmd.resp_data_len = cmd->resp_data_len;
        ucmd->user_cmd.on_free_cmd.buffer_cached = ucmd->buff_cached;
+       ucmd->user_cmd.on_free_cmd.aborted = ucmd->aborted;
        ucmd->user_cmd.on_free_cmd.status = cmd->status;
 
        ucmd->state = UCMD_STATE_ON_FREEING;
@@ -1251,6 +1253,7 @@ static int dev_user_process_reply_exec(struct dev_user_cmd *ucmd,
 out_compl:
        cmd->completed = 1;
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
+       /* !! At this point cmd can be already freed !! */
 
 out:
        TRACE_EXIT_RES(res);
@@ -1445,9 +1448,10 @@ static int dev_user_process_scst_commands(struct scst_user_dev *dev)
 
 struct dev_user_cmd *__dev_user_get_next_cmd(struct list_head *cmd_list)
 {
-       struct dev_user_cmd *u = NULL;
+       struct dev_user_cmd *u;
 
 again:
+       u = NULL;
        if (!list_empty(cmd_list)) {
                u = list_entry(cmd_list->next, typeof(*u), ready_cmd_list_entry);
                TRACE_DBG("Found ready ucmd %p", u);
@@ -1457,12 +1461,17 @@ again:
                        if (u->state == UCMD_STATE_EXECING) {
                                int rc = scst_check_local_events(u->cmd);
                                if (unlikely(rc != 0)) {
-                                       if (rc > 0) {
-                                               u->cmd->completed = 1;
-                                               u->cmd->scst_cmd_done(
-                                                       u->cmd, SCST_CMD_STATE_DEFAULT);
-                                       } else
-                                               dev_user_unjam_cmd(u, 0, NULL);
+                                       struct scst_user_dev *dev = u->dev;
+                                       spin_unlock_irq(
+                                               &dev->cmd_lists.cmd_list_lock);
+                                       u->cmd->scst_cmd_done(u->cmd,
+                                               SCST_CMD_STATE_DEFAULT);
+                                       /* 
+                                        * !! At this point cmd & u can be !!
+                                        * !! already freed                !! 
+                                        */
+                                       spin_lock_irq(
+                                               &dev->cmd_lists.cmd_list_lock);
                                        goto again;
                                }
                        } else if (unlikely(test_bit(SCST_CMD_ABORTED,
@@ -1470,10 +1479,11 @@ again:
                                switch(u->state) {
                                case UCMD_STATE_PARSING:
                                case UCMD_STATE_BUF_ALLOCING:
-                               case UCMD_STATE_EXECING:
                                        TRACE_MGMT_DBG("Aborting ucmd %p", u);
                                        dev_user_unjam_cmd(u, 0, NULL);
                                        goto again;
+                               case UCMD_STATE_EXECING:
+                                       EXTRACHECKS_BUG_ON(1);
                                }
                        }
                }
@@ -1819,11 +1829,15 @@ static void dev_user_unjam_cmd(struct dev_user_cmd *ucmd, int busy,
        switch(state) {
        case UCMD_STATE_PARSING:
        case UCMD_STATE_BUF_ALLOCING:
-               if (busy)
-                       scst_set_busy(ucmd->cmd);
-               else
-                       scst_set_cmd_error(ucmd->cmd,
-                               SCST_LOAD_SENSE(scst_sense_hardw_error));
+               if (test_bit(SCST_CMD_ABORTED, &ucmd->cmd->cmd_flags))
+                       ucmd->aborted = 1;
+               else {
+                       if (busy)
+                               scst_set_busy(ucmd->cmd);
+                       else
+                               scst_set_cmd_error(ucmd->cmd,
+                                       SCST_LOAD_SENSE(scst_sense_hardw_error));
+               }
                TRACE_MGMT_DBG("Adding ucmd %p to active list", ucmd);
                list_add(&ucmd->cmd->cmd_list_entry,
                        &ucmd->cmd->cmd_lists->active_cmd_list);
@@ -1835,15 +1849,23 @@ static void dev_user_unjam_cmd(struct dev_user_cmd *ucmd, int busy,
                        spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, *flags);
                else
                        spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-               if (busy)
-                       scst_set_busy(ucmd->cmd);
-               else
-                       scst_set_cmd_error(ucmd->cmd,
-                               SCST_LOAD_SENSE(scst_sense_hardw_error));
+               
                TRACE_MGMT_DBG("EXEC: unjamming ucmd %p", ucmd);
-               if (!test_bit(SCST_CMD_ABORTED, &ucmd->cmd->cmd_flags))
+
+               if (test_bit(SCST_CMD_ABORTED,  &ucmd->cmd->cmd_flags))
+                       ucmd->aborted = 1;
+               else {
                        ucmd->cmd->completed = 1;
+                       if (busy)
+                               scst_set_busy(ucmd->cmd);
+                       else
+                               scst_set_cmd_error(ucmd->cmd,
+                                       SCST_LOAD_SENSE(scst_sense_hardw_error));
+               }
+
                ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT);
+               /* !! At this point cmd ans ucmd can be already freed !! */
+
                if (flags != NULL)
                        spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, *flags);
                else
index 9332eff..0469d1e 100644 (file)
@@ -652,12 +652,8 @@ static int vdisk_do_job(struct scst_cmd *cmd)
        TRACE_ENTRY();
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto done;
-               else
-                       goto done_uncompl;
-       }
+       if (unlikely(rc != 0))
+               goto out_done;
 
        cmd->status = 0;
        cmd->msg_status = 0;
@@ -669,7 +665,7 @@ static int vdisk_do_job(struct scst_cmd *cmd)
                thr = vdisk_init_thr_data(cmd->tgt_dev);
                if (thr == NULL) {
                        scst_set_busy(cmd);
-                       goto done;
+                       goto out_compl;
                }
                scst_thr_data_get(&thr->hdr);
        } else
@@ -734,7 +730,7 @@ static int vdisk_do_job(struct scst_cmd *cmd)
                        (uint64_t)virt_dev->file_size, (uint64_t)data_len);
                scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
                                        scst_sense_block_out_range_error));
-               goto done;
+               goto out_compl;
        }
 
        switch (opcode) {
@@ -780,7 +776,7 @@ static int vdisk_do_job(struct scst_cmd *cmd)
                                        (uint64_t)data_len);
                                do_fsync = 1;
                                if (vdisk_fsync(thr, 0, 0, cmd) != 0)
-                                       goto done;
+                                       goto out_compl;
                        }
                        if (virt_dev->blockio) {
                                blockio_exec_rw(cmd, thr, lba_start, 1);
@@ -815,7 +811,7 @@ static int vdisk_do_job(struct scst_cmd *cmd)
                                        (uint64_t)data_len);
                                do_fsync = 1;
                                if (vdisk_fsync(thr, 0, 0, cmd) != 0)
-                                       goto done;
+                                       goto out_compl;
                        }
                        /* ToDo: BLOCKIO VERIFY */
                        vdisk_exec_write(cmd, thr, loff);
@@ -903,10 +899,10 @@ static int vdisk_do_job(struct scst_cmd *cmd)
                    SCST_LOAD_SENSE(scst_sense_invalid_opcode));
        }
 
-done:
+out_compl:
        cmd->completed = 1;
 
-done_uncompl:
+out_done:
        cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
 
 out:
index 15003bb..3168e2b 100644 (file)
        not be supported.
 #endif
 
-/* All targets, devices and dev_types management is done under this mutex */
+/*
+ * All targets, devices and dev_types management is done under this mutex.
+ *
+ * It must NOT be used in any works (schedule_work(), etc.), because
+ * otherwise a deadlock (double lock, actually) is possible, e.g., with
+ * scst_user detach_tgt(), which is called under scst_mutex and calls
+ * flush_scheduled_work().
+ */
 DEFINE_MUTEX(scst_mutex);
 
 LIST_HEAD(scst_template_list);
@@ -109,7 +116,8 @@ DECLARE_WAIT_QUEUE_HEAD(scst_mgmt_cmd_list_waitQ);
 
 DECLARE_WAIT_QUEUE_HEAD(scst_mgmt_waitQ);
 spinlock_t scst_mgmt_lock = SPIN_LOCK_UNLOCKED;
-LIST_HEAD(scst_sess_mgmt_list);
+LIST_HEAD(scst_sess_init_list);
+LIST_HEAD(scst_sess_shut_list);
 
 DECLARE_WAIT_QUEUE_HEAD(scst_dev_cmd_waitQ);
 
@@ -369,7 +377,7 @@ void scst_unregister(struct scst_tgt *tgt)
 
        mutex_lock(&scst_mutex);
        list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
-               sBUG_ON(!sess->shutting_down);
+               sBUG_ON(sess->shut_phase == SCST_SESS_SPH_READY);
        }
        mutex_unlock(&scst_mutex);
 
index a169c1f..16a8c24 100644 (file)
@@ -1048,6 +1048,7 @@ struct scst_session *scst_alloc_session(struct scst_tgt *tgt, int gfp_mask,
 #endif
 
        sess->init_phase = SCST_SESS_IPH_INITING;
+       sess->shut_phase = SCST_SESS_SPH_READY;
        atomic_set(&sess->refcnt, 0);
        for(i = 0; i < TGT_DEV_HASH_SIZE; i++) {
                struct list_head *sess_tgt_dev_list_head =
@@ -1137,8 +1138,8 @@ void scst_sched_session_free(struct scst_session *sess)
        TRACE_ENTRY();
 
        spin_lock_irqsave(&scst_mgmt_lock, flags);
-       TRACE_DBG("Adding sess %p to scst_sess_mgmt_list", sess);
-       list_add_tail(&sess->sess_mgmt_list_entry, &scst_sess_mgmt_list);
+       TRACE_DBG("Adding sess %p to scst_sess_shut_list", sess);
+       list_add_tail(&sess->sess_shut_list_entry, &scst_sess_shut_list);
        spin_unlock_irqrestore(&scst_mgmt_lock, flags);
        
        wake_up(&scst_mgmt_waitQ);
index a2527f8..cbc3f98 100644 (file)
@@ -198,7 +198,8 @@ extern struct scst_tasklet scst_tasklets[NR_CPUS];
 
 extern wait_queue_head_t scst_mgmt_waitQ;
 extern spinlock_t scst_mgmt_lock;
-extern struct list_head scst_sess_mgmt_list;
+extern struct list_head scst_sess_init_list;
+extern struct list_head scst_sess_shut_list;
 
 struct scst_cmd_thread_t {
        struct task_struct *cmd_thread;
index 331676e..07d8397 100644 (file)
@@ -61,7 +61,7 @@ struct scst_cmd *scst_rx_cmd(struct scst_session *sess,
        TRACE_ENTRY();
 
 #ifdef EXTRACHECKS
-       if (unlikely(sess->shutting_down)) {
+       if (unlikely(sess->shut_phase != SCST_SESS_SPH_READY)) {
                PRINT_ERROR_PR("%s", "New cmd while shutting down the session");
                sBUG();
        }
@@ -145,7 +145,7 @@ out_redirect:
                cmd->state = SCST_CMD_STATE_XMIT_RESP;
                /* Keep initiator away from too many BUSY commands */
                if (!in_interrupt() && !in_atomic())
-                       ssleep(2);
+                       msleep(50);
                else
                        WARN_ON_ONCE(1);
        } else {
@@ -1287,12 +1287,8 @@ static int scst_report_luns_local(struct scst_cmd *cmd)
        TRACE_ENTRY();
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto out_done;
-               else
-                       goto out_uncompl;
-       }
+       if (unlikely(rc != 0))
+               goto out_done;
 
        cmd->status = 0;
        cmd->msg_status = 0;
@@ -1366,10 +1362,10 @@ inc_dev_cnt:
        if (dev_cnt < cmd->resp_data_len)
                scst_set_resp_data_len(cmd, dev_cnt);
 
-out_done:
+out_compl:
        cmd->completed = 1;
 
-out_uncompl:
+out_done:
        /* Report the result */
        scst_cmd_done_local(cmd, SCST_CMD_STATE_DEFAULT);
 
@@ -1382,12 +1378,12 @@ out_put_err:
 out_err:
        scst_set_cmd_error(cmd,
                   SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
-       goto out_done;
+       goto out_compl;
 
 out_put_hw_err:
        scst_put_buf(cmd, buffer);
        scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
-       goto out_done;
+       goto out_compl;
 }
 
 static int scst_pre_select(struct scst_cmd *cmd)
@@ -1451,12 +1447,8 @@ static int scst_reserve_local(struct scst_cmd *cmd)
        scst_block_dev_cmd(cmd, 1);
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto out_compl;
-               else
-                       goto out_uncompl;
-       }
+       if (unlikely(rc != 0))
+               goto out_done;
 
        spin_lock_bh(&dev->dev_lock);
 
@@ -1483,10 +1475,7 @@ out:
        TRACE_EXIT_RES(res);
        return res;
 
-out_compl:
-       cmd->completed = 1;
-
-out_uncompl:
+out_done:
        res = SCST_EXEC_COMPLETED;
        /* Report the result */
        scst_cmd_done_local(cmd, SCST_CMD_STATE_DEFAULT);
@@ -1511,12 +1500,8 @@ static int scst_release_local(struct scst_cmd *cmd)
        scst_block_dev_cmd(cmd, 1);
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto out_compl;
-               else
-                       goto out_uncompl;
-       }
+       if (unlikely(rc != 0))
+               goto out_done;
 
        spin_lock_bh(&dev->dev_lock);
 
@@ -1544,16 +1529,13 @@ static int scst_release_local(struct scst_cmd *cmd)
        spin_unlock_bh(&dev->dev_lock);
 
        if (res == SCST_EXEC_COMPLETED)
-               goto out_compl;
+               goto out_done;
 
 out:
        TRACE_EXIT_RES(res);
        return res;
 
-out_compl:
-       cmd->completed = 1;
-
-out_uncompl:
+out_done:
        res = SCST_EXEC_COMPLETED;
        /* Report the result */
        scst_cmd_done_local(cmd, SCST_CMD_STATE_DEFAULT);
@@ -1629,6 +1611,7 @@ out:
 
 out_complete:
        res = 1;
+       cmd->completed = 1;
        goto out;
 
 out_uncomplete:
@@ -1786,12 +1769,8 @@ static int scst_do_send_to_midlev(struct scst_cmd *cmd)
        }
 
        rc = scst_check_local_events(cmd);
-       if (unlikely(rc != 0)) {
-               if (rc > 0)
-                       goto out_compl;
-               else
-                       goto out_aborted;
-       }
+       if (unlikely(rc != 0))
+               goto out_done;
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)        
        if (unlikely(scst_alloc_request(cmd) != 0)) {
@@ -1845,20 +1824,14 @@ out_rc_error:
 out_error:
        scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
 
-out_compl:
-       cmd->completed = 1;
-       rc = SCST_EXEC_COMPLETED;
-       scst_cmd_done_local(cmd, SCST_CMD_STATE_DEFAULT);
-       goto out;
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)        
 out_busy:
        scst_set_busy(cmd);
-       goto out_compl;
-       goto out;
+       cmd->completed = 1;
+       /* go through */
 #endif
 
-out_aborted:
+out_done:
        rc = SCST_EXEC_COMPLETED;
        /* Report the result. The cmd is not completed */
        scst_cmd_done_local(cmd, SCST_CMD_STATE_DEFAULT);
@@ -4052,7 +4025,7 @@ static int scst_post_rx_mgmt_cmd(struct scst_session *sess,
        atomic_inc(&sess->sess_cmd_count);
 
 #ifdef EXTRACHECKS
-       if (unlikely(sess->shutting_down)) {
+       if (unlikely(sess->shut_phase != SCST_SESS_SPH_READY)) {
                PRINT_ERROR_PR("%s",
                        "New mgmt cmd while shutting down the session");
                sBUG();
@@ -4346,9 +4319,9 @@ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic,
                sess->reg_sess_data = data;
                sess->init_result_fn = result_fn;
                spin_lock_irqsave(&scst_mgmt_lock, flags);
-               TRACE_DBG("Adding sess %p to scst_sess_mgmt_list", sess);
-               list_add_tail(&sess->sess_mgmt_list_entry,
-                             &scst_sess_mgmt_list);
+               TRACE_DBG("Adding sess %p to scst_sess_init_list", sess);
+               list_add_tail(&sess->sess_init_list_entry,
+                             &scst_sess_init_list);
                spin_unlock_irqrestore(&scst_mgmt_lock, flags);
                wake_up(&scst_mgmt_waitQ);
        } else {
@@ -4367,47 +4340,6 @@ out_free:
        goto out;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void scst_unreg_work_fn(void *p)
-#else
-static void scst_unreg_work_fn(struct work_struct *work)
-#endif
-{
-       int i;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-       struct scst_session *sess = (struct scst_session*)p;
-#else
-       struct scst_session *sess = container_of(work, struct scst_session,
-                                       unreg_work);
-#endif
-       struct scst_tgt_dev *tgt_dev;
-
-       TRACE_ENTRY();
-
-       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];
-               list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
-                               sess_tgt_dev_list_entry) {
-                       struct scst_dev_type *handler = tgt_dev->dev->handler;
-                       if (handler && handler->pre_unreg_sess) {
-                               TRACE_DBG("Calling dev handler's pre_unreg_sess(%p)",
-                                     tgt_dev);
-                               handler->pre_unreg_sess(tgt_dev);
-                               TRACE_DBG("%s", "Dev handler's pre_unreg_sess() "
-                                       "returned");
-                       }
-               }
-       }
-       mutex_unlock(&scst_mutex);
-
-       scst_sess_put(sess);
-
-       TRACE_EXIT();
-       return;
-}
-
 /* 
  * Must not been called in parallel with scst_rx_cmd() or 
  * scst_rx_mgmt_fn_*() for the same sess
@@ -4429,9 +4361,10 @@ void scst_unregister_session(struct scst_session *sess, int wait,
        pc = &c;
 #endif
 
+       sess->shut_phase = SCST_SESS_SPH_PRE_UNREG;
+
        spin_lock_irqsave(&scst_mgmt_lock, flags);
 
-       sess->shutting_down = 1;
        sess->unreg_done_fn = unreg_done_fn;
        if (wait) {
                sess->shutdown_compl = pc;
@@ -4446,13 +4379,7 @@ void scst_unregister_session(struct scst_session *sess, int wait,
 
        tm_dbg_task_mgmt("UNREGISTER SESSION", 1);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-       INIT_WORK(&sess->unreg_work, scst_unreg_work_fn, sess);
-#else
-       INIT_WORK(&sess->unreg_work, scst_unreg_work_fn);
-#endif
-
-       schedule_work(&sess->unreg_work);
+       scst_sess_put(sess);
 
        if (wait) {
                TRACE_DBG("Waiting for session %p to complete", sess);
@@ -4467,9 +4394,47 @@ void scst_unregister_session(struct scst_session *sess, int wait,
        return;
 }
 
+static void scst_pre_unreg_sess(struct scst_session *sess)
+{
+       int i;
+       struct scst_tgt_dev *tgt_dev;
+       unsigned long flags;
+
+       TRACE_ENTRY();
+
+       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];
+               list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
+                               sess_tgt_dev_list_entry) {
+                       struct scst_dev_type *handler = tgt_dev->dev->handler;
+                       if (handler && handler->pre_unreg_sess) {
+                               TRACE_DBG("Calling dev handler's pre_unreg_sess(%p)",
+                                     tgt_dev);
+                               handler->pre_unreg_sess(tgt_dev);
+                               TRACE_DBG("%s", "Dev handler's pre_unreg_sess() "
+                                       "returned");
+                       }
+               }
+       }
+       mutex_unlock(&scst_mutex);
+
+       sess->shut_phase = SCST_SESS_SPH_SHUTDOWN;
+
+       spin_lock_irqsave(&scst_mgmt_lock, flags);
+       TRACE_DBG("Adding sess %p to scst_sess_shut_list", sess);
+       list_add_tail(&sess->sess_shut_list_entry, &scst_sess_shut_list);
+       spin_unlock_irqrestore(&scst_mgmt_lock, flags);
+
+       TRACE_EXIT();
+       return;
+}
+
 static inline int test_mgmt_list(void)
 {
-       int res = !list_empty(&scst_sess_mgmt_list) ||
+       int res = !list_empty(&scst_sess_init_list) ||
+                 !list_empty(&scst_sess_shut_list) ||
                  unlikely(kthread_should_stop());
        return res;
 }
@@ -4500,36 +4465,64 @@ int scst_mgmt_thread(void *arg)
                        set_current_state(TASK_RUNNING);
                        remove_wait_queue(&scst_mgmt_waitQ, &wait);
                }
-restart:
-               list_for_each_entry(sess, &scst_sess_mgmt_list,
-                       sess_mgmt_list_entry)
-               {
-                       TRACE_DBG("Removing sess %p from scst_sess_mgmt_list",
+
+               while (!list_empty(&scst_sess_init_list)) {
+                       sess = list_entry(scst_sess_init_list.next,
+                               typeof(*sess), sess_init_list_entry);
+                       TRACE_DBG("Removing sess %p from scst_sess_init_list",
                                sess);
-                       list_del(&sess->sess_mgmt_list_entry);
+                       list_del(&sess->sess_init_list_entry);
                        spin_unlock_irq(&scst_mgmt_lock);
-                       if (sess->init_phase == SCST_SESS_IPH_INITING) {
+
+                       if (sess->init_phase == SCST_SESS_IPH_INITING)
                                scst_init_session(sess);
-                       } else if (sess->shutting_down) {
+                       else {
+                               PRINT_ERROR_PR("session %p is in "
+                                       "scst_sess_init_list, but in unknown "
+                                       "init phase %x", sess,
+                                       sess->init_phase);
+                               sBUG();
+                       }
+
+                       spin_lock_irq(&scst_mgmt_lock);
+               }
+
+               while (!list_empty(&scst_sess_shut_list)) {
+                       sess = list_entry(scst_sess_shut_list.next,
+                               typeof(*sess), sess_shut_list_entry);
+                       TRACE_DBG("Removing sess %p from scst_sess_shut_list",
+                               sess);
+                       list_del(&sess->sess_shut_list_entry);
+                       spin_unlock_irq(&scst_mgmt_lock);
+
+                       switch(sess->shut_phase) {
+                       case SCST_SESS_SPH_PRE_UNREG:
+                               scst_pre_unreg_sess(sess);
+                               break;
+                       case SCST_SESS_SPH_SHUTDOWN:
                                sBUG_ON(atomic_read(&sess->refcnt) != 0);
                                scst_free_session_callback(sess);
-                       } else {
+                               break;
+                       default:
                                PRINT_ERROR_PR("session %p is in "
-                                       "scst_sess_mgmt_list, but in unknown "
-                                       "phase %x", sess, sess->init_phase);
+                                       "scst_sess_shut_list, but in unknown "
+                                       "shut phase %lx", sess,
+                                       sess->shut_phase);
                                sBUG();
+                               break;
                        }
+
                        spin_lock_irq(&scst_mgmt_lock);
-                       goto restart;
                }
        }
        spin_unlock_irq(&scst_mgmt_lock);
 
        /*
         * If kthread_should_stop() is true, we are guaranteed to be
-        * on the module unload, so scst_sess_mgmt_list must be empty.
+        * on the module unload, so both lists must be empty.
         */
-       sBUG_ON(!list_empty(&scst_sess_mgmt_list));
+       sBUG_ON(!list_empty(&scst_sess_init_list));
+       sBUG_ON(!list_empty(&scst_sess_shut_list));
 
        TRACE_EXIT();
        return 0;