From: <vl...@us...> - 2008-05-31 12:05:15
|
Revision: 403 http://scst.svn.sourceforge.net/scst/?rev=403&view=rev Author: vlnb Date: 2008-05-31 05:05:02 -0700 (Sat, 31 May 2008) Log Message: ----------- The major TM processing cleanup in scst_user module which was possible after the recent SCST changes, to fix current problems. Also there are fixes for found during development/testing problems. Particularly: - 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. Modified Paths: -------------- trunk/doc/scst_user_spec.txt trunk/iscsi-scst/README trunk/iscsi-scst/kernel/config.c trunk/iscsi-scst/kernel/iscsi.h trunk/iscsi-scst/kernel/target.c trunk/iscsi-scst/usr/ctldev.c trunk/iscsi-scst/usr/iscsid.c trunk/qla2x00t/qla2x00-target/README trunk/scst/README trunk/scst/include/scst.h trunk/scst/include/scst_const.h trunk/scst/include/scst_debug.h trunk/scst/include/scst_user.h trunk/scst/src/dev_handlers/scst_cdrom.c trunk/scst/src/dev_handlers/scst_changer.c trunk/scst/src/dev_handlers/scst_disk.c trunk/scst/src/dev_handlers/scst_modisk.c trunk/scst/src/dev_handlers/scst_processor.c trunk/scst/src/dev_handlers/scst_raid.c trunk/scst/src/dev_handlers/scst_tape.c trunk/scst/src/dev_handlers/scst_user.c trunk/scst/src/dev_handlers/scst_vdisk.c trunk/scst/src/scst_debug.c trunk/scst/src/scst_lib.c trunk/scst/src/scst_main.c trunk/scst/src/scst_priv.h trunk/scst/src/scst_proc.c trunk/scst/src/scst_targ.c trunk/usr/fileio/common.c trunk/usr/fileio/common.h trunk/usr/fileio/fileio.c Modified: trunk/doc/scst_user_spec.txt =================================================================== --- trunk/doc/scst_user_spec.txt 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/doc/scst_user_spec.txt 2008-05-31 12:05:02 UTC (rev 403) @@ -2,7 +2,7 @@ USER SPACE INTERFACE DESCRIPTION. - Version 0.9.6/3 + Version 0.9.6/4 I. Description. @@ -103,7 +103,6 @@ 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 @@ * 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 @@ 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,24 +702,9 @@ - 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. + 5. SCST_USER_REPLY_CMD -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 - 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 @@ 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 Modified: trunk/iscsi-scst/README =================================================================== --- trunk/iscsi-scst/README 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/iscsi-scst/README 2008-05-31 12:05:02 UTC (rev 403) @@ -32,6 +32,16 @@ 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 @@ debugging * Tomasz Chmielewski <ma...@wp...> for testing and suggestions - + + * Bart Van Assche <bar...@gm...> for a lot of help + Vladislav Bolkhovitin <vs...@vl...>, http://scst.sourceforge.net Modified: trunk/iscsi-scst/kernel/config.c =================================================================== --- trunk/iscsi-scst/kernel/config.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/iscsi-scst/kernel/config.c 2008-05-31 12:05:02 UTC (rev 403) @@ -408,9 +408,27 @@ 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 @@ } 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 @@ break; default: - PRINT_ERROR("invalid ioctl cmd %x", cmd); - err = -EINVAL; + sBUG(); break; } Modified: trunk/iscsi-scst/kernel/iscsi.h =================================================================== --- trunk/iscsi-scst/kernel/iscsi.h 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/iscsi-scst/kernel/iscsi.h 2008-05-31 12:05:02 UTC (rev 403) @@ -467,7 +467,9 @@ { 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); } Modified: trunk/iscsi-scst/kernel/target.c =================================================================== --- trunk/iscsi-scst/kernel/target.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/iscsi-scst/kernel/target.c 2008-05-31 12:05:02 UTC (rev 403) @@ -119,6 +119,7 @@ target->scst_tgt = scst_register(&iscsi_template, target->name); if (!target->scst_tgt) { PRINT_ERROR("%s", "scst_register() failed"); + err = -EBUSY; goto out_free; } Modified: trunk/iscsi-scst/usr/ctldev.c =================================================================== --- trunk/iscsi-scst/usr/ctldev.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/iscsi-scst/usr/ctldev.c 2008-05-31 12:05:02 UTC (rev 403) @@ -83,6 +83,7 @@ } reg.version = (uintptr_t)ISCSI_SCST_INTERFACE_VERSION; + err = ioctl(ctlfd, REGISTER_USERD, ®); if (err < 0) { log_error("Unable to register: %s. Incompatible version of the " Modified: trunk/iscsi-scst/usr/iscsid.c =================================================================== --- trunk/iscsi-scst/usr/iscsid.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/iscsi-scst/usr/iscsid.c 2008-05-31 12:05:02 UTC (rev 403) @@ -109,7 +109,16 @@ 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; } Modified: trunk/qla2x00t/qla2x00-target/README =================================================================== --- trunk/qla2x00t/qla2x00-target/README 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/qla2x00t/qla2x00-target/README 2008-05-31 12:05:02 UTC (rev 403) @@ -15,9 +15,7 @@ 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 @@ 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. Modified: trunk/scst/README =================================================================== --- trunk/scst/README 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/README 2008-05-31 12:05:02 UTC (rev 403) @@ -42,11 +42,19 @@ 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,10 +484,24 @@ 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 journaled file systems, laying over them, point of view, but @@ -835,4 +857,6 @@ * Jianxi Chen <pa...@us...> for fixing problem with devices >2TB in size + * Bart Van Assche <bar...@gm...> for a lot of help + Vladislav Bolkhovitin <vs...@vl...>, http://scst.sourceforge.net Modified: trunk/scst/include/scst.h =================================================================== --- trunk/scst/include/scst.h 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/include/scst.h 2008-05-31 12:05:02 UTC (rev 403) @@ -289,12 +289,6 @@ */ #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 @@ /* 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 @@ -384,6 +375,11 @@ #define SCST_PROC_ENTRY_NAME "scsi_tgt" /************************************************************* + ** Activities suspending timeout + *************************************************************/ +#define SCST_SUSPENDING_TIMEOUT (90 * HZ) + +/************************************************************* ** Kernel cache creation helper *************************************************************/ #ifndef KMEM_CACHE @@ -773,7 +769,7 @@ * - 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 @@ */ 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 @@ 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 @@ */ 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 @@ 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 @@ /* * 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); /* Modified: trunk/scst/include/scst_const.h =================================================================== --- trunk/scst/include/scst_const.h 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/include/scst_const.h 2008-05-31 12:05:02 UTC (rev 403) @@ -280,4 +280,36 @@ #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 */ Modified: trunk/scst/include/scst_debug.h =================================================================== --- trunk/scst/include/scst_debug.h 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/include/scst_debug.h 2008-05-31 12:05:02 UTC (rev 403) @@ -242,6 +242,15 @@ 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 @@ 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 @@ 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*** " \ Modified: trunk/scst/include/scst_user.h =================================================================== --- trunk/scst/include/scst_user.h 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/include/scst_user.h 2008-05-31 12:05:02 UTC (rev 403) @@ -49,10 +49,6 @@ #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 @@ -77,20 +73,10 @@ #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 @@ 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 @@ uint8_t queue_type; uint8_t data_direction; uint8_t partial; - uint32_t timeout; + int32_t timeout; uint32_t sn; @@ -249,8 +235,7 @@ #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) Modified: trunk/scst/src/dev_handlers/scst_cdrom.c =================================================================== --- trunk/scst/src/dev_handlers/scst_cdrom.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_cdrom.c 2008-05-31 12:05:02 UTC (rev 403) @@ -38,10 +38,6 @@ .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 @@ 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 @@ 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; } Modified: trunk/scst/src/dev_handlers/scst_changer.c =================================================================== --- trunk/scst/src/dev_handlers/scst_changer.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_changer.c 2008-05-31 12:05:02 UTC (rev 403) @@ -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 @@ 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 @@ cmd->retries = SCST_PASSTHROUGH_RETRIES; - if (cmd->op_flags & SCST_LONG_TIMEOUT) - cmd->timeout = CHANGER_LONG_TIMEOUT; - else - cmd->timeout = CHANGER_TIMEOUT; - return res; } Modified: trunk/scst/src/dev_handlers/scst_disk.c =================================================================== --- trunk/scst/src/dev_handlers/scst_disk.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_disk.c 2008-05-31 12:05:02 UTC (rev 403) @@ -57,10 +57,6 @@ .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 @@ 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 @@ 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; } Modified: trunk/scst/src/dev_handlers/scst_modisk.c =================================================================== --- trunk/scst/src/dev_handlers/scst_modisk.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_modisk.c 2008-05-31 12:05:02 UTC (rev 403) @@ -57,10 +57,6 @@ .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 @@ 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 @@ 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; } Modified: trunk/scst/src/dev_handlers/scst_processor.c =================================================================== --- trunk/scst/src/dev_handlers/scst_processor.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_processor.c 2008-05-31 12:05:02 UTC (rev 403) @@ -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 @@ 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 @@ cmd->retries = SCST_PASSTHROUGH_RETRIES; - if (cmd->op_flags & SCST_LONG_TIMEOUT) - cmd->timeout = PROCESSOR_LONG_TIMEOUT; - else - cmd->timeout = PROCESSOR_TIMEOUT; return res; } Modified: trunk/scst/src/dev_handlers/scst_raid.c =================================================================== --- trunk/scst/src/dev_handlers/scst_raid.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_raid.c 2008-05-31 12:05:02 UTC (rev 403) @@ -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 @@ 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 @@ cmd->retries = SCST_PASSTHROUGH_RETRIES; - if (cmd->op_flags & SCST_LONG_TIMEOUT) - cmd->timeout = RAID_LONG_TIMEOUT; - else - cmd->timeout = RAID_TIMEOUT; return res; } Modified: trunk/scst/src/dev_handlers/scst_tape.c =================================================================== --- trunk/scst/src/dev_handlers/scst_tape.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_tape.c 2008-05-31 12:05:02 UTC (rev 403) @@ -59,10 +59,6 @@ #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 @@ 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 @@ ((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 @@ 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; } Modified: trunk/scst/src/dev_handlers/scst_user.c =================================================================== --- trunk/scst/src/dev_handlers/scst_user.c 2008-05-29 17:24:18 UTC (rev 402) +++ trunk/scst/src/dev_handlers/scst_user.c 2008-05-31 12:05:02 UTC (rev 403) @@ -34,38 +34,29 @@ #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 */ + /* 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; - 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; - int (*generic_parse)(struct scst_cmd *cmd, int (*get_block)(struct scst_cmd *cmd)); @@ -77,7 +68,6 @@ 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 @@ /* 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 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 @@ 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 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_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_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,17 +202,54 @@ 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); if (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 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 @@ 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 @@ page_cache_release(page); } + kfree(ucmd->data_pages); ucmd->data_pages = NULL; @@ -532,7 +561,7 @@ 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 @@ 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 @@ 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 @@ 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,11 +893,15 @@ 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; ucmd->user_cmd.subcode = SCST_USER_ON_FREE_CMD; @@ -886,6 +919,10 @@ 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 @@ 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; - } - } + ucmd->this_state_unjammed = 0; - 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; - } + 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 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ sBUG(); break; } + +out_put: + ucmd_put(ucmd); + out: TRACE_EXIT_RES(res); return res; @@ -1412,6 +1445,10 @@ 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 @@ 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 @@ &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 @@ } } } - u->state |= UCMD_STATE_SENT_MASK; + u->sent_to_user = 1; + u->seen_by_user = 1; } return u; } @@ -1600,73 +1639,9 @@ return res; } -static inline int test_prio_cmd_list(struct scst_user_dev *dev) +static int dev_user_reply_get_cmd(struct file *file, unsigned long arg) { - /* - * 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) -{ - int res = 0; struct scst_user_dev *dev; struct scst_user_get_cmd *cmd; struct scst_user_reply_cmd *reply; @@ -1712,10 +1687,7 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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,56 +1940,8 @@ return; } -static int __unjam_check_tgt_dev(struct scst_user_cmd *ucmd, int state, - struct scst_tgt_dev *tgt_dev) +static void dev_user_unjam_dev(struct scst_user_dev *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) -{ int i; unsigned long flags; struct scst_user_cmd *ucmd; @@ -2031,41 +1955,24 @@ 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,72 +1985,117 @@ 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); + scst_async_mcmd_completed(ucmd->mcmd, status); - spin_unlock_irqrestore(&ucmd->dev->cmd_lists.cmd_list_lock, flags); - ucmd_put(ucmd); TRACE_EXIT_RES(res); 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 po... [truncated message content] |