The major TM processing cleanup in scst_user module which was possible after the...
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Sat, 31 May 2008 12:05:02 +0000 (12:05 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Sat, 31 May 2008 12:05:02 +0000 (12:05 +0000)
 - PRIO queue was removed from scst_user. Instead, all priority commands now queued in the head of the regular queue. The corresponding code was removed from fileio_tgt as well. It necessary, in the future the priority queue can be easily restored from this patch.

 - pre_unreg_sess() was removed from struct scst_dev_type. The corresponding code was removed from SCST core as well

 - Almost all /proc/scsi_tgt commands now can fail after timeout (90 seconds) with EBUSY

 - Fixed possible incorrect command's retry if double RESET UA is detected.

 - Many minor changes and cleanups

Also docs were updated.

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

31 files changed:
doc/scst_user_spec.txt
iscsi-scst/README
iscsi-scst/kernel/config.c
iscsi-scst/kernel/iscsi.h
iscsi-scst/kernel/target.c
iscsi-scst/usr/ctldev.c
iscsi-scst/usr/iscsid.c
qla2x00t/qla2x00-target/README
scst/README
scst/include/scst.h
scst/include/scst_const.h
scst/include/scst_debug.h
scst/include/scst_user.h
scst/src/dev_handlers/scst_cdrom.c
scst/src/dev_handlers/scst_changer.c
scst/src/dev_handlers/scst_disk.c
scst/src/dev_handlers/scst_modisk.c
scst/src/dev_handlers/scst_processor.c
scst/src/dev_handlers/scst_raid.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_debug.c
scst/src/scst_lib.c
scst/src/scst_main.c
scst/src/scst_priv.h
scst/src/scst_proc.c
scst/src/scst_targ.c
usr/fileio/common.c
usr/fileio/common.h
usr/fileio/fileio.c

index 842d9eb..2d411e4 100644 (file)
@@ -2,7 +2,7 @@
 
          USER SPACE INTERFACE DESCRIPTION.
 
-                  Version 0.9.6/3
+                  Version 0.9.6/4
 
 
                I. Description.
@@ -103,7 +103,6 @@ struct scst_user_opt
        uint8_t parse_type;
        uint8_t on_free_cmd_type;
        uint8_t memory_reuse_type;
-       uint8_t prio_queue_type;
        uint8_t partial_transfers_type;
        uint32_t partial_len;
 
@@ -156,18 +155,6 @@ where:
 
    * SCST_USER_MEM_REUSE_ALL - unlimited memory reuse is possible.
 
- - prio_queue_type - defines if the user space handler need to receive
-   all management subcommands from a separate PRIO queue using
-   SCST_USER_REPLY_AND_GET_PRIO_CMD command. Management subcommands are:
-   SCST_USER_ATTACH_SESS, SCST_USER_DETACH_SESS and SCST_USER_TASK_MGMT.
-   Possible values are:
-
-   * SCST_USER_PRIO_QUEUE_SINGLE - a single queue is used for regular
-     and management subcommands
-
-   * SCST_USER_PRIO_QUEUE_SEPARATE - a separate PRIO queue is used for
-     management subcommands
-
  - partial_transfers_type - defines if the user space handler supports
    partial data transfers, when a SCSI command, which required big data
    transfer, is broken on several subcommands with smaller data
@@ -312,7 +299,7 @@ SCST_USER_PARSE returns SCSI command on PARSE state of the SCST
 processing. The PARSE state is intended to check validity of the
 command, determine data transfer type and the necessary data buffer
 size. This subcommand is returned only if SCST_USER_SET_OPTIONS
-parse_type isn'e set to SCST_USER_PARSE_STANDARD. In this case the
+parse_type isn't set to SCST_USER_PARSE_STANDARD. In this case the
 standard SCST internal parser for this SCSI device type will do all the
 job.
 
@@ -715,23 +702,8 @@ Possible return values are:
  - SCST_MGMT_STATUS_FAILED - task management function failed
 
 
-               5. SCST_USER_REPLY_AND_GET_PRIO_CMD
-
-SCST_USER_REPLY_AND_GET_PRIO_CMD is the same as
-SCST_USER_REPLY_AND_GET_CMD, except that it returns management (i.e.
-priority) subcommands from priority queue, if usage of a separate PRIO
-was configured. 
-
-Management subcommands are: SCST_USER_ATTACH_SESS, SCST_USER_DETACH_SESS
-and SCST_USER_TASK_MGMT.
-
-PRIO queue is always blocking, because poll() doesn't support, when
-different threads wait with different events mask. Only one thread is
-woken up on each event and if it isn't interested in such events,
-another (interested) one will not be woken up.
-
 
-               6. SCST_USER_REPLY_CMD
+               5. SCST_USER_REPLY_CMD
 
 SCST_USER_REPLY_CMD IOCTL function allows the user space handler to
 return the result of a command's execution. Its argument is defined as:
@@ -841,8 +813,8 @@ involve data transfer from target to initiator) and WRITE-type (i.e.
 which involve data transfer from initiator to target) commands. So the
 device is configured with parse_type SCST_USER_PARSE_STANDARD,
 on_free_cmd_type SCST_USER_ON_FREE_CMD_IGNORE, memory_reuse_type
-SCST_USER_MEM_REUSE_ALL, prio_queue_type SCST_USER_PRIO_QUEUE_SINGLE and
-partial_transfers_type SCST_USER_PARTIAL_TRANSFERS_NOT_SUPPORTED.
+SCST_USER_MEM_REUSE_ALL and partial_transfers_type
+SCST_USER_PARTIAL_TRANSFERS_NOT_SUPPORTED.
 
 Then it prepares struct scst_user_get_cmd with reply set to 0, calls
 SCST_USER_REPLY_AND_GET_CMD ioctl() and waits until some initiator
index cb43562..302cf26 100644 (file)
@@ -32,6 +32,16 @@ Installation
 Basically as in README-IET, where file names are changed as specified
 above.
 
+Only vanilla kernels from kernel.org are supported, but it should work
+on vendors' kernels, if you manage to successfully compile on them. The
+main problem with vendor's kernels is that they often contain patches,
+which will appear only in the next version of the vanilla kernel,
+therefore it's quite hard to track such changes. Thus, if during
+compilation for some vendor kernel your compiler complains about
+redefinition of some symbol, you should either switch to vanilla kernel,
+or change as necessary the corresponding to that symbol "#if
+LINUX_VERSION_CODE" statement.
+
 If during compilation you see message like "*** No rule to make target
 `xxx.h', needed by `yyy.o'.  Stop.", then your autogenerated
 dependencies don't match your compiler configuration anymore. You should
@@ -189,5 +199,7 @@ Thanks to:
    debugging
 
  * Tomasz Chmielewski <mangoo@wpkg.org> for testing and suggestions
+
+ * Bart Van Assche <bart.vanassche@gmail.com> for a lot of help
+
 Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net
index 610d691..6cec2d4 100644 (file)
@@ -408,9 +408,27 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        long err;
        u32 id;
 
-       if (cmd == REGISTER_USERD) {
+       switch (cmd) {
+       case ADD_TARGET:
+       case DEL_TARGET:
+       case ADD_SESSION:
+       case DEL_SESSION:
+       case GET_SESSION_INFO:
+       case ISCSI_PARAM_SET:
+       case ISCSI_PARAM_GET:
+       case ADD_CONN:
+       case DEL_CONN:
+       case GET_CONN_INFO:
+               break;
+
+       case REGISTER_USERD:
                err = iscsi_check_version(arg);
                goto out;
+
+       default:
+               PRINT_ERROR("Invalid ioctl cmd %x", cmd);
+               err = -EINVAL;
+               goto out;
        }
 
        if ((err = get_user(id, (u32 *) arg)) != 0)
@@ -440,7 +458,7 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        }
 
        if (!target) {
-               PRINT_ERROR("can't find the target %u", id);
+               PRINT_ERROR("Can't find the target %u", id);
                err = -EINVAL;
                goto out_unlock;
        }
@@ -481,8 +499,7 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                break;
 
        default:
-               PRINT_ERROR("invalid ioctl cmd %x", cmd);
-               err = -EINVAL;
+               sBUG();
                break;
        }
 
index cd374fd..fc4ab22 100644 (file)
@@ -467,7 +467,9 @@ static inline void cmnd_put(struct iscsi_cmnd *cmnd)
 {
        TRACE_DBG("cmnd %p, new ref_cnt %d", cmnd,
                atomic_read(&cmnd->ref_cnt)-1);
-       sBUG_ON(atomic_read(&cmnd->ref_cnt) == 0);
+
+       EXTRACHECKS_BUG_ON(atomic_read(&cmnd->ref_cnt) == 0);
+
        if (atomic_dec_and_test(&cmnd->ref_cnt))
                cmnd_done(cmnd);
 }
index 871bf45..c6f0b9a 100644 (file)
@@ -119,6 +119,7 @@ static int iscsi_target_create(struct target_info *info, u32 tid)
        target->scst_tgt = scst_register(&iscsi_template, target->name);
        if (!target->scst_tgt) {
                PRINT_ERROR("%s", "scst_register() failed");
+               err = -EBUSY;
                goto out_free;
        }
 
index 5fa91f5..298beea 100644 (file)
@@ -83,6 +83,7 @@ static int ctrdev_open(int *max_data_seg_len)
        }
 
        reg.version = (uintptr_t)ISCSI_SCST_INTERFACE_VERSION;
+
        err = ioctl(ctlfd, REGISTER_USERD, &reg);
        if (err < 0) {
                log_error("Unable to register: %s. Incompatible version of the "
index b1f2192..0310c46 100644 (file)
@@ -109,7 +109,16 @@ void text_key_add(struct connection *conn, char *key, char *value)
                conn->rsp.data = conn->rsp_buffer;
        }
        if (conn->rsp.datasize + len > INCOMING_BUFSIZE) {
-               log_warning("Dropping key (%s=%s)", key, value);
+               /* ToDo: multi-PDU replies */
+               log_warning("Dropping key (%s=%s) due to INCOMING_BUFSIZE "
+                       "limit %d and because only single PDU replies during "
+                       "discovery session are implemented. If you have "
+                       "a lot of targets, you can increase INCOMING_BUFSIZE, "
+                       "but, since it will be against iSCSI RFC required "
+                       "not-negotiated PDU limit, not all initiators might "
+                       "work with it. Alternatively, you can decrease names "
+                       "of your targets so they will fit to INCOMING_BUFSIZE "
+                       "limit", key, value, INCOMING_BUFSIZE);
                return;
        }
 
index 26eb7f6..8f32bab 100644 (file)
@@ -15,9 +15,7 @@ simultaneously, is supported as well.
 
 This version is compatible with SCST version 0.9.5 and higher.
 
-Tested on stable kernels from http://www.kernel.org. The original
-initiator driver was taken from kernel version 2.6.17.8, but it should
-also work on other versions, including 2.6.18.x and 2.6.16.x.
+The original initiator driver was taken from the kernel 2.6.17.8.
 
 See also "ToDo" file for list of known issues and unimplemented 
 features.
@@ -25,6 +23,16 @@ features.
 Installation
 ------------
 
+Only vanilla kernels from kernel.org are supported, but it should work
+on vendors' kernels, if you manage to successfully compile on them. The
+main problem with vendor's kernels is that they often contain patches,
+which will appear only in the next version of the vanilla kernel,
+therefore it's quite hard to track such changes. Thus, if during
+compilation for some vendor kernel your compiler complains about
+redefinition of some symbol, you should either switch to vanilla kernel,
+or change as necessary the corresponding to that symbol "#if
+LINUX_VERSION_CODE" statement.
+
 At first, make sure that the link "/lib/modules/`you_kernel_version`/build" 
 points to the source code for your currently running kernel.
 
index 8b93b55..9c67f9b 100644 (file)
@@ -42,11 +42,19 @@ of devices with different access permissions. See below for details.
 
 This is quite stable (but still beta) version.
 
-Tested mostly on "vanilla" 2.6.21.1 kernel from kernel.org.
-
 Installation
 ------------
 
+Only vanilla kernels from kernel.org are supported, but it should work
+on vendors' kernels, if you manage to successfully compile on them. The
+main problem with vendor's kernels is that they often contain patches,
+which will appear only in the next version of the vanilla kernel,
+therefore it's quite hard to track such changes. Thus, if during
+compilation for some vendor kernel your compiler complains about
+redefinition of some symbol, you should either switch to vanilla kernel,
+or change as necessary the corresponding to that symbol "#if
+LINUX_VERSION_CODE" statement.
+
 At first, make sure that the link "/lib/modules/`you_kernel_version`/build" 
 points to the source code for your currently running kernel.
 
@@ -476,9 +484,23 @@ will open file /vdisks/disk1 as virtual FILEIO disk with name "disk1".
 CAUTION: If you partitioned/formatted your device with block size X, *NEVER*
 ======== ever try to export and then mount it (even accidentally) with another
          block size. Otherwise you can *instantly* damage it pretty
-        badly as well as all your data on it. Messages on initiator like:
-        "attempt to access beyond end of device" is the sign of such
-        damage.
+        badly as well as all your data on it. Messages on initiator
+        like: "attempt to access beyond end of device" is the sign of
+        such damage.
+
+        Moreover, if you want to compare how well different block sizes
+        work for you, you **MUST** EVERY TIME AFTER CHANGING BLOCK SIZE
+        **COMPLETELY** **WIPE OFF** ALL THE DATA FROM THE DEVICE. In
+        other words, THE **WHOLE** DEVICE **MUST** HAVE ONLY **ZEROES**
+        AS THE DATA AFTER YOU SWITCH TO NEW BLOCK SIZE. Switching block
+        sizes isn't like switching between FILEIO and BLOCKIO, after
+        changing block size all previously written with another block
+        size data MUST BE ERASED. Otherwise you will have a full set of
+        very weird behaviors, because blocks addressing will be
+        changed, but initiators in most cases will not have a
+        possibility to detect that old addresses written on the device
+        in, e.g., partition table, don't refer anymore to what they are
+        intended to refer.
 
 IMPORTANT: By default for performance reasons VDISK FILEIO devices use write
 =========  back caching policy. This is generally safe from the consistence of
@@ -835,4 +857,6 @@ Thanks to:
  * Jianxi Chen <pacers@users.sourceforge.net> for fixing problem with
    devices >2TB in size
 
+ * Bart Van Assche <bart.vanassche@gmail.com> for a lot of help
+
 Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net
index ab7e232..ce57291 100644 (file)
@@ -289,12 +289,6 @@ typedef _Bool bool;
  */
 #define SCST_EXEC_NEED_THREAD        2
 
-/*************************************************************
- ** Default timeout for cmd's CDB execution
- ** by SCSI mid-level (cmd's "timeout" field).
- *************************************************************/
-#define SCST_DEFAULT_TIMEOUT         (30*HZ)
-
 /*
  * Set if cmd is finished and there is status/sense to be sent.
  * The status should be not sent (i.e. the flag not set) if the
@@ -334,11 +328,8 @@ typedef _Bool bool;
 /* 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
+#define SCST_SESS_SPH_SHUTDOWN       1
 
 /*************************************************************
  ** Cmd's async (atomic) flags
@@ -383,6 +374,11 @@ typedef _Bool bool;
  *************************************************************/
 #define SCST_PROC_ENTRY_NAME         "scsi_tgt"
 
+/*************************************************************
+ ** Activities suspending timeout
+ *************************************************************/
+#define SCST_SUSPENDING_TIMEOUT                        (90 * HZ)
+
 /*************************************************************
  ** Kernel cache creation helper
  *************************************************************/
@@ -773,7 +769,7 @@ struct scst_dev_type {
         *  - SCST_MGMT_STATUS_SUCCESS - the command is done with success,
         *      no firther actions required
         *  - The SCST_MGMT_STATUS_* error code if the command is failed and
-        *      no firther actions required
+        *      no further actions required
         *  - SCST_DEV_TM_NOT_COMPLETED - regular standard actions for the command
         *      should be done
         *
@@ -797,15 +793,6 @@ struct scst_dev_type {
         */
        int (*attach_tgt) (struct scst_tgt_dev *tgt_dev);
 
-       /*
-        * Called when a session, corresponding to a tgt_dev, is about to be
-        * unregistered and the tgt_dev - detached. Supposed to be used to
-        * clean out "stalled" commands, which otherwise could prevent SCST
-        * from entering into the suspended activity state and, so,
-        * unregistering the device.
-        */
-       void (*pre_unreg_sess) (struct scst_tgt_dev *tgt_dev);
-
        /* Called when tgt_dev (session) is detaching from the dev handler */
        void (*detach_tgt) (struct scst_tgt_dev *tgt_dev);
 
@@ -1172,7 +1159,7 @@ struct scst_cmd {
 
        enum scst_cmd_queue_type queue_type;
 
-       int timeout;    /* CDB execution timeout */
+       int timeout;    /* CDB execution timeout in seconds */
        int retries;    /* Amount of retries that will be done by SCSI mid-level */
 
        /* SCSI data direction, one of SCST_DATA_* constants */
@@ -1226,6 +1213,9 @@ struct scst_cmd {
         */
        int orig_sg_cnt, orig_sg_entry, orig_entry_len;
 
+       /* Used to retry commands in case of double UA */
+       int dbl_ua_orig_resp_data_len, dbl_ua_orig_data_direction;
+
        /* List corresponding mgmt cmd, if any, protected by sess_list_lock */
        struct list_head mgmt_cmd_list;
 
@@ -2302,22 +2292,30 @@ static inline void scst_mgmt_cmd_set_tgt_priv(struct scst_mgmt_cmd *mcmd,
        mcmd->tgt_priv = val;
 }
 
-/*
- * Returns mgmt cmd's completition status (SCST_MGMT_STATUS_* constants)
- */
+/* Returns mgmt cmd's completition status (SCST_MGMT_STATUS_* constants) */
 static inline int scst_mgmt_cmd_get_status(struct scst_mgmt_cmd *mcmd)
 {
        return mcmd->status;
 }
 
-/*
- * Returns mgmt cmd's TM fn
- */
+/* Returns mgmt cmd's TM fn */
 static inline int scst_mgmt_cmd_get_fn(struct scst_mgmt_cmd *mcmd)
 {
        return mcmd->fn;
 }
 
+/*
+ * Called by dev handler's task_mgmt_fn() to notify SCST core that mcmd
+ * is going to complete asynchronously.
+ */
+void scst_prepare_async_mcmd(struct scst_mgmt_cmd *mcmd);
+
+/*
+ * Called by dev handler to notify SCST core that async. mcmd is completed
+ * with status "status".
+ */
+void scst_async_mcmd_completed(struct scst_mgmt_cmd *mcmd, int status);
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
 
 static inline struct page *sg_page(struct scatterlist *sg)
@@ -2450,11 +2448,16 @@ static inline int scst_get_buf_count(struct scst_cmd *cmd)
 
 /*
  * Suspends and resumes any activity.
- * scst_suspend_activity() doesn't return until there are any
- * active commands (state after SCST_CMD_STATE_INIT). New arriving
- * commands stay in that state until scst_resume_activity() is called.
+ * Function scst_suspend_activity() doesn't return 0, until there are any
+ * active commands (state after SCST_CMD_STATE_INIT). If "interruptible"
+ * is true, it returns after SCST_SUSPENDING_TIMEOUT or if it was interrupted
+ * by a signal with the coresponding error status < 0. If "interruptible"
+ * is false, it will wait virtually forever.
+ *
+ * New arriving commands stay in that state until scst_resume_activity()
+ * is called.
  */
-void scst_suspend_activity(void);
+int scst_suspend_activity(bool interruptible);
 void scst_resume_activity(void);
 
 /*
index fa2182c..39c361f 100644 (file)
@@ -280,4 +280,36 @@ static inline int scst_is_ua_sense(const uint8_t *sense)
 #define POSITION_LEN_SHORT           20
 #define POSITION_LEN_LONG            32
 
+/*************************************************************
+ ** Various timeouts
+ *************************************************************/
+#define SCST_DEFAULT_TIMEOUT                   (60 * HZ)
+
+#define SCST_GENERIC_CHANGER_TIMEOUT           (3 * HZ)
+#define SCST_GENERIC_CHANGER_LONG_TIMEOUT      (14000 * HZ)
+
+#define SCST_GENERIC_PROCESSOR_TIMEOUT         (3 * HZ)
+#define SCST_GENERIC_PROCESSOR_LONG_TIMEOUT    (14000 * HZ)
+
+#define SCST_GENERIC_TAPE_SMALL_TIMEOUT                (3 * HZ)
+#define SCST_GENERIC_TAPE_REG_TIMEOUT          (900 * HZ)
+#define SCST_GENERIC_TAPE_LONG_TIMEOUT         (14000 * HZ)
+
+#define SCST_GENERIC_MODISK_SMALL_TIMEOUT      (3 * HZ)
+#define SCST_GENERIC_MODISK_REG_TIMEOUT                (900 * HZ)
+#define SCST_GENERIC_MODISK_LONG_TIMEOUT       (14000 * HZ)
+
+#define SCST_GENERIC_DISK_SMALL_TIMEOUT                (3 * HZ)
+#define SCST_GENERIC_DISK_REG_TIMEOUT          (60 * HZ)
+#define SCST_GENERIC_DISK_LONG_TIMEOUT         (3600 * HZ)
+
+#define SCST_GENERIC_RAID_TIMEOUT              (3 * HZ)
+#define SCST_GENERIC_RAID_LONG_TIMEOUT         (14000 * HZ)
+
+#define SCST_GENERIC_CDROM_SMALL_TIMEOUT       (3 * HZ)
+#define SCST_GENERIC_CDROM_REG_TIMEOUT         (900 * HZ)
+#define SCST_GENERIC_CDROM_LONG_TIMEOUT                (14000 * HZ)
+
+#define SCST_MAX_OTHER_TIMEOUT                 (14000 * HZ)
+
 #endif /* __SCST_CONST_H */
index e7d91a6..5c91459 100644 (file)
@@ -242,6 +242,15 @@ do {                                                                       \
        PRINT(NO_FLAG, "%s" format, __tflag, args);                     \
 } while (0)
 
+#define PRINT_WARNING(format, args...)                                 \
+do {                                                                   \
+       if (strcmp(INFO_FLAG, LOG_FLAG))                                \
+       {                                                               \
+               PRINT_LOG_FLAG(LOG_FLAG, "***WARNING*** " format, args); \
+       }                                                               \
+       PRINT_LOG_FLAG(INFO_FLAG, "***WARNING*** " format, args);       \
+} while (0)
+
 #define PRINT_ERROR(format, args...)                                   \
 do {                                                                   \
        if (strcmp(ERROR_FLAG, LOG_FLAG)) {                             \
@@ -341,6 +350,12 @@ do {                                                               \
        PRINT(INFO_FLAG, "%s: " format, LOG_PREFIX, args);      \
 } while (0)
 
+#define PRINT_WARNING(format, args...)          \
+do {                                            \
+       PRINT(INFO_FLAG, "%s: ***WARNING*** "   \
+             format, LOG_PREFIX, args);        \
+} while (0)
+
 #define PRINT_ERROR(format, args...)            \
 do {                                            \
        PRINT(ERROR_FLAG, "%s: ***ERROR*** "    \
@@ -360,6 +375,12 @@ do {                                            \
        PRINT(INFO_FLAG, format, args);         \
 } while (0)
 
+#define PRINT_WARNING(format, args...)          \
+do {                                            \
+       PRINT(INFO_FLAG, "***WARNING*** "       \
+               format, args);                  \
+} while (0)
+
 #define PRINT_ERROR(format, args...)           \
 do {                                            \
        PRINT(ERROR_FLAG, "***ERROR*** "        \
index 0e13524..1ed6096 100644 (file)
 #define SCST_USER_MEM_REUSE_ALL                3
 #define SCST_USER_MAX_MEM_REUSE_OPT    SCST_USER_MEM_REUSE_ALL
 
-#define SCST_USER_PRIO_QUEUE_SINGLE    0
-#define SCST_USER_PRIO_QUEUE_SEPARATE  1
-#define SCST_USER_MAX_PRIO_QUEUE_OPT   SCST_USER_PRIO_QUEUE_SEPARATE
-
 #define SCST_USER_PARTIAL_TRANSFERS_NOT_SUPPORTED      0
 #define SCST_USER_PARTIAL_TRANSFERS_SUPPORTED_ORDERED  1
 #define SCST_USER_PARTIAL_TRANSFERS_SUPPORTED          2
 #define UCMD_STATE_ATTACH_SESS         0x20
 #define UCMD_STATE_DETACH_SESS         0x21
 
-/* Must be changed under cmd_lists.cmd_list_lock */
-#define UCMD_STATE_SENT_MASK           0x10000
-#define UCMD_STATE_RECV_MASK           0x20000
-#define UCMD_STATE_JAMMED_MASK         0x40000
-
-#define UCMD_STATE_MASK                        (UCMD_STATE_SENT_MASK | \
-                                        UCMD_STATE_RECV_MASK | \
-                                        UCMD_STATE_JAMMED_MASK)
-
 struct scst_user_opt {
        uint8_t parse_type;
        uint8_t on_free_cmd_type;
        uint8_t memory_reuse_type;
-       uint8_t prio_queue_type;
        uint8_t partial_transfers_type;
        int32_t partial_len;
 
@@ -125,7 +111,7 @@ struct scst_user_scsi_cmd_parse {
        uint8_t cdb[SCST_MAX_CDB_SIZE];
        int32_t cdb_len;
 
-       uint32_t timeout;
+       int32_t timeout;
        int32_t bufflen;
 
        uint8_t queue_type;
@@ -165,7 +151,7 @@ struct scst_user_scsi_cmd_exec {
        uint8_t queue_type;
        uint8_t data_direction;
        uint8_t partial;
-       uint32_t timeout;
+       int32_t timeout;
 
        uint32_t sn;
 
@@ -249,8 +235,7 @@ struct scst_user_reply_cmd {
 #define SCST_USER_SET_OPTIONS          _IOW('u', 3, struct scst_user_opt)
 #define SCST_USER_GET_OPTIONS          _IOR('u', 4, struct scst_user_opt)
 #define SCST_USER_REPLY_AND_GET_CMD    _IOWR('u', 5, struct scst_user_get_cmd)
-#define SCST_USER_REPLY_AND_GET_PRIO_CMD _IOWR('u', 6, struct scst_user_get_cmd)
-#define SCST_USER_REPLY_CMD            _IOW('u', 7, struct scst_user_reply_cmd)
+#define SCST_USER_REPLY_CMD            _IOW('u', 6, struct scst_user_reply_cmd)
 
 /* Values for scst_user_get_cmd.subcode */
 #define SCST_USER_ATTACH_SESS          _IOR('s', UCMD_STATE_ATTACH_SESS, struct scst_user_sess)
index ca91989..718f573 100644 (file)
        .dev_done =             cdrom_done,     \
 }
 
-#define CDROM_SMALL_TIMEOUT  (3 * HZ)
-#define CDROM_REG_TIMEOUT    (900 * HZ)
-#define CDROM_LONG_TIMEOUT   (14000 * HZ)
-
 #define CDROM_DEF_BLOCK_SHIFT  11
 
 struct cdrom_params {
@@ -114,7 +110,7 @@ int cdrom_attach(struct scst_device *dev)
                TRACE_DBG("%s", "Doing READ_CAPACITY");
                res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
                                   buffer_size, sbuff,
-                                  CDROM_REG_TIMEOUT, 3, 0);
+                                  SCST_GENERIC_CDROM_REG_TIMEOUT, 3, 0);
 
                TRACE_DBG("READ_CAPACITY done: %x", res);
 
@@ -218,13 +214,6 @@ int cdrom_parse(struct scst_cmd *cmd)
 
        cmd->retries = SCST_PASSTHROUGH_RETRIES;
 
-       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
-               cmd->timeout = CDROM_REG_TIMEOUT;
-       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
-               cmd->timeout = CDROM_SMALL_TIMEOUT;
-       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
-               cmd->timeout = CDROM_LONG_TIMEOUT;
-
        return res;
 }
 
index d65c997..dc17751 100644 (file)
@@ -38,8 +38,6 @@
 }
 
 #define CHANGER_RETRIES       2
-#define CHANGER_TIMEOUT      (3 * HZ)
-#define CHANGER_LONG_TIMEOUT (14000 * HZ)
 #define READ_CAP_LEN          8
 
 int changer_attach(struct scst_device *);
@@ -85,8 +83,8 @@ int changer_attach(struct scst_device *dev)
        retries = SCST_DEV_UA_RETRIES;
        do {
                TRACE_DBG("%s", "Doing TEST_UNIT_READY");
-               res = scsi_test_unit_ready(dev->scsi_dev, CHANGER_TIMEOUT,
-                                          CHANGER_RETRIES
+               res = scsi_test_unit_ready(dev->scsi_dev,
+                       SCST_GENERIC_CHANGER_TIMEOUT, CHANGER_RETRIES
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
                                          );
 #else
@@ -149,11 +147,6 @@ int changer_parse(struct scst_cmd *cmd)
 
        cmd->retries = SCST_PASSTHROUGH_RETRIES;
 
-       if (cmd->op_flags & SCST_LONG_TIMEOUT)
-               cmd->timeout = CHANGER_LONG_TIMEOUT;
-       else
-               cmd->timeout = CHANGER_TIMEOUT;
-
        return res;
 }
 
index 1c759c0..435feaf 100644 (file)
        .exec =                 disk_exec,              \
 }
 
-#define DISK_SMALL_TIMEOUT  (3 * HZ)
-#define DISK_REG_TIMEOUT    (60 * HZ)
-#define DISK_LONG_TIMEOUT   (3600 * HZ)
-
 #define DISK_DEF_BLOCK_SHIFT   9
 
 struct disk_params {
@@ -190,7 +186,7 @@ int disk_attach(struct scst_device *dev)
                TRACE_DBG("%s", "Doing READ_CAPACITY");
                res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
                                   buffer_size, sbuff,
-                                  DISK_REG_TIMEOUT, 3, 0);
+                                  SCST_GENERIC_DISK_REG_TIMEOUT, 3, 0);
 
                TRACE_DBG("READ_CAPACITY done: %x", res);
 
@@ -291,13 +287,6 @@ int disk_parse(struct scst_cmd *cmd)
 
        cmd->retries = SCST_PASSTHROUGH_RETRIES;
 
-       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
-               cmd->timeout = DISK_REG_TIMEOUT;
-       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
-               cmd->timeout = DISK_SMALL_TIMEOUT;
-       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
-               cmd->timeout = DISK_LONG_TIMEOUT;
-
        return res;
 }
 
index ea333fc..1ac0c5f 100644 (file)
        .exec =                 modisk_exec,            \
 }
 
-#define MODISK_SMALL_TIMEOUT  (3 * HZ)
-#define MODISK_REG_TIMEOUT    (900 * HZ)
-#define MODISK_LONG_TIMEOUT   (14000 * HZ)
-
 #define MODISK_DEF_BLOCK_SHIFT    10
 
 struct modisk_params {
@@ -201,7 +197,7 @@ int modisk_attach(struct scst_device *dev)
                TRACE_DBG("%s", "Doing READ_CAPACITY");
                res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
                                   buffer_size, sbuff,
-                                  MODISK_REG_TIMEOUT, 3, 0);
+                                  SCST_GENERIC_MODISK_REG_TIMEOUT, 3, 0);
 
                TRACE_DBG("READ_CAPACITY done: %x", res);
 
@@ -308,13 +304,6 @@ int modisk_parse(struct scst_cmd *cmd)
 
        cmd->retries = SCST_PASSTHROUGH_RETRIES;
 
-       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
-               cmd->timeout = MODISK_REG_TIMEOUT;
-       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
-               cmd->timeout = MODISK_SMALL_TIMEOUT;
-       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
-               cmd->timeout = MODISK_LONG_TIMEOUT;
-
        return res;
 }
 
index fe48887..734e7a2 100644 (file)
@@ -38,8 +38,6 @@
 }
 
 #define PROCESSOR_RETRIES       2
-#define PROCESSOR_TIMEOUT      (3 * HZ)
-#define PROCESSOR_LONG_TIMEOUT (14000 * HZ)
 #define READ_CAP_LEN          8
 
 int processor_attach(struct scst_device *);
@@ -85,8 +83,8 @@ int processor_attach(struct scst_device *dev)
        retries = SCST_DEV_UA_RETRIES;
        do {
                TRACE_DBG("%s", "Doing TEST_UNIT_READY");
-               res = scsi_test_unit_ready(dev->scsi_dev, PROCESSOR_TIMEOUT,
-                                          PROCESSOR_RETRIES
+               res = scsi_test_unit_ready(dev->scsi_dev,
+                       SCST_GENERIC_PROCESSOR_TIMEOUT, PROCESSOR_RETRIES
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
                                          );
 #else
@@ -149,10 +147,6 @@ int processor_parse(struct scst_cmd *cmd)
 
        cmd->retries = SCST_PASSTHROUGH_RETRIES;
 
-       if (cmd->op_flags & SCST_LONG_TIMEOUT)
-               cmd->timeout = PROCESSOR_LONG_TIMEOUT;
-       else
-               cmd->timeout = PROCESSOR_TIMEOUT;
        return res;
 }
 
index 83e41cb..fb51ddc 100644 (file)
@@ -38,8 +38,6 @@
 }
 
 #define RAID_RETRIES       2
-#define RAID_TIMEOUT      (3 * HZ)
-#define RAID_LONG_TIMEOUT (14000 * HZ)
 #define READ_CAP_LEN          8
 
 int raid_attach(struct scst_device *);
@@ -85,8 +83,8 @@ int raid_attach(struct scst_device *dev)
        retries = SCST_DEV_UA_RETRIES;
        do {
                TRACE_DBG("%s", "Doing TEST_UNIT_READY");
-               res = scsi_test_unit_ready(dev->scsi_dev, RAID_TIMEOUT,
-                                          RAID_RETRIES
+               res = scsi_test_unit_ready(dev->scsi_dev,
+                       SCST_GENERIC_RAID_TIMEOUT, RAID_RETRIES
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
                                          );
 #else
@@ -149,10 +147,6 @@ int raid_parse(struct scst_cmd *cmd)
 
        cmd->retries = SCST_PASSTHROUGH_RETRIES;
 
-       if (cmd->op_flags & SCST_LONG_TIMEOUT)
-               cmd->timeout = RAID_LONG_TIMEOUT;
-       else
-               cmd->timeout = RAID_TIMEOUT;
        return res;
 }
 
index b954224..e2534a4 100644 (file)
 
 #define TAPE_RETRIES        2
 
-#define TAPE_SMALL_TIMEOUT  (3 * HZ)
-#define TAPE_REG_TIMEOUT    (900 * HZ)
-#define TAPE_LONG_TIMEOUT   (14000 * HZ)
-
 #define TAPE_DEF_BLOCK_SIZE    512
 
 /* The fixed bit in READ/WRITE/VERIFY */
@@ -181,8 +177,8 @@ int tape_attach(struct scst_device *dev)
        retries = SCST_DEV_UA_RETRIES;
        do {
                TRACE_DBG("%s", "Doing TEST_UNIT_READY");
-               res = scsi_test_unit_ready(dev->scsi_dev, TAPE_SMALL_TIMEOUT,
-                                          TAPE_RETRIES
+               res = scsi_test_unit_ready(dev->scsi_dev,
+                       SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
                                          );
 #else
@@ -201,7 +197,7 @@ int tape_attach(struct scst_device *dev)
                               ((dev->scsi_dev->lun << 5) & 0xe0) : 0),
                              0 /* Mode Page 0 */,
                              buffer, buffer_size,
-                             TAPE_SMALL_TIMEOUT, TAPE_RETRIES,
+                             SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES,
                              &data, NULL);
        TRACE_DBG("MODE_SENSE done: %x", res);
 
@@ -301,13 +297,6 @@ int tape_parse(struct scst_cmd *cmd)
 
        cmd->retries = SCST_PASSTHROUGH_RETRIES;
 
-       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
-               cmd->timeout = TAPE_REG_TIMEOUT;
-       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
-               cmd->timeout = TAPE_SMALL_TIMEOUT;
-       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
-               cmd->timeout = TAPE_LONG_TIMEOUT;
-
        return res;
 }
 
index 75ca162..e3e0512 100644 (file)
 #endif
 
 #define DEV_USER_MAJOR                 237
+
 #define DEV_USER_CMD_HASH_ORDER                6
-#define DEV_USER_TM_TIMEOUT            (10*HZ)
+
 #define DEV_USER_ATTACH_TIMEOUT                (5*HZ)
-#define DEV_USER_DETACH_TIMEOUT                (5*HZ)
-#define DEV_USER_PRE_UNREG_POLL_TIME   (HZ/10)
 
 struct scst_user_dev {
        struct rw_semaphore dev_rwsem;
 
        struct scst_cmd_lists cmd_lists;
-       /* All 3 protected by cmd_lists.cmd_list_lock */
+
+       /* Protected by cmd_lists.cmd_list_lock */
        struct list_head ready_cmd_list;
-       struct list_head prio_ready_cmd_list;
-       wait_queue_head_t prio_cmd_list_waitQ;
-
-       /* All, including detach_cmd_count, protected by cmd_lists.cmd_list_lock */
-       unsigned short blocking:1;
-       unsigned short cleaning:1;
-       unsigned short cleanup_done:1;
-       unsigned short attach_cmd_active:1;
-       unsigned short tm_cmd_active:1;
-       unsigned short internal_reset_active:1;
-       unsigned short pre_unreg_sess_active:1; /* just a small optimization */
-
-       unsigned short tst:3;
-       unsigned short queue_alg:4;
-       unsigned short tas:1;
-       unsigned short swp:1;
-       unsigned short has_own_order_mgmt:1;
-
-       unsigned short detach_cmd_count;
+
+       /* Protected by dev_rwsem or don't need any protection */
+       unsigned int blocking:1;
+       unsigned int cleanup_done:1;
+       unsigned int cleaning:1;
+       unsigned int tst:3;
+       unsigned int queue_alg:4;
+       unsigned int tas:1;
+       unsigned int swp:1;
+       unsigned int has_own_order_mgmt:1;
 
        int (*generic_parse)(struct scst_cmd *cmd,
                int (*get_block)(struct scst_cmd *cmd));
@@ -77,7 +68,6 @@ struct scst_user_dev {
        uint8_t parse_type;
        uint8_t on_free_cmd_type;
        uint8_t memory_reuse_type;
-       uint8_t prio_queue_type;
        uint8_t partial_transfers_type;
        uint32_t partial_len;
 
@@ -85,7 +75,7 @@ struct scst_user_dev {
 
        /* Both protected by cmd_lists.cmd_list_lock */
        unsigned int handle_counter;
-       struct list_head ucmd_hash[1<<DEV_USER_CMD_HASH_ORDER];
+       struct list_head ucmd_hash[1 << DEV_USER_CMD_HASH_ORDER];
 
        struct scst_device *sdev;
 
@@ -93,25 +83,13 @@ struct scst_user_dev {
        struct list_head dev_list_entry;
        char name[SCST_MAX_NAME];
 
-       /* Protected by cmd_lists.cmd_list_lock */
-       struct list_head pre_unreg_sess_list;
-
+       /* Protected by cleanup_lock */
+       unsigned char in_cleanup_list:1;
        struct list_head cleanup_list_entry;
+       /* ToDo: make it on-stack */
        struct completion cleanup_cmpl;
 };
 
-struct scst_user_pre_unreg_sess_obj {
-       struct scst_tgt_dev *tgt_dev;
-       unsigned int active:1;
-       unsigned int exit:1;
-       struct list_head pre_unreg_sess_list_entry;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-       struct work_struct pre_unreg_sess_work;
-#else
-       struct delayed_work pre_unreg_sess_work;
-#endif
-};
-
 /* Most fields are unprotected, since only one thread at time can access them */
 struct scst_user_cmd {
        struct scst_cmd *cmd;
@@ -122,7 +100,6 @@ struct scst_user_cmd {
        unsigned int buff_cached:1;
        unsigned int buf_dirty:1;
        unsigned int background_exec:1;
-       unsigned int internal_reset_tm:1;
        unsigned int aborted:1;
 
        struct scst_user_cmd *buf_ucmd;
@@ -134,6 +111,15 @@ struct scst_user_cmd {
        struct page **data_pages;
        struct sgv_pool_obj *sgv;
 
+       /* 
+        * Special flags, which can be accessed asynchronously (hence "long").
+        * Protected by cmd_lists.cmd_list_lock.
+        */
+       unsigned long sent_to_user:1;
+       unsigned long jammed:1;
+       unsigned long this_state_unjammed:1;
+       unsigned long seen_by_user:1; /* here only as a small optimization */
+
        unsigned int state;
 
        struct list_head ready_cmd_list_entry;
@@ -143,7 +129,11 @@ struct scst_user_cmd {
 
        struct scst_user_get_cmd user_cmd;
 
-       struct completion *cmpl;
+       /* cmpl used only by ATTACH_SESS, mcmd used only by TM */
+       union {
+               struct completion *cmpl;
+               struct scst_mgmt_cmd *mcmd;
+       };
        int result;
 };
 
@@ -169,9 +159,9 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd);
 
 static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
        unsigned long *flags);
-static void dev_user_unjam_dev(struct scst_user_dev *dev, int tm,
-       struct scst_tgt_dev *tgt_dev);
+static void dev_user_unjam_dev(struct scst_user_dev *dev);
 
+static int dev_user_process_reply_on_free(struct scst_user_cmd *ucmd);
 static int dev_user_process_reply_tm_exec(struct scst_user_cmd *ucmd,
        int status);
 static int dev_user_process_reply_sess(struct scst_user_cmd *ucmd, int status);
@@ -212,7 +202,31 @@ static LIST_HEAD(cleanup_list);
 static DECLARE_WAIT_QUEUE_HEAD(cleanup_list_waitQ);
 static struct task_struct *cleanup_thread;
 
-static inline void ucmd_get(struct scst_user_cmd *ucmd, int barrier)
+/*
+ * Skip this command if result is not 0. Must be called under
+ * cmd_lists.cmd_list_lock and IRQ off.
+ */
+static inline bool ucmd_get_check(struct scst_user_cmd *ucmd)
+{
+       int r = atomic_inc_return(&ucmd->ucmd_ref);
+       int res;
+       if (unlikely(r == 1)) {
+               TRACE_DBG("ucmd %p is being destroyed", ucmd);
+               atomic_dec(&ucmd->ucmd_ref);
+               res = true;
+               /*
+                * Necessary code is serialized by cmd_list_lock in
+                * cmd_remove_hash()
+                */
+       } else {
+               TRACE_DBG("ucmd %p, new ref_cnt %d", ucmd,
+                       atomic_read(&ucmd->ucmd_ref));
+               res = false;
+       }
+       return res;
+}
+
+static inline void __ucmd_get(struct scst_user_cmd *ucmd, bool barrier)
 {
        TRACE_DBG("ucmd %p, ucmd_ref %d", ucmd, atomic_read(&ucmd->ucmd_ref));
        atomic_inc(&ucmd->ucmd_ref);
@@ -220,9 +234,22 @@ static inline void ucmd_get(struct scst_user_cmd *ucmd, int barrier)
                smp_mb__after_atomic_inc();
 }
 
+static inline void ucmd_get_ordered(struct scst_user_cmd *ucmd)
+{
+       __ucmd_get(ucmd, true);
+}
+
+static inline void ucmd_get(struct scst_user_cmd *ucmd)
+{
+       __ucmd_get(ucmd, false);
+}
+
 static inline void ucmd_put(struct scst_user_cmd *ucmd)
 {
        TRACE_DBG("ucmd %p, ucmd_ref %d", ucmd, atomic_read(&ucmd->ucmd_ref));
+
+       EXTRACHECKS_BUG_ON(atomic_read(&ucmd->ucmd_ref) == 0);
+
        if (atomic_dec_and_test(&ucmd->ucmd_ref))
                dev_user_free_ucmd(ucmd);
 }
@@ -298,6 +325,7 @@ static void cmd_insert_hash(struct scst_user_cmd *ucmd)
 static inline void cmd_remove_hash(struct scst_user_cmd *ucmd)
 {
        unsigned long flags;
+
        spin_lock_irqsave(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
        list_del(&ucmd->hash_list_entry);
        spin_unlock_irqrestore(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
@@ -338,7 +366,7 @@ static struct page *dev_user_alloc_pages(struct scatterlist *sg,
                TRACE_MEM("ucmd->first_page_offset %d",
                        ucmd->first_page_offset);
                offset = ucmd->first_page_offset;
-               ucmd_get(ucmd, 0);
+               ucmd_get(ucmd);
        }
 
        if (ucmd->cur_data_page >= ucmd->num_data_pages)
@@ -393,6 +421,7 @@ static void dev_user_unmap_buf(struct scst_user_cmd *ucmd)
 
                page_cache_release(page);
        }
+
        kfree(ucmd->data_pages);
        ucmd->data_pages = NULL;
 
@@ -532,7 +561,7 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
                        sBUG_ON(ucmd->sgv != NULL);
                        res = -1;
                } else {
-                       switch (ucmd->state & ~UCMD_STATE_MASK) {
+                       switch (ucmd->state) {
                        case UCMD_STATE_BUF_ALLOCING:
                                res = 1;
                                break;
@@ -711,7 +740,7 @@ static int dev_user_parse(struct scst_cmd *cmd)
                        min(sizeof(ucmd->user_cmd.parse_cmd.cdb),
                            sizeof(cmd->cdb)));
                ucmd->user_cmd.parse_cmd.cdb_len = cmd->cdb_len;
-               ucmd->user_cmd.parse_cmd.timeout = cmd->timeout;
+               ucmd->user_cmd.parse_cmd.timeout = cmd->timeout / HZ;
                ucmd->user_cmd.parse_cmd.bufflen = cmd->bufflen;
                ucmd->user_cmd.parse_cmd.queue_type = cmd->queue_type;
                ucmd->user_cmd.parse_cmd.data_direction = cmd->data_direction;
@@ -821,7 +850,7 @@ static int dev_user_exec(struct scst_cmd *cmd)
        ucmd->user_cmd.exec_cmd.queue_type = cmd->queue_type;
        ucmd->user_cmd.exec_cmd.data_direction = cmd->data_direction;
        ucmd->user_cmd.exec_cmd.partial = 0;
-       ucmd->user_cmd.exec_cmd.timeout = cmd->timeout;
+       ucmd->user_cmd.exec_cmd.timeout = cmd->timeout / HZ;
        ucmd->user_cmd.exec_cmd.sn = cmd->tgt_sn;
 
        ucmd->state = UCMD_STATE_EXECING;
@@ -839,7 +868,7 @@ static void dev_user_free_sgv(struct scst_user_cmd *ucmd)
                ucmd->sgv = NULL;
        } else if (ucmd->data_pages != NULL) {
                /* We mapped pages, but for some reason didn't allocate them */
-               ucmd_get(ucmd, 0);
+               ucmd_get(ucmd);
                __dev_user_free_sg_entries(ucmd);
        }
        return;
@@ -864,9 +893,13 @@ static void dev_user_on_free_cmd(struct scst_cmd *cmd)
        if (ucmd->dev->on_free_cmd_type == SCST_USER_ON_FREE_CMD_IGNORE) {
                ucmd->state = UCMD_STATE_ON_FREE_SKIPPED;
                /* The state assignment must be before freeing sgv! */
-               dev_user_free_sgv(ucmd);
-               ucmd_put(ucmd);
-               goto out;
+               goto out_reply;
+       }
+
+       if (unlikely(!ucmd->seen_by_user)) {
+               TRACE_MGMT_DBG("Not seen by user ucmd %p", ucmd);
+               sBUG_ON((ucmd->sgv != NULL) || (ucmd->data_pages != NULL));
+               goto out_reply;
        }
 
        ucmd->user_cmd.cmd_h = ucmd->h;
@@ -886,6 +919,10 @@ static void dev_user_on_free_cmd(struct scst_cmd *cmd)
 out:
        TRACE_EXIT();
        return;
+
+out_reply:
+       dev_user_process_reply_on_free(ucmd);
+       goto out;
 }
 
 static void dev_user_set_block(struct scst_cmd *cmd, int block)
@@ -940,68 +977,37 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
        if (ucmd->cmd)
                do_wake |= ucmd->cmd->preprocessing_only;
 
-       EXTRACHECKS_BUG_ON(ucmd->state & UCMD_STATE_JAMMED_MASK);
-
        spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, flags);
 
-       /* Hopefully, compiler will make it as a single test/jmp */
-       if (unlikely(dev->attach_cmd_active || dev->tm_cmd_active ||
-                    dev->internal_reset_active || dev->pre_unreg_sess_active ||
-                    (dev->detach_cmd_count != 0))) {
-               switch (ucmd->state) {
-               case UCMD_STATE_PARSING:
-               case UCMD_STATE_BUF_ALLOCING:
-               case UCMD_STATE_EXECING:
-                       if (dev->pre_unreg_sess_active &&
-                           !(dev->attach_cmd_active || dev->tm_cmd_active ||
-                             dev->internal_reset_active ||
-                             (dev->detach_cmd_count != 0))) {
-                               struct scst_user_pre_unreg_sess_obj *p, *found = NULL;
-                               list_for_each_entry(p, &dev->pre_unreg_sess_list,
-                                       pre_unreg_sess_list_entry) {
-                                       if (p->tgt_dev == ucmd->cmd->tgt_dev) {
-                                               if (p->active)
-                                                       found = p;
-                                               break;
-                                       }
-                               }
-                               if (found == NULL) {
-                                       TRACE_MGMT_DBG("No pre unreg sess "
-                                               "active (ucmd %p)", ucmd);
-                                       break;
-                               } else {
-                                       TRACE_MGMT_DBG("Pre unreg sess %p "
-                                               "active (ucmd %p)", found, ucmd);
-                               }
-                       }
-                       TRACE(TRACE_MGMT, "Mgmt cmd active, returning BUSY for "
-                               "ucmd %p", ucmd);
-                       dev_user_unjam_cmd(ucmd, 1, &flags);
-                       spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, flags);
-                       goto out;
-               }
-       }
-
-       if (unlikely(ucmd->state == UCMD_STATE_TM_EXECING) ||
-           unlikely(ucmd->state == UCMD_STATE_ATTACH_SESS) ||
-           unlikely(ucmd->state == UCMD_STATE_DETACH_SESS)) {
-               if (dev->prio_queue_type == SCST_USER_PRIO_QUEUE_SEPARATE) {
-                       TRACE_MGMT_DBG("Adding mgmt ucmd %p to prio ready cmd "
-                                      "list", ucmd);
-                       list_add_tail(&ucmd->ready_cmd_list_entry,
-                               &dev->prio_ready_cmd_list);
-                       wake_up(&dev->prio_cmd_list_waitQ);
-                       do_wake = 0;
-               } else {
-                       TRACE_MGMT_DBG("Adding mgmt ucmd %p to ready cmd "
-                               "list", ucmd);
-                       list_add_tail(&ucmd->ready_cmd_list_entry,
-                               &dev->ready_cmd_list);
-                       do_wake = 1;
-               }
+       ucmd->this_state_unjammed = 0;
+
+       if ((ucmd->state == UCMD_STATE_PARSING) ||
+           (ucmd->state == UCMD_STATE_BUF_ALLOCING)) {
+               /*
+                * If we don't put such commands in the queue head, then under
+                * high load we might delay threads, waiting for memory
+                * allocations, for too long and start loosing NOPs, which
+                * would lead to consider us by remote initiators as
+                * unresponsive and stuck => broken connections, etc. If none
+                * of our commands completed in NOP timeout to allow the head
+                * commands to go, then we are really overloaded and/or stuck.
+                */
+               TRACE_DBG("Adding ucmd %p (state %d) to head of ready "
+                       "cmd list", ucmd, ucmd->state);
+               list_add(&ucmd->ready_cmd_list_entry,
+                       &dev->ready_cmd_list);
+               do_wake = 1;
+       } else if (unlikely(ucmd->state == UCMD_STATE_TM_EXECING) ||
+                  unlikely(ucmd->state == UCMD_STATE_ATTACH_SESS) ||
+                  unlikely(ucmd->state == UCMD_STATE_DETACH_SESS)) {
+               TRACE_MGMT_DBG("Adding mgmt ucmd %p (state %d) to head of "
+                       "ready cmd list", ucmd, ucmd->state);
+               list_add(&ucmd->ready_cmd_list_entry,
+                       &dev->ready_cmd_list);
+               do_wake = 1;
        } else if ((ucmd->cmd != NULL) &&
-           unlikely((ucmd->cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))) {
-               TRACE_DBG("Adding ucmd %p to head ready cmd list", ucmd);
+                  unlikely((ucmd->cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))) {
+               TRACE_DBG("Adding HQ ucmd %p to head of ready cmd list", ucmd);
                list_add(&ucmd->ready_cmd_list_entry, &dev->ready_cmd_list);
        } else {
                TRACE_DBG("Adding ucmd %p to ready cmd list", ucmd);
@@ -1015,7 +1021,19 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
 
        spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, flags);
 
-out:
+       smp_mb();
+       if (unlikely(dev->cleaning)) {
+               spin_lock_irqsave(&cleanup_lock, flags);
+               if (!dev->in_cleanup_list) {
+                       TRACE_DBG("Adding dev %p to the cleanup list (ucmd %p)",
+                               dev, ucmd);
+                       list_add_tail(&dev->cleanup_list_entry, &cleanup_list);
+                       dev->in_cleanup_list = 1;
+                       wake_up(&cleanup_list_waitQ);
+               }
+               spin_unlock_irqrestore(&cleanup_lock, flags);
+       }
+
        TRACE_EXIT();
        return;
 }
@@ -1123,6 +1141,7 @@ out_process:
 
 out_hwerr:
        scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
+       ucmd->cmd->state = SCST_CMD_STATE_PRE_XMIT_RESP;
        res = -EINVAL;
        goto out_process;
 }
@@ -1169,8 +1188,11 @@ out_process:
        return res;
 
 out_inval:
-       PRINT_ERROR("%s", "Invalid parse_reply parameter(s)");
+       PRINT_ERROR("Invalid parse_reply parameters (LUN %lld, op %x, cmd %p)",
+               (long long unsigned int)cmd->lun, cmd->cdb[0], cmd);
+       PRINT_BUFFER("Invalid parse_reply", reply, sizeof(*reply));
        scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
+       cmd->state = SCST_CMD_STATE_PRE_XMIT_RESP;
        res = -EINVAL;
        goto out_process;
 }
@@ -1231,7 +1253,7 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
                if (unlikely((cmd->data_direction == SCST_DATA_READ) ||
                             (cmd->resp_data_len != 0)))
                        goto out_inval;
-               ucmd_get(ucmd, 1);
+               ucmd_get_ordered(ucmd);
                ucmd->background_exec = 1;
                TRACE_DBG("Background ucmd %p", ucmd);
                goto out_compl;
@@ -1298,7 +1320,9 @@ out:
        return res;
 
 out_inval:
-       PRINT_ERROR("%s", "Invalid exec_reply parameter(s)");
+       PRINT_ERROR("Invalid exec_reply parameters (LUN %lld, op %x, cmd %p)",
+               (long long unsigned int)cmd->lun, cmd->cdb[0], cmd);
+       PRINT_BUFFER("Invalid exec_reply", reply, sizeof(*reply));
 
 out_hwerr:
        res = -EINVAL;
@@ -1329,39 +1353,44 @@ static int dev_user_process_reply(struct scst_user_dev *dev,
        spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
 
        ucmd = __ucmd_find_hash(dev, reply->cmd_h);
-       if (ucmd == NULL) {
+       if (unlikely(ucmd == NULL)) {
                TRACE_MGMT_DBG("cmd_h %d not found", reply->cmd_h);
                res = -ESRCH;
                goto out_unlock;
        }
 
+       if (unlikely(ucmd_get_check(ucmd))) {
+               TRACE_MGMT_DBG("Found being destroyed cmd_h %d", reply->cmd_h);
+               res = -ESRCH;
+               goto out_unlock;
+       }
+
        if (ucmd->background_exec) {
                state = UCMD_STATE_EXECING;
                goto unlock_process;
        }
 
-       if (unlikely(!(ucmd->state & UCMD_STATE_SENT_MASK))) {
-               if (ucmd->state & UCMD_STATE_JAMMED_MASK) {
-                       TRACE_MGMT_DBG("Reply on jammed ucmd %p, ignoring",
-                               ucmd);
-               } else {
-                       TRACE_MGMT_DBG("Ucmd %p isn't in the sent to user "
-                               "state %x", ucmd, ucmd->state);
-                       res = -EBUSY;
-               }
-               goto out_unlock;
+       if (unlikely(ucmd->this_state_unjammed)) {
+               TRACE_MGMT_DBG("Reply on unjammed ucmd %p, ignoring",
+                       ucmd);
+               goto out_unlock_put;
+       }
+
+       if (unlikely(!ucmd->sent_to_user)) {
+               TRACE_MGMT_DBG("Ucmd %p isn't in the sent to user "
+                       "state %x", ucmd, ucmd->state);
+               res = -EINVAL;
+               goto out_unlock_put;
        }
 
        if (unlikely(reply->subcode != ucmd->user_cmd.subcode))
                goto out_wrong_state;
 
-       if (unlikely(_IOC_NR(reply->subcode) !=
-                       (ucmd->state & ~UCMD_STATE_SENT_MASK)))
+       if (unlikely(_IOC_NR(reply->subcode) != ucmd->state))
                goto out_wrong_state;
 
-       ucmd->state &= ~UCMD_STATE_SENT_MASK;
        state = ucmd->state;
-       ucmd->state |= UCMD_STATE_RECV_MASK;
+       ucmd->sent_to_user = 0;
 
 unlock_process:
        spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
@@ -1400,6 +1429,10 @@ unlock_process:
                sBUG();
                break;
        }
+
+out_put:
+       ucmd_put(ucmd);
+
 out:
        TRACE_EXIT_RES(res);
        return res;
@@ -1412,6 +1445,10 @@ out_wrong_state:
        res = -EINVAL;
        dev_user_unjam_cmd(ucmd, 0, NULL);
 
+out_unlock_put:
+       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       goto out_put;
+
 out_unlock:
        spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
        goto out;
@@ -1498,13 +1535,17 @@ again:
                TRACE_DBG("Found ready ucmd %p", u);
                list_del(&u->ready_cmd_list_entry);
 
-               EXTRACHECKS_BUG_ON(u->state & UCMD_STATE_JAMMED_MASK);
+               EXTRACHECKS_BUG_ON(u->this_state_unjammed);
 
                if (u->cmd != NULL) {
                        if (u->state == UCMD_STATE_EXECING) {
                                struct scst_user_dev *dev = u->dev;
                                int rc;
+
+                               EXTRACHECKS_BUG_ON(u->jammed);
+
                                spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+
                                rc = scst_check_local_events(u->cmd);
                                if (unlikely(rc != 0)) {
                                        u->cmd->scst_cmd_done(u->cmd,
@@ -1517,10 +1558,7 @@ again:
                                                &dev->cmd_lists.cmd_list_lock);
                                        goto again;
                                }
-                               /*
-                                * There is no real need to lock again here, but
-                                * let's do it for simplicity.
-                                */
+
                                spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
                        } else if (unlikely(test_bit(SCST_CMD_ABORTED,
                                        &u->cmd->cmd_flags))) {
@@ -1535,7 +1573,8 @@ again:
                                }
                        }
                }
-               u->state |= UCMD_STATE_SENT_MASK;
+               u->sent_to_user = 1;
+               u->seen_by_user = 1;
        }
        return u;
 }
@@ -1600,71 +1639,7 @@ static int dev_user_get_next_cmd(struct scst_user_dev *dev,
        return res;
 }
 
-static inline int test_prio_cmd_list(struct scst_user_dev *dev)
-{
-       /*
-        * Prio queue is always blocking, because poll() seems doesn't
-        * support, when different threads wait with different events
-        * mask. Only one thread is woken up on each event and if it
-        * isn't interested in such events, another (interested) one
-        * will not be woken up. Does't know if it's a bug or feature.
-        */
-       int res = !list_empty(&dev->prio_ready_cmd_list) ||
-                 dev->cleaning || dev->cleanup_done ||
-                 signal_pending(current);
-       return res;
-}
-
-/* Called under cmd_lists.cmd_list_lock and IRQ off */
-static int dev_user_get_next_prio_cmd(struct scst_user_dev *dev,
-       struct scst_user_cmd **ucmd)
-{
-       int res = 0;
-       wait_queue_t wait;
-
-       TRACE_ENTRY();
-
-       init_waitqueue_entry(&wait, current);
-
-       while (1) {
-               if (!test_prio_cmd_list(dev)) {
-                       add_wait_queue_exclusive(&dev->prio_cmd_list_waitQ,
-                               &wait);
-                       for (;;) {
-                               set_current_state(TASK_INTERRUPTIBLE);
-                               if (test_prio_cmd_list(dev))
-                                       break;
-                               spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-                               schedule();
-                               spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-                       }
-                       set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&dev->prio_cmd_list_waitQ, &wait);
-               }
-
-               *ucmd = __dev_user_get_next_cmd(&dev->prio_ready_cmd_list);
-               if (*ucmd != NULL)
-                       break;
-
-               if (dev->cleaning || dev->cleanup_done) {
-                       res = -EAGAIN;
-                       TRACE_DBG("No ready commands, returning %d", res);
-                       break;
-               }
-
-               if (signal_pending(current)) {
-                       res = -EINTR;
-                       TRACE_DBG("Signal pending, returning %d", res);
-                       break;
-               }
-       }
-
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int dev_user_reply_get_cmd(struct file *file, unsigned long arg,
-       int prio)
+static int dev_user_reply_get_cmd(struct file *file, unsigned long arg)
 {
        int res = 0;
        struct scst_user_dev *dev;
@@ -1712,10 +1687,7 @@ static int dev_user_reply_get_cmd(struct file *file, unsigned long arg,
        }
 
        spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-       if (prio && (dev->prio_queue_type == SCST_USER_PRIO_QUEUE_SEPARATE))
-               res = dev_user_get_next_prio_cmd(dev, &ucmd);
-       else
-               res = dev_user_get_next_cmd(dev, &ucmd);
+       res = dev_user_get_next_cmd(dev, &ucmd);
        if (res == 0) {
                *cmd = ucmd->user_cmd;
                spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
@@ -1745,7 +1717,7 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
        switch (cmd) {
        case SCST_USER_REPLY_AND_GET_CMD:
                TRACE_DBG("%s", "REPLY_AND_GET_CMD");
-               res = dev_user_reply_get_cmd(file, arg, 0);
+               res = dev_user_reply_get_cmd(file, arg);
                break;
 
        case SCST_USER_REPLY_CMD:
@@ -1753,11 +1725,6 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
                res = dev_user_reply_cmd(file, arg);
                break;
 
-       case SCST_USER_REPLY_AND_GET_PRIO_CMD:
-               TRACE_DBG("%s", "REPLY_AND_GET_PRIO_CMD");
-               res = dev_user_reply_get_cmd(file, arg, 1);
-               break;
-
        case SCST_USER_REGISTER_DEVICE:
        {
                struct scst_user_dev_desc *dev_desc;
@@ -1862,18 +1829,20 @@ out:
 static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
        unsigned long *flags)
 {
-       int state = ucmd->state & ~UCMD_STATE_MASK;
+       int state = ucmd->state;
        struct scst_user_dev *dev = ucmd->dev;
 
        TRACE_ENTRY();
 
-       if (ucmd->state & UCMD_STATE_JAMMED_MASK)
+       if (ucmd->this_state_unjammed)
                goto out;
 
        TRACE_MGMT_DBG("Unjamming ucmd %p (busy %d, state %x)", ucmd, busy,
-               ucmd->state);
+               state);
 
-       ucmd->state = state | UCMD_STATE_JAMMED_MASK;
+       ucmd->jammed = 1;
+       ucmd->this_state_unjammed = 1;
+       ucmd->sent_to_user = 0;
 
        switch (state) {
        case UCMD_STATE_PARSING:
@@ -1887,6 +1856,9 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
                                scst_set_cmd_error(ucmd->cmd,
                                        SCST_LOAD_SENSE(scst_sense_hardw_error));
                }
+
+               ucmd->cmd->state = SCST_CMD_STATE_PRE_XMIT_RESP;
+
                TRACE_MGMT_DBG("Adding ucmd %p to active list", ucmd);
                list_add(&ucmd->cmd->cmd_list_entry,
                        &ucmd->cmd->cmd_lists->active_cmd_list);
@@ -1912,7 +1884,7 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
                }
 
                ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT);
-               /* !! At this point cmd ans ucmd can be already freed !! */
+               /* !! At this point cmd and ucmd can be already freed !! */
 
                if (flags != NULL)
                        spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, *flags);
@@ -1968,55 +1940,7 @@ out:
        return;
 }
 
-static int __unjam_check_tgt_dev(struct scst_user_cmd *ucmd, int state,
-       struct scst_tgt_dev *tgt_dev)
-{
-       int res = 0;
-
-       if (ucmd->cmd == NULL)
-               goto out;
-
-       if (ucmd->cmd->tgt_dev != tgt_dev)
-               goto out;
-
-       switch (state & ~UCMD_STATE_MASK) {
-       case UCMD_STATE_PARSING:
-       case UCMD_STATE_BUF_ALLOCING:
-       case UCMD_STATE_EXECING:
-               break;
-       default:
-               goto out;
-       }
-
-       res = 1;
-out:
-       return res;
-}
-
-static int __unjam_check_tm(struct scst_user_cmd *ucmd, int state)
-{
-       int res = 0;
-
-       switch (state & ~UCMD_STATE_MASK) {
-       case UCMD_STATE_PARSING:
-       case UCMD_STATE_BUF_ALLOCING:
-       case UCMD_STATE_EXECING:
-               if ((ucmd->cmd != NULL) &&
-                   (!test_bit(SCST_CMD_ABORTED,
-                               &ucmd->cmd->cmd_flags)))
-                       goto out;
-               break;
-       default:
-               goto out;
-       }
-
-       res = 1;
-out:
-       return res;
-}
-
-static void dev_user_unjam_dev(struct scst_user_dev *dev, int tm,
-       struct scst_tgt_dev *tgt_dev)
+static void dev_user_unjam_dev(struct scst_user_dev *dev)
 {
        int i;
        unsigned long flags;
@@ -2031,41 +1955,24 @@ static void dev_user_unjam_dev(struct scst_user_dev *dev, int tm,
 repeat:
        for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++) {
                struct list_head *head = &dev->ucmd_hash[i];
+               bool repeat = false;
+
                list_for_each_entry(ucmd, head, hash_list_entry) {
-                       TRACE_DBG("ALL: ucmd %p, state %x, scst_cmd %p",
+                       if (ucmd_get_check(ucmd))
+                               continue;
+
+                       TRACE_DBG("ucmd %p, state %x, scst_cmd %p",
                                ucmd, ucmd->state, ucmd->cmd);
-                       if (ucmd->state & UCMD_STATE_SENT_MASK) {
-                               int st = ucmd->state & ~UCMD_STATE_SENT_MASK;
-                               if (tgt_dev != NULL) {
-                                       if (__unjam_check_tgt_dev(ucmd, st,
-                                                       tgt_dev) == 0)
-                                               continue;
-                               } else if (tm) {
-                                       if (__unjam_check_tm(ucmd, st) == 0)
-                                               continue;
-                               }
+
+                       if (ucmd->sent_to_user) {
                                dev_user_unjam_cmd(ucmd, 0, &flags);
-                               goto repeat;
+                               repeat = true;
                        }
-               }
-       }
 
-       if ((tgt_dev != NULL) || tm) {
-               list_for_each_entry(ucmd, &dev->ready_cmd_list,
-                               ready_cmd_list_entry) {
-                       TRACE_DBG("READY: ucmd %p, state %x, scst_cmd %p",
-                               ucmd, ucmd->state, ucmd->cmd);
-                       if (tgt_dev != NULL) {
-                               if (__unjam_check_tgt_dev(ucmd, ucmd->state,
-                                               tgt_dev) == 0)
-                                       continue;
-                       } else if (tm) {
-                               if (__unjam_check_tm(ucmd, ucmd->state) == 0)
-                                       continue;
-                       }
-                       list_del(&ucmd->ready_cmd_list_entry);
-                       dev_user_unjam_cmd(ucmd, 0, &flags);
-                       goto repeat;
+                       ucmd_put(ucmd);
+
+                       if (repeat)
+                               goto repeat;
                }
        }
 
@@ -2078,49 +1985,28 @@ repeat:
        return;
 }
 
-/**
- ** In order to deal with user space handler hangups we rely on remote
- ** initiators, which in case if a command doesn't respond for too long
- ** supposed to issue a task management command, so on that event we can
- ** "unjam" the command. In order to prevent TM command from stalling, we
- ** use a timer. In order to prevent too many queued TM commands, we
- ** enqueue only 2 of them, the first one with the requested TM function,
- ** the second - with TARGET_RESET as the most comprehensive function.
- **
- ** The only exception here is DETACH_SESS subcode, where there are no TM
- ** commands could be expected, so we need manually after a timeout "unjam"
- ** all the commands on the device.
- **
- ** We also don't queue >1 ATTACH_SESS commands and after timeout fail it.
- **/
-
 static int dev_user_process_reply_tm_exec(struct scst_user_cmd *ucmd,
        int status)
 {
        int res = 0;
-       unsigned long flags;
 
        TRACE_ENTRY();
 
        TRACE_MGMT_DBG("TM reply (ucmd %p, fn %d, status %d)", ucmd,
                ucmd->user_cmd.tm_cmd.fn, status);
 
-       ucmd->result = status;
-
-       spin_lock_irqsave(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
-
-       if (ucmd->internal_reset_tm) {
-               TRACE_MGMT_DBG("Internal TM ucmd %p finished", ucmd);
-               ucmd->dev->internal_reset_active = 0;
-       } else {
-               TRACE_MGMT_DBG("TM ucmd %p finished", ucmd);
-               ucmd->dev->tm_cmd_active = 0;
+       if (status == SCST_MGMT_STATUS_TASK_NOT_EXIST) {
+               /*
+                * It is possible that user space seen TM cmd before cmd
+                * to abort or will never see it at all, because it was
+                * aborted on the way there. So, it is safe to return
+                * success instead, because, if there is the TM cmd at this
+                * point, then the cmd to abort apparrently does exist.
+                */
+               status = SCST_MGMT_STATUS_SUCCESS;
        }
 
-       if (ucmd->cmpl != NULL)
-               complete_all(ucmd->cmpl);
-
-       spin_unlock_irqrestore(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
+       scst_async_mcmd_completed(ucmd->mcmd, status);
 
        ucmd_put(ucmd);
 
@@ -2128,21 +2014,87 @@ static int dev_user_process_reply_tm_exec(struct scst_user_cmd *ucmd,
        return res;
 }
 
+static void dev_user_abort_ready_commands(struct scst_user_dev *dev)
+{
+       struct scst_user_cmd *ucmd;
+       unsigned long flags;
+
+       TRACE_ENTRY();
+
+       spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, flags);
+again:
+       list_for_each_entry(ucmd, &dev->ready_cmd_list, ready_cmd_list_entry) {
+               if ((ucmd->cmd != NULL) && !ucmd->seen_by_user &&
+                   test_bit(SCST_CMD_ABORTED, &ucmd->cmd->cmd_flags)) {
+                       switch (ucmd->state) {
+                       case UCMD_STATE_PARSING:
+                       case UCMD_STATE_BUF_ALLOCING:
+                       case UCMD_STATE_EXECING:
+                               TRACE_MGMT_DBG("Aborting ready ucmd %p", ucmd);
+                               list_del(&ucmd->ready_cmd_list_entry);
+                               dev_user_unjam_cmd(ucmd, 0, &flags);
+                               goto again;
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, flags);
+
+       TRACE_EXIT();
+       return;
+}
+
 static int dev_user_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
        struct scst_tgt_dev *tgt_dev)
 {
-       int res, rc;
        struct scst_user_cmd *ucmd;
        struct scst_user_dev *dev = (struct scst_user_dev *)tgt_dev->dev->dh_priv;
        struct scst_user_cmd *ucmd_to_abort = NULL;
 
        TRACE_ENTRY();
 
+       /*
+        * In the used approach we don't do anything with hung devices, which
+        * stopped responding and/or have stuck commands. We forcedly abort such
+        * commands only if they not yet sent to the user space or if the device
+        * is getting unloaded, e.g. if its handler program gets killed. This is
+        * because it's pretty hard to distinguish between stuck and temporary
+        * overloaded states of the device. There are several reasons for that:
+        *
+        * 1. Some commands need a lot of time to complete (several hours),
+        *    so for an impatient user such command(s) will always look as
+        *    stuck.
+        *
+        * 2. If we forcedly abort, i.e. abort before it's actually completed
+        *    in the user space, just one command, we will have to put the whole
+        *    device offline until we are sure that no more previously aborted
+        *    commands will get executed. Otherwise, we might have a possibility
+        *    for data corruption, when aborted and reported as completed
+        *    command actually gets executed *after* new commands sent
+        *    after the force abort was done. Many journaling file systems and
+        *    databases use "provide required commands order via queue draining"
+        *    approach and not putting the whole device offline after the forced
+        *    abort will break it. This makes our decision, if a command stuck
+        *    or not, cost a lot.
+        *
+        * So, we leave policy definition if a device stuck or not to
+        * the user space and simply let all commands live until they are
+        * completed or their devices get closed/killed. This approach is very
+        * much OK, but can affect management commands, which need activity
+        * suspending via scst_suspend_activity() function such as devices or
+        * targets registration/removal. But during normal life such commands
+        * should be rare. Plus, when possible, scst_suspend_activity() will
+        * return after timeout EBUSY status to allow caller to not stuck
+        * forever as well.
+        *
+        * But, anyway, ToDo, we should reimplement that in the SCST core, so
+        * stuck commands would affect only related devices.
+        */
+
+       dev_user_abort_ready_commands(dev);
+
        /* We can't afford missing TM command due to memory shortage */
        ucmd = dev_user_alloc_ucmd(dev, GFP_KERNEL|__GFP_NOFAIL);
-       ucmd->cmpl = kmalloc(sizeof(*ucmd->cmpl), GFP_KERNEL|__GFP_NOFAIL);
-
-       init_completion(ucmd->cmpl);
 
        ucmd->user_cmd.cmd_h = ucmd->h;
        ucmd->user_cmd.subcode = SCST_USER_TASK_MGMT;
@@ -2162,59 +2114,15 @@ static int dev_user_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
                mcmd->fn, mcmd->cmd_to_abort, ucmd_to_abort,
                ucmd->user_cmd.tm_cmd.cmd_h_to_abort);
 
+       ucmd->mcmd = mcmd;
        ucmd->state = UCMD_STATE_TM_EXECING;
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-       if (dev->internal_reset_active) {
-               PRINT_ERROR("Loosing TM cmd %d, because there are other "
-                       "unprocessed TM commands", mcmd->fn);
-               res = SCST_MGMT_STATUS_FAILED;
-               goto out_locked_free;
-       } else if (dev->tm_cmd_active) {
-               /*
-                * We are going to miss some TM commands, so replace it
-                * by the hardest one.
-                */
-               PRINT_ERROR("Replacing TM cmd %d by TARGET_RESET, because "
-                       "there is another unprocessed TM command", mcmd->fn);
-               ucmd->user_cmd.tm_cmd.fn = SCST_TARGET_RESET;
-               ucmd->internal_reset_tm = 1;
-               dev->internal_reset_active = 1;
-       } else
-               dev->tm_cmd_active = 1;
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       scst_prepare_async_mcmd(mcmd);
 
-       ucmd_get(ucmd, 0);
        dev_user_add_to_ready(ucmd);
 
-       /*
-        * Since the user space handler should not wait for affecting tasks to
-        * complete it shall complete the TM request ASAP, otherwise the device
-        * will be considered stalled.
-        */
-       rc = wait_for_completion_timeout(ucmd->cmpl, DEV_USER_TM_TIMEOUT);
-       if (rc > 0)
-               res = ucmd->result;
-       else {
-               PRINT_ERROR("Task management command %p timeout", ucmd);
-               res = SCST_MGMT_STATUS_FAILED;
-       }
-
-       sBUG_ON(irqs_disabled());
-
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-
-out_locked_free:
-       kfree(ucmd->cmpl);
-       ucmd->cmpl = NULL;
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-
-       dev_user_unjam_dev(ucmd->dev, 1, NULL);
-
-       ucmd_put(ucmd);
-
        TRACE_EXIT();
-       return res;
+       return SCST_DEV_TM_NOT_COMPLETED;
 }
 
 static int dev_user_attach(struct scst_device *sdev)
@@ -2286,15 +2194,11 @@ static int dev_user_process_reply_sess(struct scst_user_cmd *ucmd, int status)
 
        spin_lock_irqsave(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
 
-       if ((ucmd->state & ~UCMD_STATE_MASK) ==
-                       UCMD_STATE_ATTACH_SESS) {
+       if (ucmd->state == UCMD_STATE_ATTACH_SESS) {
                TRACE_MGMT_DBG("%s", "ATTACH_SESS finished");
                ucmd->result = status;
-               ucmd->dev->attach_cmd_active = 0;
-       } else if ((ucmd->state & ~UCMD_STATE_MASK) ==
-                       UCMD_STATE_DETACH_SESS) {
+       } else if (ucmd->state == UCMD_STATE_DETACH_SESS) {
                TRACE_MGMT_DBG("%s", "DETACH_SESS finished");
-               ucmd->dev->detach_cmd_count--;
        } else
                sBUG();
 
@@ -2348,17 +2252,8 @@ static int dev_user_attach_tgt(struct scst_tgt_dev *tgt_dev)
 
        ucmd->state = UCMD_STATE_ATTACH_SESS;
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-       if (dev->attach_cmd_active) {
-               PRINT_ERROR("%s", "ATTACH_SESS command failed, because "
-                       "there is another unprocessed ATTACH_SESS command");
-               res = -EBUSY;
-               goto out_locked_free;
-       }
-       dev->attach_cmd_active = 1;
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       ucmd_get(ucmd);
 
-       ucmd_get(ucmd, 0);
        dev_user_add_to_ready(ucmd);
 
        rc = wait_for_completion_timeout(ucmd->cmpl, DEV_USER_ATTACH_TIMEOUT);
@@ -2372,7 +2267,6 @@ static int dev_user_attach_tgt(struct scst_tgt_dev *tgt_dev)
        sBUG_ON(irqs_disabled());
 
        spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-out_locked_free:
        kfree(ucmd->cmpl);
        ucmd->cmpl = NULL;
        spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
@@ -2391,105 +2285,19 @@ out_nomem:
        goto out;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void dev_user_pre_unreg_sess_work_fn(void *p)
-#else
-static void dev_user_pre_unreg_sess_work_fn(struct work_struct *work)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-       struct scst_user_pre_unreg_sess_obj *pd = (struct scst_user_pre_unreg_sess_obj *)p;
-#else
-       struct scst_user_pre_unreg_sess_obj *pd = container_of(
-               (struct delayed_work *)work, struct scst_user_pre_unreg_sess_obj,
-               pre_unreg_sess_work);
-#endif
-       struct scst_user_dev *dev =
-               (struct scst_user_dev *)pd->tgt_dev->dev->dh_priv;
-
-       TRACE_ENTRY();
-
-       TRACE_MGMT_DBG("Unreg sess: unjaming dev %p (tgt_dev %p)", dev,
-               pd->tgt_dev);
-
-       pd->active = 1;
-
-       dev_user_unjam_dev(dev, 0, pd->tgt_dev);
-
-       if (!pd->exit) {
-               TRACE_MGMT_DBG("Rescheduling pre_unreg_sess work %p (dev %p, "
-                       "tgt_dev %p)", pd, dev, pd->tgt_dev);
-               schedule_delayed_work(&pd->pre_unreg_sess_work,
-                       DEV_USER_PRE_UNREG_POLL_TIME);
-       }
-
-       TRACE_EXIT();
-       return;
-}
-
-static void dev_user_pre_unreg_sess(struct scst_tgt_dev *tgt_dev)
-{
-       struct scst_user_dev *dev =
-               (struct scst_user_dev *)tgt_dev->dev->dh_priv;
-       struct scst_user_pre_unreg_sess_obj *pd;
-
-       TRACE_ENTRY();
-
-       /* We can't afford missing DETACH command due to memory shortage */
-       pd = kzalloc(sizeof(*pd), GFP_KERNEL|__GFP_NOFAIL);
-
-       pd->tgt_dev = tgt_dev;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-       INIT_WORK(&pd->pre_unreg_sess_work, dev_user_pre_unreg_sess_work_fn, pd);
-#else
-       INIT_DELAYED_WORK(&pd->pre_unreg_sess_work, dev_user_pre_unreg_sess_work_fn);
-#endif
-
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-       dev->pre_unreg_sess_active = 1;
-       list_add_tail(&pd->pre_unreg_sess_list_entry, &dev->pre_unreg_sess_list);
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-
-       TRACE_MGMT_DBG("Scheduling pre_unreg_sess work %p (dev %p, tgt_dev %p)",
-               pd, dev, pd->tgt_dev);
-
-       schedule_delayed_work(&pd->pre_unreg_sess_work, DEV_USER_DETACH_TIMEOUT);
-
-       TRACE_EXIT();
-       return;
-}
-
 static void dev_user_detach_tgt(struct scst_tgt_dev *tgt_dev)
 {
        struct scst_user_dev *dev =
                (struct scst_user_dev *)tgt_dev->dev->dh_priv;
        struct scst_user_cmd *ucmd;
-       struct scst_user_pre_unreg_sess_obj *pd = NULL, *p;
 
        TRACE_ENTRY();
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-       list_for_each_entry(p, &dev->pre_unreg_sess_list,
-                       pre_unreg_sess_list_entry) {
-               if (p->tgt_dev == tgt_dev) {
-                       list_del(&p->pre_unreg_sess_list_entry);
-                       if (list_empty(&dev->pre_unreg_sess_list))
-                               dev->pre_unreg_sess_active = 0;
-                       pd = p;
-                       break;
-               }
-       }
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-
-       if (pd != NULL) {
-               pd->exit = 1;
-               TRACE_MGMT_DBG("Canceling pre unreg work %p", pd);
-               cancel_delayed_work(&pd->pre_unreg_sess_work);
-               flush_scheduled_work();
-               kfree(pd);
-       }
-
-       ucmd = dev_user_alloc_ucmd(dev, GFP_KERNEL);
+       /*
+        * We can't miss TM command due to memory shortage, because it might
+        * lead to a memory leak in the user space handler.
+        */
+       ucmd = dev_user_alloc_ucmd(dev, GFP_KERNEL|__GFP_NOFAIL);
        if (ucmd == NULL)
                goto out;
 
@@ -2500,10 +2308,6 @@ static void dev_user_detach_tgt(struct scst_tgt_dev *tgt_dev)
        ucmd->user_cmd.subcode = SCST_USER_DETACH_SESS;
        ucmd->user_cmd.sess.sess_h = (unsigned long)tgt_dev;
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-       dev->detach_cmd_count++;
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-
        ucmd->state = UCMD_STATE_DETACH_SESS;
 
        dev_user_add_to_ready(ucmd);
@@ -2642,8 +2446,6 @@ static int dev_user_register_dev(struct file *file,
        INIT_LIST_HEAD(&dev->cmd_lists.active_cmd_list);
        init_waitqueue_head(&dev->cmd_lists.cmd_list_waitQ);
        INIT_LIST_HEAD(&dev->ready_cmd_list);
-       INIT_LIST_HEAD(&dev->prio_ready_cmd_list);
-       init_waitqueue_head(&dev->prio_cmd_list_waitQ);
        if (file->f_flags & O_NONBLOCK) {
                TRACE_DBG("%s", "Non-blocking operations");
                dev->blocking = 0;
@@ -2651,7 +2453,6 @@ static int dev_user_register_dev(struct file *file,
                dev->blocking = 1;
        for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++)
                INIT_LIST_HEAD(&dev->ucmd_hash[i]);
-       INIT_LIST_HEAD(&dev->pre_unreg_sess_list);
 
        strncpy(dev->name, dev_desc->name, sizeof(dev->name)-1);
        dev->name[sizeof(dev->name)-1] = '\0';
@@ -2679,7 +2480,6 @@ static int dev_user_register_dev(struct file *file,
        dev->devtype.attach = dev_user_attach;
        dev->devtype.detach = dev_user_detach;
        dev->devtype.attach_tgt = dev_user_attach_tgt;
-       dev->devtype.pre_unreg_sess = dev_user_pre_unreg_sess;
        dev->devtype.detach_tgt = dev_user_detach_tgt;
        dev->devtype.exec = dev_user_exec;
        dev->devtype.on_free_cmd = dev_user_on_free_cmd;
@@ -2687,7 +2487,7 @@ static int dev_user_register_dev(struct file *file,
 
        init_completion(&dev->cleanup_cmpl);
        dev->block = block;
-       dev->def_block = dev->block;
+       dev->def_block = block;
 
        res = __dev_user_set_opt(dev, &dev_desc->opt);
 
@@ -2772,7 +2572,6 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
        if ((opt->parse_type > SCST_USER_MAX_PARSE_OPT) ||
            (opt->on_free_cmd_type > SCST_USER_MAX_ON_FREE_CMD_OPT) ||
            (opt->memory_reuse_type > SCST_USER_MAX_MEM_REUSE_OPT) ||
-           (opt->prio_queue_type > SCST_USER_MAX_PRIO_QUEUE_OPT) ||
            (opt->partial_transfers_type > SCST_USER_MAX_PARTIAL_TRANSFERS_OPT)) {
                PRINT_ERROR("%s", "Invalid option");
                res = -EINVAL;
@@ -2791,18 +2590,6 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
                goto out;
        }
 
-       if ((dev->prio_queue_type != opt->prio_queue_type) &&
-           (opt->prio_queue_type == SCST_USER_PRIO_QUEUE_SINGLE)) {
-               struct scst_user_cmd *u, *t;
-               /* No need for lock, the activity is suspended */
-               list_for_each_entry_safe(u, t, &dev->prio_ready_cmd_list,
-                               ready_cmd_list_entry) {
-                       list_move_tail(&u->ready_cmd_list_entry,
-                               &dev->ready_cmd_list);
-               }
-       }
-
-       dev->prio_queue_type = opt->prio_queue_type;
        dev->parse_type = opt->parse_type;
        dev->on_free_cmd_type = opt->on_free_cmd_type;
        dev->memory_reuse_type = opt->memory_reuse_type;
@@ -2843,14 +2630,18 @@ static int dev_user_set_opt(struct file *file, const struct scst_user_opt *opt)
                mutex_unlock(&dev_priv_mutex);
                goto out;
        }
-       down_read(&dev->dev_rwsem);
+       down_write(&dev->dev_rwsem);
        mutex_unlock(&dev_priv_mutex);
 
-       scst_suspend_activity();
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out;
+
        res = __dev_user_set_opt(dev, opt);
+
        scst_resume_activity();
 
-       up_read(&dev->dev_rwsem);
+       up_write(&dev->dev_rwsem);
 
 out:
        TRACE_EXIT_RES(res);
@@ -2878,7 +2669,6 @@ static int dev_user_get_opt(struct file *file, void *arg)
        opt.parse_type = dev->parse_type;
        opt.on_free_cmd_type = dev->on_free_cmd_type;
        opt.memory_reuse_type = dev->memory_reuse_type;
-       opt.prio_queue_type = dev->prio_queue_type;
        opt.partial_transfers_type = dev->partial_transfers_type;
        opt.partial_len = dev->partial_len;
        opt.tst = dev->tst;
@@ -2940,12 +2730,18 @@ static int dev_user_release(struct inode *inode, struct file *file)
 
        down_write(&dev->dev_rwsem);
 
-       spin_lock(&cleanup_lock);
+       spin_lock_irq(&cleanup_lock);
+
+       dev->cleaning = 1;
+       smp_mb(); /* pair to dev_user_add_to_ready() */
+
+       sBUG_ON(dev->in_cleanup_list);
        list_add_tail(&dev->cleanup_list_entry, &cleanup_list);
-       spin_unlock(&cleanup_lock);
+       dev->in_cleanup_list = 1;
+
+       spin_unlock_irq(&cleanup_lock);
 
        wake_up(&cleanup_list_waitQ);
-       wake_up(&dev->prio_cmd_list_waitQ);
        wake_up(&dev->cmd_lists.cmd_list_waitQ);
 
        scst_unregister_virtual_device(dev->virt_id);
@@ -2956,9 +2752,18 @@ static int dev_user_release(struct inode *inode, struct file *file)
        TRACE_DBG("Unregistering finished (dev %p)", dev);
 
        dev->cleanup_done = 1;
+       smp_mb(); /* just in case */
+
+       spin_lock_irq(&cleanup_lock);
+       if (!dev->in_cleanup_list) {
+               list_add_tail(&dev->cleanup_list_entry, &cleanup_list);
+               dev->in_cleanup_list = 1;
+       }
+       spin_unlock_irq(&cleanup_lock);
+
        wake_up(&cleanup_list_waitQ);
-       wake_up(&dev->prio_cmd_list_waitQ);
        wake_up(&dev->cmd_lists.cmd_list_waitQ);
+
        wait_for_completion(&dev->cleanup_cmpl);
 
        up_write(&dev->dev_rwsem); /* to make the debug check happy */
@@ -2981,24 +2786,30 @@ static void dev_user_process_cleanup(struct scst_user_dev *dev)
 
        TRACE_ENTRY();
 
-       dev->prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
-       dev->cleaning = 1;
-       dev->blocking = 1;
+       dev->blocking = 0;
 
        while (1) {
                TRACE_DBG("Cleanuping dev %p", dev);
 
-               dev_user_unjam_dev(dev, 0, NULL);
+               dev_user_unjam_dev(dev);
 
                spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
-               rc = dev_user_get_next_prio_cmd(dev, &ucmd);
-               if (rc != 0)
-                       rc = dev_user_get_next_cmd(dev, &ucmd);
+               smp_mb(); /* just in case, pair for dev_user_release()
+                          * cleanup_done assignment.
+                          */
+               rc = dev_user_get_next_cmd(dev, &ucmd);
                if (rc == 0)
                        dev_user_unjam_cmd(ucmd, 1, NULL);
                spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
-               if ((rc == -EAGAIN) && dev->cleanup_done)
-                       break;
+
+               if (rc == -EAGAIN) {
+                       if (dev->cleanup_done)
+                               break;
+                       else {
+                               TRACE_DBG("No more commands (dev %p)", dev);
+                               goto out;
+                       }
+               }
        }
 
 #ifdef EXTRACHECKS
@@ -3006,11 +2817,13 @@ static void dev_user_process_cleanup(struct scst_user_dev *dev)
        int i;
        for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++) {
                struct list_head *head = &dev->ucmd_hash[i];
-               struct scst_user_cmd *ucmd, *t;
-               list_for_each_entry_safe(ucmd, t, head, hash_list_entry) {
+               struct scst_user_cmd *ucmd;
+again:
+               list_for_each_entry(ucmd, head, hash_list_entry) {
                        PRINT_ERROR("Lost ucmd %p (state %x, ref %d)", ucmd,
                                ucmd->state, atomic_read(&ucmd->ucmd_ref));
                        ucmd_put(ucmd);
+                       goto again;
                }
        }
 }
@@ -3019,6 +2832,7 @@ static void dev_user_process_cleanup(struct scst_user_dev *dev)
        TRACE_DBG("Cleanuping done (dev %p)", dev);
        complete_all(&dev->cleanup_cmpl);
 
+out:
        TRACE_EXIT();
        return;
 }
@@ -3040,7 +2854,7 @@ static int dev_user_cleanup_thread(void *arg)
 
        current->flags |= PF_NOFREEZE;
 
-       spin_lock(&cleanup_lock);
+       spin_lock_irq(&cleanup_lock);
        while (!kthread_should_stop()) {
                wait_queue_t wait;
                init_waitqueue_entry(&wait, current);
@@ -3051,23 +2865,27 @@ static int dev_user_cleanup_thread(void *arg)
                                set_current_state(TASK_INTERRUPTIBLE);
                                if (test_cleanup_list())
                                        break;
-                               spin_unlock(&cleanup_lock);
+                               spin_unlock_irq(&cleanup_lock);
                                schedule();
-                               spin_lock(&cleanup_lock);
+                               spin_lock_irq(&cleanup_lock);
                        }
                        set_current_state(TASK_RUNNING);
                        remove_wait_queue(&cleanup_list_waitQ, &wait);
                }
-restart:
-               list_for_each_entry(dev, &cleanup_list, cleanup_list_entry) {
+
+               while (!list_empty(&cleanup_list)) {
+                       dev = list_entry(cleanup_list.next, typeof(*dev),
+                               cleanup_list_entry);
                        list_del(&dev->cleanup_list_entry);
-                       spin_unlock(&cleanup_lock);
+                       dev->in_cleanup_list = 0;
+                       spin_unlock_irq(&cleanup_lock);
+
                        dev_user_process_cleanup(dev);
-                       spin_lock(&cleanup_lock);
-                       goto restart;
+
+                       spin_lock_irq(&cleanup_lock);
                }
        }
-       spin_unlock(&cleanup_lock);
+       spin_unlock_irq(&cleanup_lock);
 
        /*
         * If kthread_should_stop() is true, we are guaranteed to be
index b228dad..0fb281c 100644 (file)
@@ -1956,7 +1956,7 @@ static void wait_on_retry_sync_kiocb(struct kiocb *iocb)
 typedef ssize_t (*iov_fn_t)(struct kiocb *, const struct iovec *,
                                unsigned long, loff_t);
 
-ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov,
+static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov,
                unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn)
 {
        struct kiocb kiocb;
@@ -3115,7 +3115,9 @@ static int vcdrom_change(char *p, char *name)
                virt_dev->file_name = fn;
        }
 
-       scst_suspend_activity();
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out_free;
 
        if (virt_dev->prevent_allow_medium_removal) {
                PRINT_ERROR("Prevent medium removal for "
index dba045e..6b36bc3 100644 (file)
@@ -34,6 +34,13 @@ static inline int get_current_tid(void)
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
        return current->pid;
 #else
+       if (in_interrupt()) {
+               /*
+                * Unfortunately, task_pid_vnr() isn't IRQ-safe, so otherwise
+                * it can oops. ToDo.
+                */
+               return 0;
+       }
        return task_pid_vnr(current);
 #endif
 }
index c4ffba8..947d277 100644 (file)
@@ -95,9 +95,12 @@ void scst_set_cmd_error_status(struct scst_cmd *cmd, int status)
        cmd->status = status;
        cmd->host_status = DID_OK;
 
+       cmd->dbl_ua_orig_resp_data_len = cmd->resp_data_len;
+       cmd->dbl_ua_orig_data_direction = cmd->data_direction;
+
        cmd->data_direction = SCST_DATA_NONE;
-       cmd->is_send_status = 1;
        cmd->resp_data_len = 0;
+       cmd->is_send_status = 1;
 
        cmd->completed = 1;
 
@@ -1065,7 +1068,7 @@ static void scst_send_release(struct scst_device *dev)
        TRACE(TRACE_DEBUG | TRACE_SCSI, "Sending RELEASE req %p to SCSI "
                "mid-level", req);
        scst_do_req(req, req->sr_cmnd, (void *)req->sr_buffer, req->sr_bufflen,
-                   scst_req_done, SCST_DEFAULT_TIMEOUT, 3);
+                   scst_req_done, 15, 3);
 
 out:
        TRACE_EXIT();
@@ -1100,7 +1103,7 @@ static void scst_send_release(struct scst_device *dev)
                TRACE(TRACE_DEBUG | TRACE_SCSI, "%s", "Sending RELEASE req to "
                        "SCSI mid-level");
                rc = scsi_execute(scsi_dev, cdb, SCST_DATA_NONE, NULL, 0,
-                               sense, SCST_DEFAULT_TIMEOUT, 0, 0);
+                               sense, 15, 0, 0);
                TRACE_DBG("MODE_SENSE done: %x", rc);
 
                if (scsi_status_is_good(rc)) {
@@ -1320,6 +1323,8 @@ struct scst_cmd *scst_alloc_cmd(int gfp_mask)
        cmd->data_len = -1;
        cmd->is_send_status = 1;
        cmd->resp_data_len = -1;
+       cmd->dbl_ua_orig_resp_data_len = -1;
+       cmd->dbl_ua_orig_data_direction = SCST_DATA_UNKNOWN;
 
 out:
        TRACE_EXIT();
@@ -1996,7 +2001,7 @@ int scst_sbc_generic_parse(struct scst_cmd *cmd,
                if ((cmd->cdb[1] & BYTCHK) == 0) {
                        cmd->data_len = cmd->bufflen << get_block_shift(cmd);
                        cmd->bufflen = 0;
-                       goto out;
+                       goto set_timeout;
                } else
                        cmd->data_len = 0;
                break;
@@ -2013,7 +2018,14 @@ int scst_sbc_generic_parse(struct scst_cmd *cmd,
                cmd->bufflen = cmd->bufflen << get_block_shift(cmd);
        }
 
-out:
+set_timeout:
+       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
+               cmd->timeout = SCST_GENERIC_DISK_REG_TIMEOUT;
+       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_DISK_SMALL_TIMEOUT;
+       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_DISK_LONG_TIMEOUT;
+
        TRACE_DBG("res %d, bufflen %d, data_len %d, direct %d",
              res, cmd->bufflen, cmd->data_len, cmd->data_direction);
 
@@ -2047,7 +2059,7 @@ int scst_cdrom_generic_parse(struct scst_cmd *cmd,
                if ((cmd->cdb[1] & BYTCHK) == 0) {
                        cmd->data_len = cmd->bufflen << get_block_shift(cmd);
                        cmd->bufflen = 0;
-                       goto out;
+                       goto set_timeout;
                }
                break;
        default:
@@ -2058,7 +2070,14 @@ int scst_cdrom_generic_parse(struct scst_cmd *cmd,
        if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED)
                cmd->bufflen = cmd->bufflen << get_block_shift(cmd);
 
-out:
+set_timeout:
+       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
+               cmd->timeout = SCST_GENERIC_CDROM_REG_TIMEOUT;
+       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_CDROM_SMALL_TIMEOUT;
+       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_CDROM_LONG_TIMEOUT;
+
        TRACE_DBG("res=%d, bufflen=%d, direct=%d", res, cmd->bufflen,
                cmd->data_direction);
 
@@ -2092,7 +2111,7 @@ int scst_modisk_generic_parse(struct scst_cmd *cmd,
                if ((cmd->cdb[1] & BYTCHK) == 0) {
                        cmd->data_len = cmd->bufflen << get_block_shift(cmd);
                        cmd->bufflen = 0;
-                       goto out;
+                       goto set_timeout;
                }
                break;
        default:
@@ -2103,7 +2122,14 @@ int scst_modisk_generic_parse(struct scst_cmd *cmd,
        if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED)
                cmd->bufflen = cmd->bufflen << get_block_shift(cmd);
 
-out:
+set_timeout:
+       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
+               cmd->timeout = SCST_GENERIC_MODISK_REG_TIMEOUT;
+       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_MODISK_SMALL_TIMEOUT;
+       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_MODISK_LONG_TIMEOUT;
+
        TRACE_DBG("res=%d, bufflen=%d, direct=%d", res, cmd->bufflen,
                cmd->data_direction);
 
@@ -2145,6 +2171,13 @@ int scst_tape_generic_parse(struct scst_cmd *cmd,
        if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED & cmd->cdb[1])
                cmd->bufflen = cmd->bufflen * get_block_size(cmd);
 
+       if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
+               cmd->timeout = SCST_GENERIC_TAPE_REG_TIMEOUT;
+       else if (cmd->op_flags & SCST_SMALL_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_TAPE_SMALL_TIMEOUT;
+       else if (cmd->op_flags & SCST_LONG_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_TAPE_LONG_TIMEOUT;
+
        TRACE_EXIT_RES(res);
        return res;
 }
@@ -2180,21 +2213,42 @@ static int scst_null_parse(struct scst_cmd *cmd)
 int scst_changer_generic_parse(struct scst_cmd *cmd,
        int (*nothing)(struct scst_cmd *cmd))
 {
-       return scst_null_parse(cmd);
+       int res = scst_null_parse(cmd);
+
+       if (cmd->op_flags & SCST_LONG_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_CHANGER_LONG_TIMEOUT;
+       else
+               cmd->timeout = SCST_GENERIC_CHANGER_TIMEOUT;
+
+       return res;
 }
 EXPORT_SYMBOL(scst_changer_generic_parse);
 
 int scst_processor_generic_parse(struct scst_cmd *cmd,
        int (*nothing)(struct scst_cmd *cmd))
 {
-       return scst_null_parse(cmd);
+       int res = scst_null_parse(cmd);
+
+       if (cmd->op_flags & SCST_LONG_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_PROCESSOR_LONG_TIMEOUT;
+       else
+               cmd->timeout = SCST_GENERIC_PROCESSOR_TIMEOUT;
+
+       return res;
 }
 EXPORT_SYMBOL(scst_processor_generic_parse);
 
 int scst_raid_generic_parse(struct scst_cmd *cmd,
        int (*nothing)(struct scst_cmd *cmd))
 {
-       return scst_null_parse(cmd);
+       int res = scst_null_parse(cmd);
+
+       if (cmd->op_flags & SCST_LONG_TIMEOUT)
+               cmd->timeout = SCST_GENERIC_RAID_LONG_TIMEOUT;
+       else
+               cmd->timeout = SCST_GENERIC_RAID_TIMEOUT;
+
+       return res;
 }
 EXPORT_SYMBOL(scst_raid_generic_parse);
 
@@ -2364,8 +2418,7 @@ int scst_obtain_device_parameters(struct scst_device *dev)
 
                TRACE(TRACE_SCSI, "%s", "Doing internal MODE_SENSE");
                res = scsi_execute(dev->scsi_dev, cmd, SCST_DATA_READ, buffer,
-                          sizeof(buffer), sense_buffer, SCST_DEFAULT_TIMEOUT,
-                           0, 0);
+                               sizeof(buffer), sense_buffer, 15, 0, 0);
 
                TRACE_DBG("MODE_SENSE done: %x", res);
 
index f42eacd..117655f 100644 (file)
@@ -151,6 +151,8 @@ struct scst_dev_type scst_null_devtype = {
        .name = "none",
 };
 
+static void __scst_resume_activity(void);
+
 int __scst_register_target_template(struct scst_tgt_template *vtt,
        const char *version)
 {
@@ -299,12 +301,14 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt,
        const char *target_name)
 {
        struct scst_tgt *tgt;
+       int rc = 0;
 
        TRACE_ENTRY();
 
        tgt = kzalloc(sizeof(*tgt), GFP_KERNEL);
        if (tgt == NULL) {
                TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt failed");
+               rc = -ENOMEM;
                goto out_err;
        }
 
@@ -319,8 +323,14 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt,
        tgt->retry_timer.data = (unsigned long)tgt;
        tgt->retry_timer.function = scst_tgt_retry_timer_fn;
 
-       scst_suspend_activity();
-       mutex_lock(&scst_mutex);
+       rc = scst_suspend_activity(true);
+       if (rc != 0)
+               goto out_free_tgt_err;
+
+       if (mutex_lock_interruptible(&scst_mutex) != 0) {
+               rc = -EINTR;
+               goto out_resume_free;
+       }
 
        if (target_name != NULL) {
                int len = strlen(target_name) + 1 +
@@ -330,13 +340,15 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt,
                if (tgt->default_group_name == NULL) {
                        TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of default "
                                "group name failed");
-                       goto out_free_err;
+                       rc = -ENOMEM;
+                       goto out_unlock_resume;
                }
                sprintf(tgt->default_group_name, "%s_%s", SCST_DEFAULT_ACG_NAME,
                        target_name);
        }
 
-       if (scst_build_proc_target_entries(tgt) < 0)
+       rc = scst_build_proc_target_entries(tgt);
+       if (rc < 0)
                goto out_free_name;
        else
                list_add_tail(&tgt->tgt_list_entry, &vtt->tgt_list);
@@ -354,16 +366,19 @@ out:
 out_free_name:
        kfree(tgt->default_group_name);
 
-out_free_err:
+out_unlock_resume:
        mutex_unlock(&scst_mutex);
+
+out_resume_free:
        scst_resume_activity();
 
+out_free_tgt_err:
        kfree(tgt);
        tgt = NULL;
 
 out_err:
-       PRINT_ERROR("Failed to register target %s for template %s",
-               target_name, vtt->name);
+       PRINT_ERROR("Failed to register target %s for template %s (error %d)",
+               target_name, vtt->name, rc);
        goto out;
 }
 EXPORT_SYMBOL(scst_register);
@@ -398,7 +413,7 @@ void scst_unregister(struct scst_tgt *tgt)
        wait_event(tgt->unreg_waitQ, test_sess_list(tgt));
        TRACE_DBG("%s", "wait_event() returned");
 
-       scst_suspend_activity();
+       scst_suspend_activity(false);
        mutex_lock(&scst_mutex);
 
        list_del(&tgt->tgt_list_entry);
@@ -422,11 +437,44 @@ void scst_unregister(struct scst_tgt *tgt)
 }
 EXPORT_SYMBOL(scst_unregister);
 
-void scst_suspend_activity(void)
+static int scst_susp_wait(bool interruptible)
 {
+       int res = 0;
+
        TRACE_ENTRY();
 
-       mutex_lock(&scst_suspend_mutex);
+       if (interruptible) {
+               res = wait_event_interruptible_timeout(scst_dev_cmd_waitQ,
+                       (atomic_read(&scst_cmd_count) == 0),
+                       SCST_SUSPENDING_TIMEOUT);
+               if (res <= 0) {
+                       __scst_resume_activity();
+                       if (res == 0)
+                               res = -EBUSY;
+               } else
+                       res = 0;
+       } else
+               wait_event(scst_dev_cmd_waitQ, atomic_read(&scst_cmd_count) == 0);
+
+       TRACE_MGMT_DBG("wait_event() returned %d", res);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+int scst_suspend_activity(bool interruptible)
+{
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       if (interruptible) {
+               if (mutex_lock_interruptible(&scst_suspend_mutex) != 0) {
+                       res = -EINTR;
+                       goto out;
+               }
+       } else
+               mutex_lock(&scst_suspend_mutex);
 
        TRACE_MGMT_DBG("suspend_count %d", suspend_count);
        suspend_count++;
@@ -437,39 +485,63 @@ void scst_suspend_activity(void)
        set_bit(SCST_FLAG_SUSPENDED, &scst_flags);
        smp_mb__after_set_bit();
 
-       TRACE_MGMT_DBG("Waiting for %d active commands to complete",
-             atomic_read(&scst_cmd_count));
-       wait_event(scst_dev_cmd_waitQ, atomic_read(&scst_cmd_count) == 0);
-       TRACE_MGMT_DBG("%s", "wait_event() returned");
+       /*
+        * See comment in scst_user.c::dev_user_task_mgmt_fn() for more
+        * information about scst_user behavior.
+        *
+        * ToDo: make the global suspending unneeded (Switch to per-device
+        * reference counting? That would mean to switch off from lockless
+        * implementation of scst_translate_lun().. )
+        */
+       PRINT_INFO("Waiting for %d active commands to complete... This might "
+               "take few minutes for disks or few hours for tapes, if you "
+               "use long executed commands, like REWIND or FORMAT. In case, "
+               "if you have a hung user space device (i.e. made using "
+               "scst_user module) not responding to any commands, if might "
+               "take virtually forever until the corresponding user space "
+               "program recovers and starts responding or gets killed.",
+               atomic_read(&scst_cmd_count));
+
+       res = scst_susp_wait(interruptible);
+       if (res != 0)
+               goto out_clear;
 
        clear_bit(SCST_FLAG_SUSPENDING, &scst_flags);
        smp_mb__after_clear_bit();
 
        TRACE_MGMT_DBG("Waiting for %d active commands finally to complete",
-             atomic_read(&scst_cmd_count));
-       wait_event(scst_dev_cmd_waitQ, atomic_read(&scst_cmd_count) == 0);
-       TRACE_MGMT_DBG("%s", "wait_event() returned");
+               atomic_read(&scst_cmd_count));
+
+       res = scst_susp_wait(interruptible);
+       if (res != 0)
+               goto out_clear;
+
+       PRINT_INFO("%s", "All active commands completed");
 
 out_up:
        mutex_unlock(&scst_suspend_mutex);
 
-       TRACE_EXIT();
-       return;
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_clear:
+       clear_bit(SCST_FLAG_SUSPENDING, &scst_flags);
+       smp_mb__after_clear_bit();
+       goto out_up;
 }
 EXPORT_SYMBOL(scst_suspend_activity);
 
-void scst_resume_activity(void)
+static void __scst_resume_activity(void)
 {
        struct scst_cmd_lists *l;
 
        TRACE_ENTRY();
 
-       mutex_lock(&scst_suspend_mutex);
-
        suspend_count--;
        TRACE_MGMT_DBG("suspend_count %d left", suspend_count);
        if (suspend_count > 0)
-               goto out_up;
+               goto out;
 
        clear_bit(SCST_FLAG_SUSPENDED, &scst_flags);
        smp_mb__after_clear_bit();
@@ -491,7 +563,17 @@ void scst_resume_activity(void)
        spin_unlock_irq(&scst_mcmd_lock);
        wake_up_all(&scst_mgmt_cmd_list_waitQ);
 
-out_up:
+out:
+       TRACE_EXIT();
+       return;
+}
+
+void scst_resume_activity(void)
+{
+       TRACE_ENTRY();
+
+       mutex_lock(&scst_suspend_mutex);
+       __scst_resume_activity();
        mutex_unlock(&scst_suspend_mutex);
 
        TRACE_EXIT();
@@ -507,8 +589,14 @@ static int scst_register_device(struct scsi_device *scsidp)
 
        TRACE_ENTRY();
 
-       scst_suspend_activity();
-       mutex_lock(&scst_mutex);
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out_err;
+
+       if (mutex_lock_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out_resume;
+       }
 
        res = scst_alloc_device(GFP_KERNEL, &dev);
        if (res != 0)
@@ -538,8 +626,11 @@ static int scst_register_device(struct scsi_device *scsidp)
 
 out_up:
        mutex_unlock(&scst_mutex);
+
+out_resume:
        scst_resume_activity();
 
+out_err:
        if (res == 0) {
                PRINT_INFO("Attached SCSI target mid-level at "
                    "scsi%d, channel %d, id %d, lun %d, type %d",
@@ -571,7 +662,7 @@ static void scst_unregister_device(struct scsi_device *scsidp)
 
        TRACE_ENTRY();
 
-       scst_suspend_activity();
+       scst_suspend_activity(false);
        mutex_lock(&scst_mutex);
 
        list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
@@ -663,7 +754,10 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler,
        if (res != 0)
                goto out;
 
-       scst_suspend_activity();
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out;
+
        if (mutex_lock_interruptible(&scst_mutex) != 0) {
                res = -EINTR;
                goto out_resume;
@@ -720,7 +814,7 @@ void scst_unregister_virtual_device(int id)
 
        TRACE_ENTRY();
 
-       scst_suspend_activity();
+       scst_suspend_activity(false);
        mutex_lock(&scst_mutex);
 
        list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
@@ -791,7 +885,10 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type,
        }
 #endif
 
-       scst_suspend_activity();
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out_error;
+
        if (mutex_lock_interruptible(&scst_mutex) != 0) {
                res = -EINTR;
                goto out_err_res;
@@ -855,7 +952,7 @@ void scst_unregister_dev_driver(struct scst_dev_type *dev_type)
 
        TRACE_ENTRY();
 
-       scst_suspend_activity();
+       scst_suspend_activity(false);
        mutex_lock(&scst_mutex);
 
        list_for_each_entry(dt, &scst_dev_type_list, dev_type_list_entry) {
index 3ea0051..b180ff3 100644 (file)
@@ -120,7 +120,6 @@ extern unsigned long scst_trace_flag;
 #define SCST_MAX_DEV_COMMANDS                256
 
 #define SCST_TGT_RETRY_TIMEOUT               (3/2*HZ)
-#define SCST_CMD_MEM_TIMEOUT                 (120*HZ)
 
 static inline int scst_get_context(void)
 {
@@ -527,6 +526,7 @@ static inline void scst_check_restore_sg_buff(struct scst_cmd *cmd)
                        cmd->orig_sg_cnt);
                cmd->sg[cmd->orig_sg_entry].length = cmd->orig_entry_len;
                cmd->sg_cnt = cmd->orig_sg_cnt;
+               cmd->sg_buff_modified = 0;
        }
 }
 
index 85a9f08..1c2b531 100644 (file)
@@ -1209,7 +1209,7 @@ out:
 static ssize_t scst_proc_scsi_tgt_gen_write(struct file *file, const char __user *buf,
                                        size_t length, loff_t *off)
 {
-       int res = length, rc = 0, action;
+       int res, rc = 0, action;
        char *buffer, *p;
        struct scst_acg *a, *acg = NULL;
 
@@ -1262,13 +1262,17 @@ static ssize_t scst_proc_scsi_tgt_gen_write(struct file *file, const char __user
                goto out_free;
        }
 
-       scst_suspend_activity();
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out_free;
 
        if (mutex_lock_interruptible(&scst_mutex) != 0) {
                res = -EINTR;
                goto out_free_resume;
        }
 
+       res = length;
+
        switch (action) {
        case SCST_PROC_ACTION_ADD_GROUP:
        case SCST_PROC_ACTION_DEL_GROUP:
@@ -1426,7 +1430,7 @@ out_synt_err:
 static ssize_t scst_proc_groups_devices_write(struct file *file, const char __user *buf,
                                          size_t length, loff_t *off)
 {
-       int res = length, action, virt = 0, rc, read_only = 0;
+       int res, action, virt = 0, rc, read_only = 0;
        char *buffer, *p, *e = NULL;
        unsigned int host, channel = 0, id = 0, lun = 0, virt_lun;
        struct scst_acg *acg = (struct scst_acg *)PDE(file->f_dentry->d_inode)->data;
@@ -1481,13 +1485,17 @@ static ssize_t scst_proc_groups_devices_write(struct file *file, const char __us
                goto out_free;
        }
 
-       scst_suspend_activity();
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out_free;
 
        if (mutex_lock_interruptible(&scst_mutex) != 0) {
                res = -EINTR;
                goto out_free_resume;
        }
 
+       res = length;
+
        switch (action) {
        case SCST_PROC_ACTION_ADD:
        case SCST_PROC_ACTION_DEL:
@@ -1565,7 +1573,7 @@ static ssize_t scst_proc_groups_devices_write(struct file *file, const char __us
                }
                if (acg_dev) {
                        acg_dev = acg_dev_tmp;
-                       PRINT_ERROR("virt lun %d exist in group %s",
+                       PRINT_ERROR("virt lun %d already exists in group %s",
                                       virt_lun, acg->acg_name);
                        res = -EINVAL;
                        goto out_free_up;
index c176a78..655bc69 100644 (file)
@@ -540,16 +540,26 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
                        }
                }
                if (unlikely(cmd->bufflen != cmd->expected_transfer_len)) {
-                       TRACE(TRACE_MINOR, "Warning: expected transfer length "
-                               "%d for opcode 0x%02x (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_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb,
-                               cmd->cdb_len);
+                       static int repd;
+                       
+                       if (repd < 100) {
+                               /*
+                                * Intentionally unlocked. Few messages more
+                                * or less don't matter.
+                                */
+                               repd++;
+                               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_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB",
+                                       cmd->cdb, cmd->cdb_len);
+                       }
                }
 #endif
        }
@@ -2079,6 +2089,12 @@ static int scst_check_sense(struct scst_cmd *cmd)
                                                cmd->driver_status = 0;
                                                mempool_free(cmd->sense, scst_sense_mempool);
                                                cmd->sense = NULL;
+                                               sBUG_ON(cmd->dbl_ua_orig_resp_data_len < 0);
+                                               sBUG_ON(cmd->sg_buff_modified);
+                                               cmd->data_direction =
+                                                       cmd->dbl_ua_orig_data_direction;
+                                               cmd->resp_data_len =
+                                                       cmd->dbl_ua_orig_resp_data_len;
                                                cmd->retry = 1;
                                                cmd->state = SCST_CMD_STATE_SEND_TO_MIDLEV;
                                                res = 1;
@@ -2226,7 +2242,8 @@ static int scst_done_cmd_check(struct scst_cmd *cmd, int *pres)
 #ifdef EXTRACHECKS
                                if (buffer[SCST_INQ_BYTE3] & SCST_INQ_NORMACA_BIT) {
                                        PRINT_INFO("NormACA set for device: "
-                                           "lun=%Ld, type 0x%02x",
+                                           "lun=%Ld, type 0x%02x. Clear it, "
+                                           "since it's unsupported.",
                                            (long long unsigned int)cmd->lun,
                                            buffer[0]);
                                }
@@ -3425,11 +3442,82 @@ void scst_done_cmd_mgmt(struct scst_cmd *cmd)
        return;
 }
 
+/* Called under scst_mcmd_lock and IRQs disabled */
+static int __scst_dec_finish_wait_count(struct scst_mgmt_cmd *mcmd, bool *wake)
+{
+       TRACE_ENTRY();
+
+       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);
+               goto out;
+       }
+
+       if (mcmd->completed) {
+               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 = true;
+       }
+
+out:
+       TRACE_EXIT_RES(mcmd->cmd_finish_wait_count);
+       return mcmd->cmd_finish_wait_count;
+}
+
+/* No locks */
+void scst_prepare_async_mcmd(struct scst_mgmt_cmd *mcmd)
+{
+       unsigned long flags;
+
+       TRACE_ENTRY();
+
+       TRACE_MGMT_DBG("Preparing mcmd %p for async execution", mcmd);
+
+       spin_lock_irqsave(&scst_mcmd_lock, flags);
+        mcmd->cmd_finish_wait_count++;
+       spin_unlock_irqrestore(&scst_mcmd_lock, flags);
+
+       TRACE_EXIT();
+       return;
+}
+EXPORT_SYMBOL(scst_prepare_async_mcmd);
+
+/* No locks */
+void scst_async_mcmd_completed(struct scst_mgmt_cmd *mcmd, int status)
+{
+       unsigned long flags;
+       bool wake = false;
+
+       TRACE_ENTRY();
+
+       TRACE_MGMT_DBG("Async mcmd %p completed (status %d)", mcmd, status);
+
+       spin_lock_irqsave(&scst_mcmd_lock, flags);
+
+       if (status != SCST_MGMT_STATUS_SUCCESS)
+               mcmd->status = status;
+
+       __scst_dec_finish_wait_count(mcmd, &wake);
+
+       spin_unlock_irqrestore(&scst_mcmd_lock, flags);
+
+       if (wake)
+               wake_up(&scst_mgmt_cmd_list_waitQ);
+
+       TRACE_EXIT();
+       return;
+}
+EXPORT_SYMBOL(scst_async_mcmd_completed);
+
 /* No locks */
 static void scst_finish_cmd_mgmt(struct scst_cmd *cmd)
 {
        struct scst_mgmt_cmd_stub *mstb, *t;
-       bool wake = 0;
+       bool wake = false;
        unsigned long flags;
 
        TRACE_ENTRY();
@@ -3452,21 +3540,11 @@ static void scst_finish_cmd_mgmt(struct scst_cmd *cmd)
                if (cmd->completed)
                        mcmd->completed_cmd_count++;
 
-               mcmd->cmd_finish_wait_count--;
-               if (mcmd->cmd_finish_wait_count > 0) {
+               if (__scst_dec_finish_wait_count(mcmd, &wake) > 0) {
                        TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, "
                                "skipping", mcmd->cmd_finish_wait_count);
                        continue;
                }
-
-               if (mcmd->completed) {
-                       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);
@@ -3513,10 +3591,7 @@ static inline int scst_is_strict_mgmt_fn(int mgmt_fn)
        }
 }
 
-/*
- * Might be called under sess_list_lock and IRQ off + BHs also off
- * Returns -1 if command is being executed (ABORT failed), 0 otherwise
- */
+/* Might be called under sess_list_lock and IRQ off + BHs also off */
 void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
        int other_ini, int call_dev_task_mgmt_fn)
 {
@@ -3547,7 +3622,6 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
 
        spin_unlock_irqrestore(&other_ini_lock, flags);
 
-
        /*
         * To sync with cmd->finished/done set in
         * scst_finish_cmd()/scst_pre_xmit_response()
@@ -3587,12 +3661,15 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
                        &cmd->mgmt_cmd_list);
 
                /*
-                * Delay the response until the command's finish in
-                * order to guarantee that "no further responses from
-                * the task are sent to the SCSI initiator port" after
-                * response from the TM function is sent (SAM). Plus,
-                * we must wait here to be sure that we won't receive
-                * double commands with the same tag.
+                * Delay the response until the command's finish in order to
+                * guarantee that "no further responses from the task are sent
+                * to the SCSI initiator port" after response from the TM
+                * function is sent (SAM). Plus, we must wait here to be sure
+                * that we won't receive double commands with the same tag.
+                * Moreover, if we don't wait here, we might have a possibility
+                * for data corruption, when aborted and reported as completed
+                * command actually gets executed *after* new commands sent
+                * after this TM command completed.
                 */
                TRACE_MGMT_DBG("cmd %p (tag %llu) being executed/xmitted "
                        "(state %d, proc time %ld sec.), deferring ABORT...",
@@ -4853,6 +4930,7 @@ static int scst_init_session(struct scst_session *sess)
 
 #ifdef CONFIG_LOCKDEP
        if (res == 0) {
+               /* ToDo: make it on-stack */
                sess->shutdown_compl = kmalloc(sizeof(*sess->shutdown_compl),
                        GFP_KERNEL);
                if (sess->shutdown_compl == NULL)
@@ -4984,7 +5062,7 @@ void scst_unregister_session_ex(struct scst_session *sess, int wait,
                        rc, sess);
        }
 
-       sess->shut_phase = SCST_SESS_SPH_PRE_UNREG;
+       sess->shut_phase = SCST_SESS_SPH_SHUTDOWN;
 
        spin_lock_irqsave(&scst_mgmt_lock, flags);
 
@@ -4995,12 +5073,9 @@ void scst_unregister_session_ex(struct scst_session *sess, int wait,
                 sess->shutdown_compl = NULL;
 #endif
 
-       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);
+       scst_sess_put(sess);
 
        if (wait) {
                TRACE_DBG("Waiting for session %p to complete", sess);
@@ -5016,41 +5091,6 @@ void scst_unregister_session_ex(struct scst_session *sess, int wait,
 }
 EXPORT_SYMBOL(scst_unregister_session_ex);
 
-static void scst_pre_unreg_sess(struct scst_session *sess)
-{
-       int i;
-       struct scst_tgt_dev *tgt_dev;
-
-       TRACE_ENTRY();
-
-       tm_dbg_task_mgmt(NULL, "UNREGISTER SESSION", 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];
-               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;
-
-       scst_sess_put(sess);
-
-       TRACE_EXIT();
-       return;
-}
-
 static inline int test_mgmt_list(void)
 {
        int res = !list_empty(&scst_sess_init_list) ||
@@ -5120,9 +5160,6 @@ int scst_mgmt_thread(void *arg)
                        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);
index ecef595..c832cc6 100644 (file)
@@ -210,7 +210,7 @@ static int do_exec(struct vdisk_cmd *vcmd)
 #endif
 
 #ifdef DEBUG_TM_IGNORE
-       if (dev->debug_tm_ignore && (random() % 200000) == 75) {
+       if (dev->debug_tm_ignore && (random() % 10000) == 75) {
                TRACE_MGMT_DBG("Ignore cmd op %x (h=%d)", cdb[0],
                        vcmd->cmd->cmd_h);
                res = 150;
@@ -726,7 +726,11 @@ void *main_loop(void *arg)
                        default:
                                PRINT_ERROR("SCST_USER_REPLY_AND_GET_CMD failed: "
                                        "%s (%d)", strerror(res), res);
+#if 1
+                               continue;
+#else
                                goto out_close;
+#endif
                        }
 again_poll:
                        res = poll(&pl, 1, 2000);
@@ -746,7 +750,11 @@ again_poll:
                                        goto again_poll;
                                default:
                                        PRINT_ERROR("poll() failed: %s", strerror(res));
+#if 1
+                                       goto again_poll;
+#else
                                        goto out_close;
+#endif
                                }
                        }
                }
@@ -791,8 +799,6 @@ again_poll:
                        break;
 
                case SCST_USER_TASK_MGMT:
-                       if (dev->prio_thr)
-                               goto err;
                        res = do_tm(&vcmd);
 #if DEBUG_TM_FN_IGNORE
                        if (dev->debug_tm_ignore) {
@@ -803,13 +809,10 @@ again_poll:
 
                case SCST_USER_ATTACH_SESS:
                case SCST_USER_DETACH_SESS:
-                       if (dev->prio_thr)
-                               goto err;
                        res = do_sess(&vcmd);
                        break;
 
                default:
-err:
                        PRINT_ERROR("Unknown or wrong cmd subcode %x",
                                cmd.subcode);
                        goto out_close;
@@ -832,78 +835,6 @@ out:
        return (void *)(long)res;
 }
 
-void *prio_loop(void *arg)
-{
-       int res = 0;
-       struct vdisk_dev *dev = (struct vdisk_dev *)arg;
-       struct scst_user_get_cmd cmd;
-       struct scst_user_reply_cmd reply;
-       struct vdisk_cmd vcmd = { -1, &cmd, dev, &reply, {0}};
-       int scst_usr_fd = dev->scst_usr_fd;
-
-       TRACE_ENTRY();
-
-       cmd.preply = 0;
-
-       while(1) {
-               res = ioctl(scst_usr_fd, SCST_USER_REPLY_AND_GET_PRIO_CMD, &cmd);
-               if (res != 0) {
-                       res = errno;
-                       switch(res) {
-                       case ESRCH:
-                       case EBUSY:
-                       case EINTR:
-                       case EAGAIN:
-                               TRACE_MGMT_DBG("SCST_USER_REPLY_AND_GET_PRIO_CMD returned "
-                                       "%d (%s)", res, strerror(res));
-                               cmd.preply = 0;
-                               continue;
-                       default:
-                               PRINT_ERROR("SCST_USER_REPLY_AND_GET_PRIO_CMD failed: "
-                                       "%s (%d)", strerror(res), res);
-                               goto out_close;
-                       }
-               }
-
-               TRACE_BUFFER("Received cmd", &cmd, sizeof(cmd));
-
-               switch(cmd.subcode) {
-               case SCST_USER_TASK_MGMT:
-                       res = do_tm(&vcmd);
-#if DEBUG_TM_FN_IGNORE
-                       if (dev->debug_tm_ignore) {
-                               sleep(15);
-                       }
-#endif
-                       break;
-
-               case SCST_USER_ATTACH_SESS:
-               case SCST_USER_DETACH_SESS:
-                       res = do_sess(&vcmd);
-                       break;
-
-               default:
-                       PRINT_ERROR("Unknown or wrong prio cmd subcode %x",
-                               cmd.subcode);
-                       goto out_close;
-               }
-
-               if (res != 0)
-                       goto out_close;
-
-               cmd.preply = (unsigned long)&reply;
-               TRACE_BUFFER("Sending reply", &reply, sizeof(reply));
-       }
-
-out_close:
-       close(vcmd.fd);
-
-       PRINT_INFO("Prio thread %d exited (res=%d)", gettid(), res);
-
-       TRACE_EXIT_RES(res);
-       return (void *)(long)res;
-}
-
 static void exec_inquiry(struct vdisk_cmd *vcmd)
 {
        struct vdisk_dev *dev = vcmd->dev;
index c21abe9..9286832 100644 (file)
@@ -72,7 +72,6 @@ struct vdisk_dev {
        unsigned int nullio:1;
        unsigned int cdrom_empty:1;
        unsigned int non_blocking:1;
-       unsigned int prio_thr:1;
 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
        unsigned int debug_tm_ignore:1;
 #if defined(DEBUG_TM_IGNORE_ALL)
@@ -118,4 +117,3 @@ struct vdisk_cmd
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
 void *main_loop(void *arg);
-void *prio_loop(void *arg);
index a81ac47..b8393da 100644 (file)
@@ -80,7 +80,6 @@ static struct option const long_options[] =
        {"parse", required_argument, 0, 'p'},
        {"on_free", required_argument, 0, 'f'},
        {"mem_reuse", required_argument, 0, 'm'},
-       {"prio_thread", no_argument, 0, 's'},
        {"non_blocking", no_argument, 0, 'l'},
 #if defined(DEBUG) || defined(TRACING)
        {"debug", required_argument, 0, 'd'},
@@ -110,7 +109,6 @@ static void usage(void)
                "(default) or \"call\"\n");
        printf("  -m, --mem_reuse=type  Memory reuse type, one of \"all\" "
                "(default), \"read\", \"write\" or \"none\"\n");
-       printf("  -s, --prio_thread     Use separate thread for mgmt (prio) commands\n");
        printf("  -l, --non_blocking    Use non-blocking operations\n");
 #if defined(DEBUG) || defined(TRACING)
        printf("  -d, --debug=level     Debug tracing level\n");
@@ -178,7 +176,7 @@ int main(int argc, char **argv)
        dev.type = TYPE_DISK;
        dev.alloc_fn = align_alloc;
 
-       while ((ch = getopt_long(argc, argv, "+b:e:tronsglcp:f:m:d:vh", long_options,
+       while ((ch = getopt_long(argc, argv, "+b:e:tronglcp:f:m:d:vh", long_options,
                                &longindex)) >= 0) {
                switch (ch) {
                case 'b':
@@ -245,9 +243,6 @@ int main(int argc, char **argv)
                        else
                                goto out_usage;
                        break;
-               case 's':
-                       dev.prio_thr = 1;
-                       break;
                case 'l':
                        dev.non_blocking = 1;
                        break;
@@ -352,10 +347,6 @@ int main(int argc, char **argv)
                dev.alloc_fn = malloc;
        }
 
-       if (dev.prio_thr) {
-               PRINT_INFO("    %s", "Using separate prio thread");
-       }
-
 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
        if (dev.debug_tm_ignore) {
                PRINT_INFO("    %s", "DEBUG_TM_IGNORE");
@@ -385,10 +376,6 @@ int main(int argc, char **argv)
        desc.opt.parse_type = parse_type;
        desc.opt.on_free_cmd_type = on_free_cmd_type;
        desc.opt.memory_reuse_type = memory_reuse_type;
-       if (dev.prio_thr)
-               desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
-       else
-               desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
 
        desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS;
        desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
@@ -415,10 +402,6 @@ int main(int argc, char **argv)
                opt.parse_type = parse_type;
                opt.on_free_cmd_type = on_free_cmd_type;
                opt.memory_reuse_type = memory_reuse_type;
-               if (dev.prio_thr)
-                       opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
-               else
-                       opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
 
                res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
                if (res != 0) {
@@ -438,9 +421,9 @@ int main(int argc, char **argv)
 
        {
                pthread_t thread[threads];
-               pthread_t prio;
                int i, j, rc;
                void *rc1;
+
                for(i = 0; i < threads; i++) {
                        rc = pthread_create(&thread[i], NULL, main_loop, &dev);
                        if (rc != 0) {
@@ -451,16 +434,6 @@ int main(int argc, char **argv)
                        }
                }
 
-               if (dev.prio_thr) {
-                       rc = pthread_create(&prio, NULL, prio_loop, &dev);
-                       if (rc != 0) {
-                               res = errno;
-                               PRINT_ERROR("Prio pthread_create() failed: %s",
-                                       strerror(res));
-                               dev.prio_thr = 0;
-                       }
-               }
-
                j = i;
                for(i = 0; i < j; i++) {
                        rc = pthread_join(thread[i], &rc1);
@@ -475,19 +448,6 @@ int main(int argc, char **argv)
                        } else
                                PRINT_INFO("Thread %d exited", i);
                }
-               if (dev.prio_thr) {
-                       rc = pthread_join(prio, &rc1);
-                       if (rc != 0) {
-                               res = errno;
-                               PRINT_ERROR("Prio pthread_join() failed: %s",
-                                       strerror(res));
-                       } else if (rc1 != NULL) {
-                               res = (long)rc1;
-                               PRINT_INFO("Prio thread %d exited, res %lx", i,
-                                       (long)rc1);
-                       } else
-                               PRINT_INFO("Prio thread %d exited", i);
-               }
        }
 
        pthread_mutex_destroy(&dev.dev_mutex);