From 62b075c66d7f54801d3aa33d77306712b35bf612 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 3 Apr 2007 15:44:44 +0000 Subject: [PATCH] Various fixes, cleanups updates and preparations git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@108 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/README | 4 + scst/include/scsi_tgt.h | 317 +++++++----------- scst/include/scst_const.h | 243 ++++++++++++++ scst/include/scst_debug.h | 8 +- scst/src/Makefile | 5 +- scst/src/dev_handlers/Makefile | 6 +- scst/src/dev_handlers/scst_cdrom.c | 27 +- scst/src/dev_handlers/scst_changer.c | 9 +- scst/src/dev_handlers/scst_dev_handler.h | 4 +- scst/src/dev_handlers/scst_disk.c | 26 +- scst/src/dev_handlers/scst_modisk.c | 26 +- scst/src/dev_handlers/scst_processor.c | 9 +- scst/src/dev_handlers/scst_raid.c | 9 +- scst/src/dev_handlers/scst_tape.c | 23 +- scst/src/dev_handlers/scst_vdisk.c | 42 +-- scst/src/scst.c | 69 ++-- scst/src/scst_cdbprobe.h | 4 +- scst/src/scst_debug.c | 7 +- scst/src/scst_lib.c | 231 ++++++------- scst/src/scst_mem.c | 396 +++++++++++++++-------- scst/src/scst_mem.h | 51 +-- scst/src/scst_module.c | 2 +- scst/src/scst_priv.h | 28 +- scst/src/scst_proc.c | 80 ++--- scst/src/scst_targ.c | 264 +++++++++------ 25 files changed, 1124 insertions(+), 766 deletions(-) create mode 100644 scst/include/scst_const.h diff --git a/scst/README b/scst/README index 97bff5988..11223a1b0 100644 --- a/scst/README +++ b/scst/README @@ -416,6 +416,10 @@ Also it has an advantage over FILEIO that it doesn't copy data between the system cache and the commands data buffers, so it saves a considerable amount of CPU power and memory bandwidth. +IMPORTANT: Since data in BLOCKIO and FILEIO modes are not consistent between +========= them, if you try to use a device in both those modes simultaneously, + you will almost instantly corrupt your data on that device. + Performance ----------- diff --git a/scst/include/scsi_tgt.h b/scst/include/scsi_tgt.h index e99e38a57..4a5b6a6ec 100644 --- a/scst/include/scsi_tgt.h +++ b/scst/include/scsi_tgt.h @@ -1,7 +1,7 @@ /* * include/scsi_tgt.h * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * Main SCSI target mid-level include file. @@ -34,6 +34,8 @@ #include #include +#include + /* Version numbers, the same as for the kernel */ #define SCST_VERSION_CODE 0x000906 #define SCST_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) @@ -96,6 +98,13 @@ *************************************************************/ #define SCST_CMD_STATE_NEED_THREAD_CTX 100 +/************************************************************* + ** Can be retuned instead of cmd's state by dev handlers' + ** parse function, if the cmd processing should be stopped + ** for now. The cmd will be restarted by dev handlers itself. + *************************************************************/ +#define SCST_CMD_STATE_STOP 101 + /************************************************************* ** States of mgmt command processing state machine *************************************************************/ @@ -240,14 +249,6 @@ *************************************************************/ #define SCST_DEFAULT_TIMEOUT (30*HZ) -/************************************************************* - ** Data direction aliases - *************************************************************/ -#define SCST_DATA_UNKNOWN DMA_BIDIRECTIONAL -#define SCST_DATA_WRITE DMA_TO_DEVICE -#define SCST_DATA_READ DMA_FROM_DEVICE -#define SCST_DATA_NONE DMA_NONE - /************************************************************* ** Flags of cmd->tgt_resp_flags *************************************************************/ @@ -261,48 +262,6 @@ */ #define SCST_TSC_FLAG_STATUS 0x2 -/************************************************************* - ** Values for management functions - *************************************************************/ -#define SCST_ABORT_TASK 0 -#define SCST_ABORT_TASK_SET 1 -#define SCST_CLEAR_ACA 2 -#define SCST_CLEAR_TASK_SET 3 -#define SCST_LUN_RESET 4 -#define SCST_TARGET_RESET 5 - -/** SCST extensions **/ - -/* - * Notifies about I_T nexus loss event in the corresponding session. - * Aborts all tasks there, resets the reservation, if any, and sets - * up the I_T Nexus loss UA. - */ -#define SCST_NEXUS_LOSS_SESS 6 - -/* Aborts all tasks in the corresponding session with TASK_ABORTED status */ -#define SCST_ABORT_ALL_TASKS_SESS 7 - -/* - * Notifies about I_T nexus loss event. Aborts all tasks in all sessions - * of the tgt, resets the reservations, if any, and sets up the I_T Nexus - * loss UA. - */ -#define SCST_NEXUS_LOSS 8 - -/* Aborts all tasks in all sessions of the tgt with TASK_ABORTED status */ -#define SCST_ABORT_ALL_TASKS 9 - -/************************************************************* - ** Values for mgmt cmd's status field. Codes taken from iSCSI - *************************************************************/ -#define SCST_MGMT_STATUS_SUCCESS 0 -#define SCST_MGMT_STATUS_TASK_NOT_EXIST -1 -#define SCST_MGMT_STATUS_LUN_NOT_EXIST -2 -#define SCST_MGMT_STATUS_FN_NOT_SUPPORTED -5 -#define SCST_MGMT_STATUS_REJECTED -255 -#define SCST_MGMT_STATUS_FAILED -129 - /************************************************************* ** Additional return code for dev handler's task_mgmt_fn() *************************************************************/ @@ -366,6 +325,9 @@ /* Set if tgt_dev is RESERVED by another session */ #define SCST_TGT_DEV_RESERVED 1 +/* Set HEAD OF QUEUE cmd is being executed */ +#define SCST_TGT_DEV_HQ_ACTIVE 2 + /* Set if the corresponding context is atomic */ #define SCST_TGT_DEV_AFTER_INIT_WR_ATOMIC 5 #define SCST_TGT_DEV_AFTER_INIT_OTH_ATOMIC 6 @@ -374,106 +336,15 @@ #define SCST_TGT_DEV_AFTER_RX_DATA_ATOMIC 9 #define SCST_TGT_DEV_AFTER_EXEC_ATOMIC 10 -/* Set HEAD OF QUEUE cmd is being executed */ -#define SCST_TGT_DEV_HQ_ACTIVE 12 - #ifdef DEBUG_TM #define SCST_TGT_DEV_UNDER_TM_DBG 20 #endif -/************************************************************* - ** Commands that are not listed anywhere else - *************************************************************/ -#ifndef REPORT_DEVICE_IDENTIFIER -#define REPORT_DEVICE_IDENTIFIER 0xA3 -#endif -#ifndef INIT_ELEMENT_STATUS -#define INIT_ELEMENT_STATUS 0x07 -#endif -#ifndef INIT_ELEMENT_STATUS_RANGE -#define INIT_ELEMENT_STATUS_RANGE 0x37 -#endif -#ifndef PREVENT_ALLOW_MEDIUM -#define PREVENT_ALLOW_MEDIUM 0x1E -#endif -#ifndef READ_ATTRIBUTE -#define READ_ATTRIBUTE 0x8C -#endif -#ifndef REQUEST_VOLUME_ADDRESS -#define REQUEST_VOLUME_ADDRESS 0xB5 -#endif -#ifndef WRITE_ATTRIBUTE -#define WRITE_ATTRIBUTE 0x8D -#endif -#ifndef WRITE_VERIFY_16 -#define WRITE_VERIFY_16 0x8E -#endif -#ifndef VERIFY_6 -#define VERIFY_6 0x13 -#endif -#ifndef VERIFY_12 -#define VERIFY_12 0xAF -#endif - -/************************************************************* - ** Control byte field in CDB - *************************************************************/ -#ifndef CONTROL_BYTE_LINK_BIT -#define CONTROL_BYTE_LINK_BIT 0x01 -#endif -#ifndef CONTROL_BYTE_NACA_BIT -#define CONTROL_BYTE_NACA_BIT 0x04 -#endif - -/************************************************************* - ** Byte 1 in INQUIRY CDB - *************************************************************/ -#define SCST_INQ_EVPD 0x01 - -/************************************************************* - ** Byte 3 in Standard INQUIRY data - *************************************************************/ -#define SCST_INQ_BYTE3 3 - -#define SCST_INQ_NORMACA_BIT 0x20 - -/************************************************************* - ** Byte 2 in RESERVE_10 CDB - *************************************************************/ -#define SCST_RES_3RDPTY 0x10 -#define SCST_RES_LONGID 0x02 - -/************************************************************* - ** Bits in the READ POSITION command - *************************************************************/ -#define TCLP_BIT 4 -#define LONG_BIT 2 -#define BT_BIT 1 - -/************************************************************* - ** Misc SCSI constants - *************************************************************/ -#define SCST_SENSE_ASC_UA_RESET 0x29 -#define READ_CAP_LEN 8 -#define READ_CAP16_LEN 12 -#define BYTCHK 0x02 -#define POSITION_LEN_SHORT 20 -#define POSITION_LEN_LONG 32 - /************************************************************* ** Name of the entry in /proc *************************************************************/ #define SCST_PROC_ENTRY_NAME "scsi_tgt" -/************************************************************* - ** Used with scst_set_cmd_error() - *************************************************************/ -#define SCST_LOAD_SENSE(key_asc_ascq) key_asc_ascq - -#define SCST_SENSE_VALID(sense) ((((uint8_t *)(sense))[0] & 0x70) == 0x70) - -#define SCST_NO_SENSE(sense) (((uint8_t *)(sense))[2] == 0) - /************************************************************* * TYPES *************************************************************/ @@ -751,13 +622,21 @@ struct scst_dev_type unsigned dev_done_atomic:1; unsigned dedicated_thread:1; + /* Set, if no /proc files should be automatically created by SCST */ + unsigned no_proc:1; + + /* Set if expected_sn in cmd->scst_cmd_done() */ + unsigned inc_expected_sn_on_done:1; + /* * Called to parse CDB from the cmd and initialize * cmd->bufflen and cmd->data_direction (both - REQUIRED). * Returns the command's next state or SCST_CMD_STATE_DEFAULT, * if the next default state should be used, or * SCST_CMD_STATE_NEED_THREAD_CTX if the function called in atomic - * context, but requires sleeping. In the last case, the function + * context, but requires sleeping, or SCST_CMD_STATE_STOP if the + * command should not be further processed for now. In the + * SCST_CMD_STATE_NEED_THREAD_CTX case the function * will be recalled in the thread context, where sleeping is allowed. * * Pay attention to "atomic" attribute of the cmd, which can be get @@ -899,7 +778,10 @@ struct scst_tgt struct timer_list retry_timer; int retry_timer_active; - /* Maximum SG table size */ + /* + * Maximum SG table size. Needed here, since different cards on the + * same target template can have different SG table limitations. + */ int sg_tablesize; /* Used for storage of target driver private stuff */ @@ -979,15 +861,6 @@ struct scst_session void (*unreg_done_fn) (struct scst_session *sess); }; -enum scst_cmd_queue_type -{ - SCST_CMD_QUEUE_UNTAGGED = 0, - SCST_CMD_QUEUE_SIMPLE, - SCST_CMD_QUEUE_ORDERED, - SCST_CMD_QUEUE_HEAD_OF_QUEUE, - SCST_CMD_QUEUE_ACA -}; - struct scst_cmd_lists { spinlock_t cmd_list_lock; @@ -1100,13 +973,19 @@ struct scst_cmd unsigned int may_need_dma_sync:1; /* Set if the cmd was done or aborted out of its SN */ - unsigned long out_of_sn:1; - - /* Set if the cmd is HEAD OF QUEUE */ - unsigned long head_of_queue:1; + unsigned int out_of_sn:1; /* Set if the cmd is deferred HEAD OF QUEUE */ - unsigned long hq_deferred:1; + unsigned int hq_deferred:1; + + /* Set if the internal parse should be skipped */ + unsigned int skip_parse:1; + + /* + * Set if inc expected_sn in cmd->scst_cmd_done() (to + * save extra dereferences) + */ + unsigned inc_expected_sn_on_done:1; /**************************************************************/ @@ -1128,7 +1007,7 @@ struct scst_cmd struct list_head sn_cmd_list_entry; /* Cmd's serial number, used to execute cmd's in order of arrival */ - unsigned int sn; + unsigned long sn; /* The corresponding sn_slot in tgt_dev->sn_slots */ atomic_t *sn_slot; @@ -1143,7 +1022,7 @@ struct scst_cmd uint32_t tag; /* CDB and its len */ - uint8_t cdb[MAX_COMMAND_SIZE]; + uint8_t cdb[SCST_MAX_CDB_SIZE]; int cdb_len; enum scst_cmd_queue_type queue_type; @@ -1156,17 +1035,21 @@ struct scst_cmd /* Remote initiator supplied values, if any */ scst_data_direction expected_data_direction; - unsigned int expected_transfer_len; + int expected_transfer_len; - /* cmd data length */ - size_t data_len; + /* + * Cmd data length. Could be different from bufflen for commands like + * VERIFY, which transfer different amount of data (if any), than + * processed. + */ + int data_len; /* Completition routine */ void (*scst_cmd_done) (struct scst_cmd *cmd, int next_state); struct sgv_pool_obj *sgv; /* sgv object */ - size_t bufflen; /* cmd buffer length */ + int bufflen; /* cmd buffer length */ struct scatterlist *sg; /* cmd data buffer SG vector */ int sg_cnt; /* SG segments count */ @@ -1199,7 +1082,7 @@ struct scst_cmd */ int orig_sg_cnt, orig_sg_entry, orig_entry_len; - uint8_t sense_buffer[SCSI_SENSE_BUFFERSIZE]; /* sense buffer */ + uint8_t sense_buffer[SCST_SENSE_BUFFERSIZE]; /* sense buffer */ /* The corresponding mgmt cmd, if any, protected by sess_list_lock */ struct scst_mgmt_cmd *mgmt_cmnd; @@ -1359,6 +1242,10 @@ struct scst_tgt_dev /* How many cmds alive on this dev in this session */ atomic_t cmd_count; + int gfp_mask; + struct sgv_pool *pool; + int max_sg_cnt; + unsigned long tgt_dev_flags; /* tgt_dev's async flags */ /* @@ -1371,13 +1258,18 @@ struct scst_tgt_dev */ int def_cmd_count; spinlock_t sn_lock; - int expected_sn; - int curr_sn; + unsigned long expected_sn, curr_sn; struct list_head deferred_cmd_list; struct list_head skipped_sn_list; struct list_head hq_cmd_list; - unsigned short prev_cmd_ordered; /* Set if the prev cmd was ORDERED */ - short num_free_sn_slots; + + /* + * Set if the prev cmd was ORDERED. Size must allow unprotected + * modifications + */ + unsigned long prev_cmd_ordered; + + int num_free_sn_slots; atomic_t *cur_sn_slot; atomic_t sn_slots[10]; @@ -1471,7 +1363,7 @@ struct scst_tgt_dev_UA /* List entry in tgt_dev->UA_list */ struct list_head UA_list_entry; /* Unit Attention sense */ - uint8_t UA_sense_buffer[SCSI_SENSE_BUFFERSIZE]; + uint8_t UA_sense_buffer[SCST_SENSE_BUFFERSIZE]; }; enum scst_cdb_flags @@ -1480,6 +1372,7 @@ enum scst_cdb_flags SCST_SMALL_TIMEOUT = 0x02, SCST_LONG_TIMEOUT = 0x04, SCST_UNKNOWN_LENGTH = 0x08, + SCST_INFO_INVALID = 0x10, }; struct scst_info_cdb @@ -1491,28 +1384,6 @@ struct scst_info_cdb const char *op_name; }; -/* - * Sense data for the appropriate errors. Can be used with - * scst_set_cmd_error() - */ -#define scst_sense_no_sense NO_SENSE, 0x00, 0 -#define scst_sense_hardw_error HARDWARE_ERROR, 0x44, 0 -#define scst_sense_aborted_command ABORTED_COMMAND, 0x00, 0 -#define scst_sense_invalid_opcode ILLEGAL_REQUEST, 0x20, 0 -#define scst_sense_invalid_field_in_cdb ILLEGAL_REQUEST, 0x24, 0 -#define scst_sense_invalid_field_in_parm_list ILLEGAL_REQUEST, 0x26, 0 -#define scst_sense_reset_UA UNIT_ATTENTION, 0x29, 0 -#define scst_sense_nexus_loss_UA UNIT_ATTENTION, 0x29, 0x7 -#define scst_sense_saving_params_unsup ILLEGAL_REQUEST, 0x39, 0 -#define scst_sense_lun_not_supported ILLEGAL_REQUEST, 0x25, 0 -#define scst_sense_data_protect DATA_PROTECT, 0x00, 0 -#define scst_sense_miscompare_error MISCOMPARE, 0x1D, 0 -#define scst_sense_block_out_range_error ILLEGAL_REQUEST, 0x21, 0 -#define scst_sense_medium_changed_UA UNIT_ATTENTION, 0x28, 0 -#define scst_sense_read_error MEDIUM_ERROR, 0x11, 0 -#define scst_sense_write_error MEDIUM_ERROR, 0x03, 0 -#define scst_sense_not_ready NOT_READY, 0x04, 0x10 - #ifndef smp_mb__after_set_bit /* There is no smp_mb__after_set_bit() in the kernel */ #define smp_mb__after_set_bit() smp_mb(); @@ -1787,6 +1658,20 @@ static inline int scst_to_tgt_dma_dir(int scst_dir) return scst_dir; } +/* + * Returns 1, if cmd's CDB is locally handled by SCST and 0 otherwise. + * Dev handlers parse() and dev_done() not called for such commands. + */ +static inline int scst_is_cmd_local(struct scst_cmd *cmd) +{ + int res = 0; + switch (cmd->cdb[0]) { + case REPORT_LUNS: + res = 1; + } + return res; +} + /* * Registers a virtual device. * Parameters: @@ -2047,7 +1932,7 @@ static inline scst_data_direction scst_cmd_get_expected_data_direction( return cmd->expected_data_direction; } -static inline unsigned int scst_cmd_get_expected_transfer_len( +static inline int scst_cmd_get_expected_transfer_len( struct scst_cmd *cmd) { return cmd->expected_transfer_len; @@ -2055,7 +1940,7 @@ static inline unsigned int scst_cmd_get_expected_transfer_len( static inline void scst_cmd_set_expected(struct scst_cmd *cmd, scst_data_direction expected_data_direction, - unsigned int expected_transfer_len) + int expected_transfer_len) { cmd->expected_data_direction = expected_data_direction; cmd->expected_transfer_len = expected_transfer_len; @@ -2162,6 +2047,13 @@ static inline int scst_get_buf_count(struct scst_cmd *cmd) void scst_suspend_activity(void); void scst_resume_activity(void); +/* + * Main SCST commands processing routing. Must be used only by dev handlers. + * Argument context sets the execution context, only SCST_CONTEXT_DIRECT and + * SCST_CONTEXT_DIRECT_ATOMIC are allowed. + */ +void scst_process_active_cmd(struct scst_cmd *cmd, int context); + /* * Returns target driver's root entry in SCST's /proc hierarchy. * The driver can create own files/directoryes here, which should @@ -2310,6 +2202,34 @@ static inline void scst_thr_data_put(struct scst_thr_data_hdr *data) data->free_fn(data); } +/* SGV pool routines and flag bits */ + +/* Set if the allocated object must be not from the cache */ +#define SCST_POOL_ALLOC_NO_CACHED 1 + +/* Set if there should not be any memory allocations on a cache miss */ +#define SCST_POOL_NO_ALLOC_ON_CACHE_MISS 2 + +/* Set an object should be returned even if it doesn't have SG vector built */ +#define SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL 4 + +struct sgv_pool_obj; +struct sgv_pool; + +struct sgv_pool *sgv_pool_create(const char *name, int clustered); +void sgv_pool_destroy(struct sgv_pool *pool); + +void sgv_pool_set_allocator(struct sgv_pool *pool, + struct page *(*alloc_pages_fn)(struct scatterlist *, gfp_t, void *), + void (*free_pages_fn)(struct scatterlist *, int, void *)); + +struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size, + unsigned long gfp_mask, int atomic, int *count, + struct sgv_pool_obj **sgv, void *priv); +void sgv_pool_free(struct sgv_pool_obj *sgv); + +void *sgv_get_priv(struct sgv_pool_obj *sgv); + /** ** Generic parse() support routines. ** Done via pointer on functions to avoid unneeded dereferences on @@ -2341,15 +2261,18 @@ int scst_tape_generic_parse(struct scst_cmd *cmd, /* Generic parse() for changer devices */ int scst_changer_generic_parse(struct scst_cmd *cmd, - struct scst_info_cdb *info_cdb, int nothing); + struct scst_info_cdb *info_cdb, + int (*nothing)(struct scst_cmd *cmd)); /* Generic parse() for "processor" devices */ int scst_processor_generic_parse(struct scst_cmd *cmd, - struct scst_info_cdb *info_cdb, int nothing); + struct scst_info_cdb *info_cdb, + int (*nothing)(struct scst_cmd *cmd)); /* Generic parse() for RAID devices */ int scst_raid_generic_parse(struct scst_cmd *cmd, - struct scst_info_cdb *info_cdb, int nothing); + struct scst_info_cdb *info_cdb, + int (*nothing)(struct scst_cmd *cmd)); /** ** Generic dev_done() support routines. diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h new file mode 100644 index 000000000..a0fd4c7a6 --- /dev/null +++ b/scst/include/scst_const.h @@ -0,0 +1,243 @@ +/* + * include/scst_const.h + * + * Copyright (C) 2004-2007 Vladislav Bolkhovitin + * + * Contains macroses for execution tracing and error reporting + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SCST_CONST_H +#define __SCST_CONST_H + +#include + +/*** Shared constants between user and kernel spaces ***/ + +/* Max size of CDB */ +#define SCST_MAX_CDB_SIZE 16 + +/* Max size of sense */ +#define SCST_SENSE_BUFFERSIZE 96 + +/************************************************************* + ** Values for task management functions + *************************************************************/ +#define SCST_ABORT_TASK 0 +#define SCST_ABORT_TASK_SET 1 +#define SCST_CLEAR_ACA 2 +#define SCST_CLEAR_TASK_SET 3 +#define SCST_LUN_RESET 4 +#define SCST_TARGET_RESET 5 + +/** SCST extensions **/ + +/* + * Notifies about I_T nexus loss event in the corresponding session. + * Aborts all tasks there, resets the reservation, if any, and sets + * up the I_T Nexus loss UA. + */ +#define SCST_NEXUS_LOSS_SESS 6 + +/* Aborts all tasks in the corresponding session with TASK_ABORTED status */ +#define SCST_ABORT_ALL_TASKS_SESS 7 + +/* + * Notifies about I_T nexus loss event. Aborts all tasks in all sessions + * of the tgt, resets the reservations, if any, and sets up the I_T Nexus + * loss UA. + */ +#define SCST_NEXUS_LOSS 8 + +/* Aborts all tasks in all sessions of the tgt with TASK_ABORTED status */ +#define SCST_ABORT_ALL_TASKS 9 + +/************************************************************* + ** Values for mgmt cmd's status field. Codes taken from iSCSI + *************************************************************/ +#define SCST_MGMT_STATUS_SUCCESS 0 +#define SCST_MGMT_STATUS_TASK_NOT_EXIST -1 +#define SCST_MGMT_STATUS_LUN_NOT_EXIST -2 +#define SCST_MGMT_STATUS_FN_NOT_SUPPORTED -5 +#define SCST_MGMT_STATUS_REJECTED -255 +#define SCST_MGMT_STATUS_FAILED -129 + +/************************************************************* + ** SCSI task attribute queue types + *************************************************************/ +enum scst_cmd_queue_type +{ + SCST_CMD_QUEUE_UNTAGGED = 0, + SCST_CMD_QUEUE_SIMPLE, + SCST_CMD_QUEUE_ORDERED, + SCST_CMD_QUEUE_HEAD_OF_QUEUE, + SCST_CMD_QUEUE_ACA +}; + +/************************************************************* + ** Data direction aliases + *************************************************************/ +#define SCST_DATA_UNKNOWN 0 +#define SCST_DATA_WRITE 1 +#define SCST_DATA_READ 2 +#define SCST_DATA_NONE 3 + +/************************************************************* + ** Sense manipulation and examination + *************************************************************/ +#define SCST_LOAD_SENSE(key_asc_ascq) key_asc_ascq + +#define SCST_SENSE_VALID(sense) ((((uint8_t *)(sense))[0] & 0x70) == 0x70) + +#define SCST_NO_SENSE(sense) (((uint8_t *)(sense))[2] == 0) + +/************************************************************* + ** Sense data for the appropriate errors. Can be used with + ** scst_set_cmd_error() + *************************************************************/ +#define scst_sense_no_sense NO_SENSE, 0x00, 0 +#define scst_sense_hardw_error HARDWARE_ERROR, 0x44, 0 +#define scst_sense_aborted_command ABORTED_COMMAND, 0x00, 0 +#define scst_sense_invalid_opcode ILLEGAL_REQUEST, 0x20, 0 +#define scst_sense_invalid_field_in_cdb ILLEGAL_REQUEST, 0x24, 0 +#define scst_sense_invalid_field_in_parm_list ILLEGAL_REQUEST, 0x26, 0 +#define scst_sense_reset_UA UNIT_ATTENTION, 0x29, 0 +#define scst_sense_nexus_loss_UA UNIT_ATTENTION, 0x29, 0x7 +#define scst_sense_saving_params_unsup ILLEGAL_REQUEST, 0x39, 0 +#define scst_sense_lun_not_supported ILLEGAL_REQUEST, 0x25, 0 +#define scst_sense_data_protect DATA_PROTECT, 0x00, 0 +#define scst_sense_miscompare_error MISCOMPARE, 0x1D, 0 +#define scst_sense_block_out_range_error ILLEGAL_REQUEST, 0x21, 0 +#define scst_sense_medium_changed_UA UNIT_ATTENTION, 0x28, 0 +#define scst_sense_read_error MEDIUM_ERROR, 0x11, 0 +#define scst_sense_write_error MEDIUM_ERROR, 0x03, 0 +#define scst_sense_not_ready NOT_READY, 0x04, 0x10 + +/************************************************************* + * SCSI opcodes not listed anywhere else + *************************************************************/ +#ifndef REPORT_DEVICE_IDENTIFIER +#define REPORT_DEVICE_IDENTIFIER 0xA3 +#endif +#ifndef INIT_ELEMENT_STATUS +#define INIT_ELEMENT_STATUS 0x07 +#endif +#ifndef INIT_ELEMENT_STATUS_RANGE +#define INIT_ELEMENT_STATUS_RANGE 0x37 +#endif +#ifndef PREVENT_ALLOW_MEDIUM +#define PREVENT_ALLOW_MEDIUM 0x1E +#endif +#ifndef READ_ATTRIBUTE +#define READ_ATTRIBUTE 0x8C +#endif +#ifndef REQUEST_VOLUME_ADDRESS +#define REQUEST_VOLUME_ADDRESS 0xB5 +#endif +#ifndef WRITE_ATTRIBUTE +#define WRITE_ATTRIBUTE 0x8D +#endif +#ifndef WRITE_VERIFY_16 +#define WRITE_VERIFY_16 0x8E +#endif +#ifndef VERIFY_6 +#define VERIFY_6 0x13 +#endif +#ifndef VERIFY_12 +#define VERIFY_12 0xAF +#endif +#ifndef READ_16 +#define READ_16 0x88 +#endif +#ifndef WRITE_16 +#define WRITE_16 0x8a +#endif +#ifndef VERIFY_16 +#define VERIFY_16 0x8f +#endif +#ifndef SERVICE_ACTION_IN +#define SERVICE_ACTION_IN 0x9e +#endif +#ifndef SAI_READ_CAPACITY_16 +/* values for service action in */ +#define SAI_READ_CAPACITY_16 0x10 +#endif +#ifndef MI_REPORT_TARGET_PGS +/* values for maintenance in */ +#define MI_REPORT_TARGET_PGS 0x0a +#endif +#ifndef REPORT_LUNS +#define REPORT_LUNS 0xa0 +#endif + +/************************************************************* + ** SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft + ** T10/1561-D Revision 4 Draft dated 7th November 2002. + *************************************************************/ +#define SAM_STAT_GOOD 0x00 +#define SAM_STAT_CHECK_CONDITION 0x02 +#define SAM_STAT_CONDITION_MET 0x04 +#define SAM_STAT_BUSY 0x08 +#define SAM_STAT_INTERMEDIATE 0x10 +#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 +#define SAM_STAT_RESERVATION_CONFLICT 0x18 +#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ +#define SAM_STAT_TASK_SET_FULL 0x28 +#define SAM_STAT_ACA_ACTIVE 0x30 +#define SAM_STAT_TASK_ABORTED 0x40 + +/************************************************************* + ** Control byte field in CDB + *************************************************************/ +#ifndef CONTROL_BYTE_LINK_BIT +#define CONTROL_BYTE_LINK_BIT 0x01 +#endif +#ifndef CONTROL_BYTE_NACA_BIT +#define CONTROL_BYTE_NACA_BIT 0x04 +#endif + +/************************************************************* + ** Byte 1 in INQUIRY CDB + *************************************************************/ +#define SCST_INQ_EVPD 0x01 + +/************************************************************* + ** Byte 3 in Standard INQUIRY data + *************************************************************/ +#define SCST_INQ_BYTE3 3 + +#define SCST_INQ_NORMACA_BIT 0x20 + +/************************************************************* + ** Byte 2 in RESERVE_10 CDB + *************************************************************/ +#define SCST_RES_3RDPTY 0x10 +#define SCST_RES_LONGID 0x02 + +/************************************************************* + ** Bits in the READ POSITION command + *************************************************************/ +#define TCLP_BIT 4 +#define LONG_BIT 2 +#define BT_BIT 1 + +/************************************************************* + ** Misc SCSI constants + *************************************************************/ +#define SCST_SENSE_ASC_UA_RESET 0x29 +#define READ_CAP_LEN 8 +#define READ_CAP16_LEN 12 +#define BYTCHK 0x02 +#define POSITION_LEN_SHORT 20 +#define POSITION_LEN_LONG 32 + +#endif /* __SCST_CONST_H */ diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index 1c8391ac9..040994e1f 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -1,7 +1,7 @@ /* * include/scst_debug.h * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * Contains macroses for execution tracing and error reporting @@ -100,7 +100,7 @@ #if defined(DEBUG) || defined(TRACING) extern int debug_print_prefix(unsigned long trace_flag, const char *func, int line); -extern void debug_print_buffer(unsigned long trace_flag, const void *data, int len); +extern void debug_print_buffer(const void *data, int len); #define TRACE(trace, format, args...) \ do { \ @@ -138,7 +138,7 @@ do { \ __tflag = NO_FLAG; \ } \ PRINT(NO_FLAG, "%s%s:", __tflag, message); \ - debug_print_buffer(trace_flag, buff, len); \ + debug_print_buffer(buff, len); \ } \ } while(0) @@ -152,7 +152,7 @@ do { \ __tflag = NO_FLAG; \ } \ PRINT(NO_FLAG, "%s%s:", __tflag, message); \ - debug_print_buffer(trace_flag, buff, len); \ + debug_print_buffer(buff, len); \ } \ } while(0) diff --git a/scst/src/Makefile b/scst/src/Makefile index e8ca08e5a..8b3a9188a 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -70,6 +70,7 @@ install: all install -d $(INSTALL_DIR_H) install -m 644 ../include/scsi_tgt.h $(INSTALL_DIR_H) install -m 644 ../include/scst_debug.h $(INSTALL_DIR_H) + install -m 644 ../include/scst_const.h $(INSTALL_DIR_H) ifneq ($(MODS_VERS),) rm -f $(INSTALL_DIR_H)/Module.symvers install -m 644 Modules.symvers $(INSTALL_DIR_H) @@ -91,7 +92,7 @@ endif INSTALL_DIR := /lib/modules/$(shell uname -r)/extra INSTALL_DIR_H := /usr/local/include/scst -EXTRA_CFLAGS += -I$(SCST_INC_DIR) +EXTRA_CFLAGS += -I$(SCST_INC_DIR) -Wextra -Wno-unused-parameter #EXTRA_CFLAGS += -DSTRICT_SERIALIZING @@ -102,7 +103,7 @@ EXTRA_CFLAGS += -DEXTRACHECKS #EXTRA_CFLAGS += -DTRACING -EXTRA_CFLAGS += -DDEBUG +EXTRA_CFLAGS += -DDEBUG -g #EXTRA_CFLAGS += -DDEBUG_TM -DTM_DBG_GO_OFFLINE=0 #EXTRA_CFLAGS += -DDEBUG_RETRY #EXTRA_CFLAGS += -DDEBUG_OOM diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index be590a20e..22937895b 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -61,10 +61,12 @@ endif INSTALL_DIR := /lib/modules/$(shell uname -r)/extra -EXTRA_CFLAGS += -I$(SUBDIRS) -I$(SCST_INC_DIR) +EXTRA_CFLAGS += -I$(SUBDIRS) -I$(SCST_INC_DIR) -Wextra -Wno-unused-parameter + +EXTRA_CFLAGS += -DEXTRACHECKS #EXTRA_CFLAGS += -DTRACING -EXTRA_CFLAGS += -DDEBUG +EXTRA_CFLAGS += -DDEBUG -g clean: rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend Modules.symvers Module.symvers diff --git a/scst/src/dev_handlers/scst_cdrom.c b/scst/src/dev_handlers/scst_cdrom.c index c87826408..7f593e01e 100644 --- a/scst/src/dev_handlers/scst_cdrom.c +++ b/scst/src/dev_handlers/scst_cdrom.c @@ -1,7 +1,7 @@ /* * scst_cdrom.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * SCSI CDROM (type 5) dev handler @@ -71,7 +71,7 @@ int cdrom_attach(struct scst_device *dev) const int buffer_size = 512; uint8_t *buffer = NULL; int retries; - unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; + unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE]; enum dma_data_direction data_dir; unsigned char *sbuff; struct cdrom_params *params; @@ -140,7 +140,7 @@ int cdrom_attach(struct scst_device *dev) TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)", sector_size, dev->scsi_dev->scsi_level, SCSI_2); } else { - TRACE_BUFFER("Sense set", sbuff, SCSI_SENSE_BUFFERSIZE); + TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE); params->block_shift = CDROM_DEF_BLOCK_SHIFT; // res = -ENODEV; goto out_free_buf; @@ -186,6 +186,10 @@ void cdrom_detach(struct scst_device *dev) static int cdrom_get_block_shift(struct scst_cmd *cmd) { struct cdrom_params *params = (struct cdrom_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be + * called, when there are existing commands. + */ return params->block_shift; } @@ -203,12 +207,6 @@ static int cdrom_get_block_shift(struct scst_cmd *cmd) int cdrom_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) { int res = SCST_CMD_STATE_DEFAULT; - - - /* - * No need for locks here, since *_detach() can not be - * called, when there are existing commands. - */ scst_cdrom_generic_parse(cmd, info_cdb, cdrom_get_block_shift); @@ -227,6 +225,10 @@ int cdrom_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) static void cdrom_set_block_shift(struct scst_cmd *cmd, int block_shift) { struct cdrom_params *params = (struct cdrom_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be + * called, when there are existing commands. + */ if (block_shift != 0) params->block_shift = block_shift; else @@ -251,10 +253,6 @@ int cdrom_done(struct scst_cmd *cmd) TRACE_ENTRY(); - /* - * No need for locks here, since *_detach() can not be - * called, when there are existing commands. - */ res = scst_block_generic_dev_done(cmd, cdrom_set_block_shift); TRACE_EXIT_RES(res); @@ -299,3 +297,6 @@ module_init(cdrom_init); module_exit(cdrom_exit); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); +MODULE_DESCRIPTION("SCSI CDROM (type 5) dev handler for SCST"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/dev_handlers/scst_changer.c b/scst/src/dev_handlers/scst_changer.c index e11d3fcbb..40666faeb 100644 --- a/scst/src/dev_handlers/scst_changer.c +++ b/scst/src/dev_handlers/scst_changer.c @@ -1,7 +1,7 @@ /* * scst_changer.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * SCSI medium changer (type 8) dev handler @@ -158,9 +158,6 @@ int changer_done(struct scst_cmd *cmd) TRACE_ENTRY(); - if (unlikely(cmd->sg == NULL)) - goto out; - /* * SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len * based on cmd->status and cmd->data_direction, therefore change @@ -175,7 +172,6 @@ int changer_done(struct scst_cmd *cmd) } #endif -out: TRACE_EXIT(); return res; } @@ -217,4 +213,7 @@ static void __exit changer_exit(void) module_init(changer_init); module_exit(changer_exit); +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCSI medium changer (type 8) dev handler for SCST"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/dev_handlers/scst_dev_handler.h b/scst/src/dev_handlers/scst_dev_handler.h index 6de68ff6e..edc921f42 100644 --- a/scst/src/dev_handlers/scst_dev_handler.h +++ b/scst/src/dev_handlers/scst_dev_handler.h @@ -15,8 +15,8 @@ #ifdef DEBUG #define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_PID | \ - TRACE_FUNCTION | TRACE_MGMT | TRACE_MINOR | TRACE_MGMT_DEBUG | \ - TRACE_SPECIAL) + TRACE_LINE | TRACE_FUNCTION | TRACE_MGMT | TRACE_MINOR | \ + TRACE_MGMT_DEBUG | TRACE_SPECIAL) #else #define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MINOR) #endif diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index 671afbe5e..b32f88628 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -1,7 +1,7 @@ /* * scst_disk.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * SCSI disk (type 0) dev handler @@ -147,7 +147,7 @@ int disk_attach(struct scst_device *dev) const int buffer_size = 512; uint8_t *buffer = NULL; int retries; - unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; + unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE]; enum dma_data_direction data_dir; unsigned char *sbuff; struct disk_params *params; @@ -213,7 +213,7 @@ int disk_attach(struct scst_device *dev) else params->block_shift = scst_calc_block_shift(sector_size); } else { - TRACE_BUFFER("Sense set", sbuff, SCSI_SENSE_BUFFERSIZE); + TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE); res = -ENODEV; goto out_free_buf; } @@ -258,6 +258,10 @@ void disk_detach(struct scst_device *dev) static int disk_get_block_shift(struct scst_cmd *cmd) { struct disk_params *params = (struct disk_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be + * called, when there are existing commands. + */ return params->block_shift; } @@ -276,11 +280,6 @@ int disk_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) { int res = SCST_CMD_STATE_DEFAULT; - /* - * No need for locks here, since *_detach() can not be - * called, when there are existing commands. - */ - scst_sbc_generic_parse(cmd, info_cdb, disk_get_block_shift); cmd->retries = 1; @@ -299,6 +298,10 @@ int disk_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) static void disk_set_block_shift(struct scst_cmd *cmd, int block_shift) { struct disk_params *params = (struct disk_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be + * called, when there are existing commands. + */ if (block_shift != 0) params->block_shift = block_shift; else @@ -323,10 +326,6 @@ int disk_done(struct scst_cmd *cmd) TRACE_ENTRY(); - /* - * No need for locks here, since *_detach() can not be - * called, when there are existing commands. - */ res = scst_block_generic_dev_done(cmd, disk_set_block_shift); TRACE_EXIT_RES(res); @@ -373,4 +372,7 @@ int disk_exec(struct scst_cmd *cmd) return res; } +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCSI disk (type 0) dev handler for SCST"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/dev_handlers/scst_modisk.c b/scst/src/dev_handlers/scst_modisk.c index b1c5d4fc1..b1d420c6d 100644 --- a/scst/src/dev_handlers/scst_modisk.c +++ b/scst/src/dev_handlers/scst_modisk.c @@ -1,7 +1,7 @@ /* * scst_modisk.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * SCSI MO disk (type 7) dev handler @@ -147,7 +147,7 @@ int modisk_attach(struct scst_device *dev) const int buffer_size = 512; uint8_t *buffer = NULL; int retries; - unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; + unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE]; enum dma_data_direction data_dir; unsigned char *sbuff; struct modisk_params *params; @@ -226,7 +226,7 @@ int modisk_attach(struct scst_device *dev) TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)", sector_size, dev->scsi_dev->scsi_level, SCSI_2); } else { - TRACE_BUFFER("Sense set", sbuff, SCSI_SENSE_BUFFERSIZE); + TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE); if (sbuff[2] != NOT_READY) res = -ENODEV; @@ -273,6 +273,10 @@ void modisk_detach(struct scst_device *dev) static int modisk_get_block_shift(struct scst_cmd *cmd) { struct modisk_params *params = (struct modisk_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be + * called, when there are existing commands. + */ return params->block_shift; } @@ -291,11 +295,6 @@ int modisk_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) { int res = SCST_CMD_STATE_DEFAULT; - /* - * No need for locks here, since *_detach() can not be - * called, when there are existing commands. - */ - scst_modisk_generic_parse(cmd, info_cdb, modisk_get_block_shift); cmd->retries = 1; @@ -313,6 +312,10 @@ int modisk_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) static void modisk_set_block_shift(struct scst_cmd *cmd, int block_shift) { struct modisk_params *params = (struct modisk_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be + * called, when there are existing commands. + */ if (block_shift != 0) params->block_shift = block_shift; else @@ -337,10 +340,6 @@ int modisk_done(struct scst_cmd *cmd) TRACE_ENTRY(); - /* - * No need for locks here, since *_detach() can not be - * called, when there are existing commands. - */ res = scst_block_generic_dev_done(cmd, modisk_set_block_shift); TRACE_EXIT_RES(res); @@ -387,4 +386,7 @@ int modisk_exec(struct scst_cmd *cmd) return res; } +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCSI MO disk (type 7) dev handler for SCST"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/dev_handlers/scst_processor.c b/scst/src/dev_handlers/scst_processor.c index 2b097f5bd..15c7dab55 100644 --- a/scst/src/dev_handlers/scst_processor.c +++ b/scst/src/dev_handlers/scst_processor.c @@ -1,7 +1,7 @@ /* * scst_processor.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * SCSI medium processor (type 3) dev handler @@ -157,9 +157,6 @@ int processor_done(struct scst_cmd *cmd) TRACE_ENTRY(); - if (unlikely(cmd->sg == NULL)) - goto out; - /* * SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len * based on cmd->status and cmd->data_direction, therefore change @@ -174,7 +171,6 @@ int processor_done(struct scst_cmd *cmd) } #endif -out: TRACE_EXIT(); return res; } @@ -216,4 +212,7 @@ static void __exit processor_exit(void) module_init(processor_init); module_exit(processor_exit); +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCSI medium processor (type 3) dev handler for SCST"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/dev_handlers/scst_raid.c b/scst/src/dev_handlers/scst_raid.c index 5afa803e7..8bf826622 100644 --- a/scst/src/dev_handlers/scst_raid.c +++ b/scst/src/dev_handlers/scst_raid.c @@ -1,7 +1,7 @@ /* * scst_raid.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * SCSI raid(controller) (type 0xC) dev handler @@ -157,9 +157,6 @@ int raid_done(struct scst_cmd *cmd) TRACE_ENTRY(); - if (unlikely(cmd->sg == NULL)) - goto out; - /* * SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len * based on cmd->status and cmd->data_direction, therefore change @@ -174,7 +171,6 @@ int raid_done(struct scst_cmd *cmd) } #endif -out: TRACE_EXIT(); return res; } @@ -216,4 +212,7 @@ static void __exit raid_exit(void) module_init(raid_init); module_exit(raid_exit); +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCSI raid(controller) (type 0xC) dev handler for SCST"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index dc260ac81..bdbb65588 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -1,7 +1,7 @@ /* * scst_tape.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * SCSI tape (type 1) dev handler @@ -261,6 +261,10 @@ void tape_detach(struct scst_device *dev) static int tape_get_block_size(struct scst_cmd *cmd) { struct tape_params *params = (struct tape_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be called, + * when there are existing commands. + */ return params->block_size; } @@ -279,11 +283,6 @@ int tape_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) { int res = SCST_CMD_STATE_DEFAULT; - /* - * No need for locks here, since *_detach() can not be called, - * when there are existing commands. - */ - scst_tape_generic_parse(cmd, info_cdb, tape_get_block_size); cmd->retries = 1; @@ -301,6 +300,10 @@ int tape_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) static void tape_set_block_size(struct scst_cmd *cmd, int block_size) { struct tape_params *params = (struct tape_params *)cmd->dev->dh_priv; + /* + * No need for locks here, since *_detach() can not be called, when + * there are existing commands. + */ params->block_size = block_size; return; } @@ -324,11 +327,6 @@ int tape_done(struct scst_cmd *cmd) TRACE_ENTRY(); - /* - * No need for locks here, since *_detach() can not be called, when - * there are existing commands. - */ - if ((status == SAM_STAT_GOOD) || (status == SAM_STAT_CONDITION_MET)) { res = scst_tape_generic_dev_done(cmd, tape_set_block_size); } else if ((status == SAM_STAT_CHECK_CONDITION) && @@ -413,4 +411,7 @@ int tape_exec(struct scst_cmd *cmd) return res; } +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCSI tape (type 1) dev handler for SCST"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index b650a39e6..3c87dd124 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -1,7 +1,7 @@ /* * scst_vdisk.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * (C) 2007 Ming Zhang * (C) 2007 Ross Walker @@ -44,12 +44,14 @@ #define TRACE_ORDER 0x80000000 +#if defined(DEBUG) || defined(TRACING) static struct scst_proc_log vdisk_proc_local_trace_tbl[] = { { TRACE_ORDER, "order" }, { 0, NULL } }; #define trace_log_tbl vdisk_proc_local_trace_tbl +#endif #include "scst_dev_handler.h" @@ -122,6 +124,10 @@ struct scst_vdisk_dev { }; struct scst_vdisk_tgt_dev { + /* + * Used without locking since SCST core ensures that only commands + * of the same type per tgt_dev can be processed simultaneously + */ enum scst_cmd_queue_type last_write_cmd_queue_type; }; @@ -949,10 +955,6 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd) goto out_free; } - /* - * ToDo: write through/back flags as well as read only one. - */ - if (cmd->cdb[1] & CMDDT) { TRACE_DBG("%s", "INQUIRY: CMDDT is unsupported"); scst_set_cmd_error(cmd, @@ -969,7 +971,7 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd) int dev_id_num; char dev_id_str[6]; - for (dev_id_num = 0, i = 0; i < strlen(virt_dev->name); i++) { + for (dev_id_num = 0, i = 0; i < (int)strlen(virt_dev->name); i++) { dev_id_num += virt_dev->name[i]; } len = scnprintf(dev_id_str, 6, "%d", dev_id_num); @@ -1218,8 +1220,6 @@ static void vdisk_exec_mode_sense(struct scst_cmd *cmd) goto out_free; } - memset(buf, 0, sizeof(buf)); - if (0x3 == pcontrol) { TRACE_DBG("%s", "MODE SENSE: Saving values not supported"); scst_set_cmd_error(cmd, @@ -2029,7 +2029,7 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr, need_new_bio = 1; for (j = 0; j < cmd->sg_cnt; ++j) { - unsigned int len, bytes, off, thislen; + int len, bytes, off, thislen; struct page *page; page = sgl[j].page; @@ -2171,7 +2171,7 @@ static void vdisk_exec_verify(struct scst_cmd *cmd, compare = 1; while (length > 0) { - len_mem = length > LEN_MEM ? LEN_MEM : length; + len_mem = (length > LEN_MEM) ? LEN_MEM : length; TRACE_DBG("Verify: length %zd - len_mem %zd", length, len_mem); if (!virt_dev->nullio) @@ -2190,8 +2190,7 @@ static void vdisk_exec_verify(struct scst_cmd *cmd, scst_put_buf(cmd, address_sav); goto out_set_fs; } - if (compare && memcmp(address, mem_verify, len_mem) != 0) - { + if (compare && memcmp(address, mem_verify, len_mem) != 0) { TRACE_DBG("Verify: error memcmp length %zd", length); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_miscompare_error)); @@ -2200,8 +2199,7 @@ static void vdisk_exec_verify(struct scst_cmd *cmd, } length -= len_mem; address += len_mem; - if (compare && length <= 0) - { + if (compare && length <= 0) { scst_put_buf(cmd, address_sav); length = scst_get_buf_next(cmd, &address); address_sav = address; @@ -2398,7 +2396,6 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, if (isdigit(*p)) { char *pp; - uint32_t t; block_size = simple_strtoul(p, &pp, 0); p = pp; if ((*p != '\0') && !isspace(*p)) { @@ -2409,17 +2406,8 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, while (isspace(*p) && *p != '\0') p++; - t = block_size; - block_shift = 0; - while(1) { - if ((t & 1) != 0) - break; - t >>= 1; - block_shift++; - } + block_shift = scst_calc_block_shift(block_size); if (block_shift < 9) { - PRINT_ERROR_PR("Wrong block size %d", - block_size); res = -EINVAL; goto out_free_vdev; } @@ -3088,4 +3076,8 @@ static void __exit exit_scst_vdisk_driver(void) module_init(init_scst_vdisk_driver); module_exit(exit_scst_vdisk_driver); +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCSI disk (type 0) and CDROM (type 5) dev handler for " + "SCST using files on file systems or block devices"); +MODULE_VERSION(SCST_VERSION_STRING); diff --git a/scst/src/scst.c b/scst/src/scst.c index ae792a095..39f722e63 100644 --- a/scst/src/scst.c +++ b/scst/src/scst.c @@ -1,7 +1,7 @@ /* * scst.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * This program is free software; you can redistribute it and/or @@ -111,7 +111,7 @@ int scst_virt_dev_last_id = 1; /* protected by scst_mutex */ * could fail in improper places. */ spinlock_t scst_temp_UA_lock = SPIN_LOCK_UNLOCKED; -uint8_t scst_temp_UA[SCSI_SENSE_BUFFERSIZE]; +uint8_t scst_temp_UA[SCST_SENSE_BUFFERSIZE]; module_param_named(scst_threads, scst_threads, int, 0); MODULE_PARM_DESC(scst_threads, "SCSI target threads count"); @@ -705,7 +705,7 @@ int scst_register_dev_driver(struct scst_dev_type *dev_type) #ifdef VDISK_ONLY if (dev_type->exec == NULL) { - PRINT_ERROR_PR("Pass-through dev handlers (handler %s) not " + PRINT_ERROR_PR("Pass-through dev handlers (handler \"%s\") not " "supported. Recompile SCST with undefined VDISK_ONLY", dev_type->name); res = -EINVAL; @@ -722,8 +722,8 @@ int scst_register_dev_driver(struct scst_dev_type *dev_type) exist = 0; list_for_each_entry(dt, &scst_dev_type_list, dev_type_list_entry) { if (strcmp(dt->name, dev_type->name) == 0) { - PRINT_ERROR_PR("Device type handler %s already exist", - dt->name); + PRINT_ERROR_PR("Device type handler \"%s\" already " + "exist", dt->name); exist = 1; break; } @@ -749,7 +749,7 @@ int scst_register_dev_driver(struct scst_dev_type *dev_type) scst_resume_activity(); if (res == 0) { - PRINT_INFO_PR("Device handler %s for type %d registered " + PRINT_INFO_PR("Device handler \"%s\" for type %d registered " "successfully", dev_type->name, dev_type->type); } @@ -762,7 +762,7 @@ out_up: out_err: scst_resume_activity(); - PRINT_ERROR_PR("Failed to register device handler %s for type %d", + PRINT_ERROR_PR("Failed to register device handler \"%s\" for type %d", dev_type->name, dev_type->type); goto out; } @@ -785,7 +785,7 @@ void scst_unregister_dev_driver(struct scst_dev_type *dev_type) } } if (!found) { - PRINT_ERROR_PR("Dev handler %s isn't registered", + PRINT_ERROR_PR("Dev handler \"%s\" isn't registered", dev_type->name); goto out_up; } @@ -804,7 +804,7 @@ void scst_unregister_dev_driver(struct scst_dev_type *dev_type) scst_cleanup_proc_dev_handler_dir_entries(dev_type); - PRINT_INFO_PR("Device handler %s for type %d unloaded", + PRINT_INFO_PR("Device handler \"%s\" for type %d unloaded", dev_type->name, dev_type->type); out: @@ -827,20 +827,28 @@ int scst_register_virtual_dev_driver(struct scst_dev_type *dev_type) if (res != 0) goto out_err; - res = scst_build_proc_dev_handler_dir_entries(dev_type); - if (res < 0) - goto out_err; - - PRINT_INFO_PR("Virtuel device handler %s for type %d registered " - "successfully", dev_type->name, dev_type->type); + if (!dev_type->no_proc) { + res = scst_build_proc_dev_handler_dir_entries(dev_type); + if (res < 0) + goto out_err; + } + + if (dev_type->type != -1) { + PRINT_INFO_PR("Virtual device handler %s for type %d " + "registered successfully", dev_type->name, + dev_type->type); + } else { + PRINT_INFO_PR("Virtual device handler \"%s\" registered " + "successfully", dev_type->name); + } out: TRACE_EXIT_RES(res); return res; out_err: - PRINT_ERROR_PR("Failed to register virtual device handler %s for " - "type %d", dev_type->name, dev_type->type); + PRINT_ERROR_PR("Failed to register virtual device handler \"%s\"", + dev_type->name); goto out; } @@ -848,10 +856,10 @@ void scst_unregister_virtual_dev_driver(struct scst_dev_type *dev_type) { TRACE_ENTRY(); - scst_cleanup_proc_dev_handler_dir_entries(dev_type); + if (!dev_type->no_proc) + scst_cleanup_proc_dev_handler_dir_entries(dev_type); - PRINT_INFO_PR("Device handler %s for type %d unloaded", - dev_type->name, dev_type->type); + PRINT_INFO_PR("Device handler \"%s\" unloaded", dev_type->name); TRACE_EXIT(); return; @@ -1195,14 +1203,21 @@ static int __init init_scst(void) { struct scsi_sense_hdr *shdr; BUILD_BUG_ON((sizeof(cmd->sense_buffer) < sizeof(*shdr)) && - (sizeof(cmd->sense_buffer) >= SCSI_SENSE_BUFFERSIZE)); + (sizeof(cmd->sense_buffer) >= SCST_SENSE_BUFFERSIZE)); } #endif { struct scst_tgt_dev *t; + struct scst_cmd *c; BUILD_BUG_ON(sizeof(t->curr_sn) != sizeof(t->expected_sn)); + BUILD_BUG_ON(sizeof(c->sn) != sizeof(t->expected_sn)); } + BUILD_BUG_ON(SCST_DATA_UNKNOWN != DMA_BIDIRECTIONAL); + BUILD_BUG_ON(SCST_DATA_WRITE != DMA_TO_DEVICE); + BUILD_BUG_ON(SCST_DATA_READ != DMA_FROM_DEVICE); + BUILD_BUG_ON(SCST_DATA_NONE != DMA_NONE); + spin_lock_init(&scst_main_cmd_lists.cmd_list_lock); INIT_LIST_HEAD(&scst_main_cmd_lists.active_cmd_list); init_waitqueue_head(&scst_main_cmd_lists.cmd_list_waitQ); @@ -1275,7 +1290,7 @@ static int __init init_scst(void) scst_scsi_op_list_init(); - for (i = 0; i < ARRAY_SIZE(scst_tasklets); i++) { + for (i = 0; i < (int)ARRAY_SIZE(scst_tasklets); i++) { spin_lock_init(&scst_tasklets[i].tasklet_lock); INIT_LIST_HEAD(&scst_tasklets[i].tasklet_cmd_list); tasklet_init(&scst_tasklets[i].tasklet, (void*)scst_cmd_tasklet, @@ -1425,6 +1440,8 @@ EXPORT_SYMBOL(scst_set_cmd_error_status); EXPORT_SYMBOL(scst_set_cmd_error); EXPORT_SYMBOL(scst_set_resp_data_len); +EXPORT_SYMBOL(scst_process_active_cmd); + /* * Target Driver Side (i.e. HBA) */ @@ -1476,6 +1493,14 @@ EXPORT_SYMBOL(scst_del_all_thr_data); EXPORT_SYMBOL(scst_dev_del_all_thr_data); EXPORT_SYMBOL(scst_find_thr_data); +/* SGV pool routines */ +EXPORT_SYMBOL(sgv_pool_create); +EXPORT_SYMBOL(sgv_pool_destroy); +EXPORT_SYMBOL(sgv_pool_set_allocator); +EXPORT_SYMBOL(sgv_pool_alloc); +EXPORT_SYMBOL(sgv_pool_free); +EXPORT_SYMBOL(sgv_get_priv); + /* Generic parse() routines */ EXPORT_SYMBOL(scst_calc_block_shift); EXPORT_SYMBOL(scst_sbc_generic_parse); diff --git a/scst/src/scst_cdbprobe.h b/scst/src/scst_cdbprobe.h index deb430cc3..b40bfd0cc 100644 --- a/scst/src/scst_cdbprobe.h +++ b/scst/src/scst_cdbprobe.h @@ -1,7 +1,7 @@ /* * scst_cdbprobe.h * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * This program is free software; you can redistribute it and/or @@ -509,6 +509,6 @@ static const struct scst_sdbops scst_scsi_op_table[] = { #define WRITE_LONG_2 0xea */ -#define SCST_CDB_TBL_SIZE ((sizeof(scst_scsi_op_table)/sizeof(struct scst_sdbops))) +#define SCST_CDB_TBL_SIZE ((int)(sizeof(scst_scsi_op_table)/sizeof(struct scst_sdbops))) #endif /* __SCST_CDBPROBE_H */ diff --git a/scst/src/scst_debug.c b/scst/src/scst_debug.c index fc425c836..51da638dc 100644 --- a/scst/src/scst_debug.c +++ b/scst/src/scst_debug.c @@ -1,7 +1,7 @@ /* - * include/scst_debug.c + * scst_debug.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * Contains helper functions for execution tracing and error reporting. @@ -52,8 +52,7 @@ int debug_print_prefix(unsigned long trace_flag, const char *func, return i; } -void debug_print_buffer(unsigned long trace_flag, const void *data, - int len) +void debug_print_buffer(const void *data, int len) { int z, z1, i; const unsigned char *buf = (const unsigned char *) data; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 26b3855f9..382e788f8 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -1,7 +1,7 @@ /* * scst_lib.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * This program is free software; you can redistribute it and/or @@ -126,7 +126,7 @@ void scst_set_resp_data_len(struct scst_cmd *cmd, int resp_data_len) l += cmd->sg[i].length; if (l >= resp_data_len) { int left = resp_data_len - (l - cmd->sg[i].length); - TRACE(TRACE_SG, "cmd %p (tag %d), " + TRACE(TRACE_SG|TRACE_MEMORY, "cmd %p (tag %d), " "resp_data_len %d, i %d, cmd->sg[i].length %d, " "left %d", cmd, cmd->tag, resp_data_len, i, cmd->sg[i].length, left); @@ -387,6 +387,7 @@ out: static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, struct scst_acg_dev *acg_dev) { + int ini_sg, ini_unchecked_isa_dma, ini_use_clustering; struct scst_tgt_dev *tgt_dev; struct scst_device *dev = acg_dev->dev; int rc, i; @@ -413,6 +414,40 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev->sess = sess; atomic_set(&tgt_dev->cmd_count, 0); + tgt_dev->gfp_mask = __GFP_NOWARN; + tgt_dev->pool = &scst_sgv.norm; + + if (tgt_dev->dev->scsi_dev != NULL) { + ini_sg = tgt_dev->dev->scsi_dev->host->sg_tablesize; + ini_unchecked_isa_dma = + tgt_dev->dev->scsi_dev->host->unchecked_isa_dma; + ini_use_clustering = + (tgt_dev->dev->scsi_dev->host->use_clustering == + ENABLE_CLUSTERING); + } else { + ini_sg = (1 << 15) /* infinite */; + ini_unchecked_isa_dma = 0; + ini_use_clustering = 0; + } + tgt_dev->max_sg_cnt = min(ini_sg, sess->tgt->sg_tablesize); + + if ((sess->tgt->tgtt->use_clustering || ini_use_clustering) && + !sess->tgt->tgtt->no_clustering) { + TRACE_MEM("%s", "Use clustering"); + tgt_dev->pool = &scst_sgv.norm_clust; + } + + if (sess->tgt->tgtt->unchecked_isa_dma || ini_unchecked_isa_dma) { + TRACE_MEM("%s", "Use ISA DMA memory"); + tgt_dev->gfp_mask |= GFP_DMA; + tgt_dev->pool = &scst_sgv.dma; + } else { +#ifdef SCST_HIGHMEM + gfp_mask |= __GFP_HIGHMEM; + tgt_dev->pool = &scst_sgv.highmem; +#endif + } + tgt_dev->p_cmd_lists = &scst_main_cmd_lists; if (dev->scsi_dev != NULL) { @@ -437,7 +472,7 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev->expected_sn = 1; tgt_dev->num_free_sn_slots = ARRAY_SIZE(tgt_dev->sn_slots); tgt_dev->cur_sn_slot = &tgt_dev->sn_slots[0]; - for(i = 0; i < ARRAY_SIZE(tgt_dev->sn_slots); i++) + for(i = 0; i < (int)ARRAY_SIZE(tgt_dev->sn_slots); i++) atomic_set(&tgt_dev->sn_slots[i], 0); if (dev->handler->parse_atomic && @@ -908,8 +943,8 @@ struct scst_cmd *scst_complete_request_sense(struct scst_cmd *cmd) TRACE_BUFF_FLAG(TRACE_SCSI, "REQUEST SENSE returned", buf, len); memcpy(orig_cmd->sense_buffer, buf, - (sizeof(orig_cmd->sense_buffer) > len) ? - len : sizeof(orig_cmd->sense_buffer)); + ((int)sizeof(orig_cmd->sense_buffer) > len) ? + len : (int)sizeof(orig_cmd->sense_buffer)); } else { PRINT_ERROR_PR("%s", "Unable to get the sense via " "REQUEST SENSE, returning HARDWARE ERROR"); @@ -992,7 +1027,7 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev) { struct scsi_device *scsi_dev; unsigned char cdb[6]; - unsigned char sense[SCSI_SENSE_BUFFERSIZE]; + unsigned char *sense; int rc; TRACE_ENTRY(); @@ -1000,6 +1035,9 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev) if (tgt_dev->dev->scsi_dev == NULL) goto out; + /* We can't afford missing RELEASE due to memory shortage */ + sense = kzalloc(SCST_SENSE_BUFFERSIZE, GFP_KERNEL|__GFP_NOFAIL); + scsi_dev = tgt_dev->dev->scsi_dev; memset(cdb, 0, sizeof(cdb)); @@ -1010,13 +1048,15 @@ static void scst_send_release(struct scst_tgt_dev *tgt_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, - 3, GFP_KERNEL); + sense, SCST_DEFAULT_TIMEOUT, 3, GFP_KERNEL); if (rc) { PRINT_INFO_PR("scsi_execute() failed: %d", rc); - goto out; + goto out_free; } +out_free: + kfree(sense); + out: TRACE_EXIT(); return; @@ -1220,6 +1260,8 @@ void scst_free_cmd(struct scst_cmd *cmd) if (unlikely(cmd->mgmt_cmnd)) scst_complete_cmd_mgmt(cmd, cmd->mgmt_cmnd); + scst_check_restore_sg_buff(cmd); + if (unlikely(cmd->internal)) { if (cmd->bufflen > 0) scst_release_space(cmd); @@ -1250,7 +1292,7 @@ void scst_free_cmd(struct scst_cmd *cmd) #ifdef EXTRACHECKS if (unlikely(!cmd->sent_to_midlev)) { PRINT_ERROR_PR("Finishing not executed cmd %p (opcode " - "%d, target %s, lun %Ld, sn %d, expected_sn %d)", + "%d, target %s, lun %Ld, sn %ld, expected_sn %ld)", cmd, cmd->cdb[0], cmd->tgtt->name, (uint64_t)cmd->lun, cmd->sn, cmd->tgt_dev->expected_sn); scst_unblock_deferred(cmd->tgt_dev, cmd); @@ -1258,7 +1300,7 @@ void scst_free_cmd(struct scst_cmd *cmd) #endif if (unlikely(cmd->out_of_sn)) { - TRACE_SN("Out of SN cmd %p (tag %d, sn %d), " + TRACE_SN("Out of SN cmd %p (tag %d, sn %ld), " "destroy=%d", cmd, cmd->tag, cmd->sn, destroy); destroy = test_and_set_bit(SCST_CMD_CAN_BE_DESTROYED, &cmd->cmd_flags); @@ -1421,97 +1463,36 @@ void scst_release_request(struct scst_cmd *cmd) int scst_alloc_space(struct scst_cmd *cmd) { - int tgt_sg = cmd->tgt->sg_tablesize; - int ini_sg; int gfp_mask; int res = -ENOMEM; - int ini_unchecked_isa_dma, ini_use_clustering; - int use_clustering = 0; - struct sgv_pool *pool; int atomic = scst_cmd_atomic(cmd); + int flags; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; TRACE_ENTRY(); - if (cmd->data_buf_alloced) { - TRACE_MEM("%s", "data_buf_alloced set, returning"); - sBUG_ON(cmd->sg == NULL); - res = 0; - goto out; - } + gfp_mask = tgt_dev->gfp_mask | (atomic ? GFP_ATOMIC : GFP_KERNEL); - gfp_mask = __GFP_NOWARN; - gfp_mask |= (atomic ? GFP_ATOMIC : GFP_KERNEL); - pool = &scst_sgv.norm; - - if (cmd->dev->scsi_dev != NULL) { - ini_sg = cmd->dev->scsi_dev->host->sg_tablesize; - ini_unchecked_isa_dma = - cmd->dev->scsi_dev->host->unchecked_isa_dma; - ini_use_clustering = - (cmd->dev->scsi_dev->host->use_clustering == - ENABLE_CLUSTERING); - } - else { - ini_sg = (1 << 15) /* infinite */; - ini_unchecked_isa_dma = 0; - ini_use_clustering = 0; - } - - if ((cmd->tgtt->use_clustering || ini_use_clustering) && - !cmd->tgtt->no_clustering) - { - TRACE_MEM("%s", "Use clustering"); - pool = &scst_sgv.norm_clust; - use_clustering = 1; - } - - if (cmd->tgtt->unchecked_isa_dma || ini_unchecked_isa_dma) { - TRACE_MEM("%s", "Use ISA DMA memory"); - gfp_mask |= GFP_DMA; - pool = &scst_sgv.dma; - } else { -#ifdef SCST_HIGHMEM - gfp_mask |= __GFP_HIGHMEM; - pool = &scst_sgv.highmem; -#endif - } - - if (cmd->no_sgv) { - if (atomic) - goto out; - cmd->sg = scst_alloc(cmd->bufflen, gfp_mask, use_clustering, - &cmd->sg_cnt); - } else { - cmd->sg = sgv_pool_alloc(pool, cmd->bufflen, gfp_mask, atomic, - &cmd->sg_cnt, &cmd->sgv); - } + flags = atomic ? SCST_POOL_NO_ALLOC_ON_CACHE_MISS : 0; + if (cmd->no_sgv) + flags |= SCST_POOL_ALLOC_NO_CACHED; + cmd->sg = sgv_pool_alloc(tgt_dev->pool, cmd->bufflen, gfp_mask, flags, + &cmd->sg_cnt, &cmd->sgv, NULL); if (cmd->sg == NULL) goto out; - if (unlikely(cmd->sg_cnt > ini_sg)) { + if (unlikely(cmd->sg_cnt > tgt_dev->max_sg_cnt)) { static int ll; if (ll < 10) { PRINT_INFO("Unable to complete command due to " - "underlying device SG IO count limitation " - "(requested %d, available %d)", cmd->sg_cnt, - ini_sg); + "SG IO count limitation (requested %d, " + "available %d, tgt lim %d)", cmd->sg_cnt, + tgt_dev->max_sg_cnt, cmd->tgt->sg_tablesize); ll++; } goto out_sg_free; } - if (unlikely(cmd->sg_cnt > tgt_sg)) { - static int ll; - if (ll < 10) { - PRINT_INFO("Unable to complete command due to " - "target device %s SG IO count limitation " - "(requested %d, available %d)", cmd->tgtt->name, - cmd->sg_cnt, tgt_sg); - ll++; - } - goto out_sg_free; - } - res = 0; out: @@ -1519,10 +1500,7 @@ out: return res; out_sg_free: - if (cmd->no_sgv) - scst_free(cmd->sg, cmd->sg_cnt); - else - sgv_pool_free(cmd->sgv); + sgv_pool_free(cmd->sgv); cmd->sgv = NULL; cmd->sg = NULL; cmd->sg_cnt = 0; @@ -1533,16 +1511,15 @@ void scst_release_space(struct scst_cmd *cmd) { TRACE_ENTRY(); + if (cmd->sgv == NULL) + goto out; + if (cmd->data_buf_alloced) { TRACE_MEM("%s", "data_buf_alloced set, returning"); goto out; } - if (cmd->sgv) { - scst_check_restore_sg_buff(cmd); - sgv_pool_free(cmd->sgv); - } else if (cmd->sg) - scst_free(cmd->sg, cmd->sg_cnt); + sgv_pool_free(cmd->sgv); cmd->sgv = NULL; cmd->sg_cnt = 0; @@ -1565,7 +1542,7 @@ int __scst_get_buf(struct scst_cmd *cmd, uint8_t **buf) *buf = NULL; - if (i >= cmd->sg_cnt) + if ((i >= cmd->sg_cnt) || unlikely(sg == NULL)) goto out; #ifdef SCST_HIGHMEM /* @@ -1712,6 +1689,8 @@ int scst_get_cdb_info(const uint8_t *cdb_p, int dev_type, TRACE(TRACE_SCSI, "Unknown opcode 0x%x for type %d", op, dev_type); res = -1; + memset(info_p, 0, sizeof(*info_p)); + info_p->flags = SCST_INFO_INVALID; goto out; } @@ -1833,11 +1812,12 @@ out_err: int scst_calc_block_shift(int sector_size) { int block_shift = 0; - int t = sector_size; + int t; if (sector_size == 0) sector_size = 512; + t = sector_size; while(1) { if ((t & 1) != 0) break; @@ -2104,19 +2084,22 @@ static int scst_null_parse(struct scst_cmd *cmd, struct scst_info_cdb *info_cdb) } int scst_changer_generic_parse(struct scst_cmd *cmd, - struct scst_info_cdb *info_cdb, int nothing) + struct scst_info_cdb *info_cdb, + int (*nothing)(struct scst_cmd *cmd)) { return scst_null_parse(cmd, info_cdb); } int scst_processor_generic_parse(struct scst_cmd *cmd, - struct scst_info_cdb *info_cdb, int nothing) + struct scst_info_cdb *info_cdb, + int (*nothing)(struct scst_cmd *cmd)) { return scst_null_parse(cmd, info_cdb); } int scst_raid_generic_parse(struct scst_cmd *cmd, - struct scst_info_cdb *info_cdb, int nothing) + struct scst_info_cdb *info_cdb, + int (*nothing)(struct scst_cmd *cmd)) { return scst_null_parse(cmd, info_cdb); } @@ -2130,9 +2113,6 @@ int scst_block_generic_dev_done(struct scst_cmd *cmd, TRACE_ENTRY(); - if (unlikely(cmd->sg == NULL)) - goto out; - /* * SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len * based on cmd->status and cmd->data_direction, therefore change @@ -2149,9 +2129,8 @@ int scst_block_generic_dev_done(struct scst_cmd *cmd, buffer_size = scst_get_buf_first(cmd, &buffer); if (unlikely(buffer_size <= 0)) { - PRINT_ERROR_PR("%s: Unable to get the buffer", - __FUNCTION__); - scst_set_busy(cmd); + PRINT_ERROR_PR("%s: Unable to get the buffer " + "(%d)", __FUNCTION__, buffer_size); goto out; } @@ -2191,9 +2170,6 @@ int scst_tape_generic_dev_done(struct scst_cmd *cmd, TRACE_ENTRY(); - if (unlikely(cmd->sg == NULL)) - goto out; - /* * SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len * based on cmd->status and cmd->data_direction, therefore change @@ -2205,9 +2181,8 @@ int scst_tape_generic_dev_done(struct scst_cmd *cmd, case MODE_SELECT: buffer_size = scst_get_buf_first(cmd, &buffer); if (unlikely(buffer_size <= 0)) { - PRINT_ERROR_PR("%s: Unable to get the buffer", - __FUNCTION__); - scst_set_busy(cmd); + PRINT_ERROR_PR("%s: Unable to get the buffer (%d)", + __FUNCTION__, buffer_size); goto out; } break; @@ -2395,7 +2370,7 @@ void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, } memset(UA_entry, 0, sizeof(*UA_entry)); - if (sense_len > sizeof(UA_entry->UA_sense_buffer)) + if (sense_len > (int)sizeof(UA_entry->UA_sense_buffer)) sense_len = sizeof(UA_entry->UA_sense_buffer); memcpy(UA_entry->UA_sense_buffer, sense, sense_len); set_bit(SCST_TGT_DEV_UA_PENDING, &tgt_dev->tgt_dev_flags); @@ -2531,7 +2506,7 @@ int scst_check_hq_cmd(struct scst_cmd *cmd) struct scst_cmd *__scst_check_deferred_commands(struct scst_tgt_dev *tgt_dev) { struct scst_cmd *res = NULL, *cmd, *t; - int expected_sn = tgt_dev->expected_sn; + typeof(tgt_dev->expected_sn) expected_sn = tgt_dev->expected_sn; spin_lock_irq(&tgt_dev->sn_lock); @@ -2565,7 +2540,7 @@ restart: list_for_each_entry_safe(cmd, t, &tgt_dev->deferred_cmd_list, sn_cmd_list_entry) { if (cmd->sn == expected_sn) { - TRACE_SN("Deferred command %p (sn %d) found", + TRACE_SN("Deferred command %p (sn %ld) found", cmd, cmd->sn); tgt_dev->def_cmd_count--; list_del(&cmd->sn_cmd_list_entry); @@ -2594,12 +2569,13 @@ restart: * !! sn_slot and sn_cmd_list_entry, could be !! * !! already destroyed !! */ - TRACE_SN("cmd %p (tag %d) with skipped sn %d found", + TRACE_SN("cmd %p (tag %d) with skipped sn %ld found", cmd, cmd->tag, cmd->sn); tgt_dev->def_cmd_count--; list_del(&cmd->sn_cmd_list_entry); spin_unlock_irq(&tgt_dev->sn_lock); - EXTRACHECKS_BUG_ON(cmd->head_of_queue); + EXTRACHECKS_BUG_ON(cmd->queue_type == + SCST_CMD_QUEUE_HEAD_OF_QUEUE); if (test_and_set_bit(SCST_CMD_CAN_BE_DESTROYED, &cmd->cmd_flags)) { scst_destroy_put_cmd(cmd); @@ -2691,6 +2667,8 @@ int scst_inc_on_dev_cmd(struct scst_cmd *cmd) int res = 0; struct scst_device *dev = cmd->dev; + TRACE_ENTRY(); + sBUG_ON(cmd->blocking); atomic_inc(&dev->on_dev_count); @@ -2707,7 +2685,7 @@ int scst_inc_on_dev_cmd(struct scst_cmd *cmd) &dev->blocked_cmd_list); res = 1; } else { - __scst_block_dev(cmd->dev); + __scst_block_dev(dev); cmd->blocking = 1; } spin_unlock_bh(&dev->dev_lock); @@ -2735,17 +2713,19 @@ repeat: } spin_unlock_bh(&dev->dev_lock); } - if (unlikely(cmd->dev->dev_serialized)) { + if (unlikely(dev->dev_serialized)) { spin_lock_bh(&dev->dev_lock); barrier(); /* to reread block_count */ - if (cmd->dev->block_count == 0) { + if (dev->block_count == 0) { TRACE_MGMT_DBG("cmd %p (tag %d), blocking further " "cmds due to serializing (dev %p)", cmd, cmd->tag, dev); - __scst_block_dev(cmd->dev); + __scst_block_dev(dev); cmd->blocking = 1; } else { spin_unlock_bh(&dev->dev_lock); + TRACE_MGMT_DBG("Somebody blocked the device, " + "repeating (count %d)", dev->block_count); goto repeat; } spin_unlock_bh(&dev->dev_lock); @@ -2753,6 +2733,7 @@ repeat: #endif out: + TRACE_EXIT_RES(res); return res; out_unlock: @@ -2780,7 +2761,7 @@ void scst_unblock_cmds(struct scst_device *dev) * scst_inc_expected_sn(). */ if (likely(!cmd->internal) && likely(!cmd->retry)) { - int expected_sn; + typeof(cmd->tgt_dev->expected_sn) expected_sn; if (cmd->tgt_dev == NULL) sBUG(); expected_sn = cmd->tgt_dev->expected_sn; @@ -2812,7 +2793,7 @@ void scst_unblock_cmds(struct scst_device *dev) list_del(&cmd->blocked_cmd_list_entry); TRACE_MGMT_DBG("Adding blocked cmd %p to active cmd list", cmd); spin_lock(&cmd->cmd_lists->cmd_list_lock); - if (unlikely(cmd->head_of_queue)) + if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) list_add(&cmd->cmd_list_entry, &cmd->cmd_lists->active_cmd_list); else @@ -2833,7 +2814,7 @@ static struct scst_cmd *__scst_unblock_deferred( { struct scst_cmd *res = NULL; - if (out_of_sn_cmd->head_of_queue) { + if (out_of_sn_cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE) { TRACE_SN("HQ out_of_sn_cmd %p", out_of_sn_cmd); spin_lock_irq(&out_of_sn_cmd->tgt_dev->sn_lock); list_del(&out_of_sn_cmd->sn_cmd_list_entry); @@ -2848,8 +2829,8 @@ static struct scst_cmd *__scst_unblock_deferred( tgt_dev->def_cmd_count++; list_add_tail(&out_of_sn_cmd->sn_cmd_list_entry, &tgt_dev->skipped_sn_list); - TRACE_SN("out_of_sn_cmd %p with sn %d added to skipped_sn_list " - "(expected_sn %d)", out_of_sn_cmd, out_of_sn_cmd->sn, + TRACE_SN("out_of_sn_cmd %p with sn %ld added to skipped_sn_list " + "(expected_sn %ld)", out_of_sn_cmd, out_of_sn_cmd->sn, tgt_dev->expected_sn); spin_unlock_irq(&tgt_dev->sn_lock); } @@ -2873,7 +2854,7 @@ void scst_unblock_deferred(struct scst_tgt_dev *tgt_dev, if (cmd != NULL) { unsigned long flags; spin_lock_irqsave(&cmd->cmd_lists->cmd_list_lock, flags); - TRACE_SN("cmd %p with sn %d added to the head of active cmd " + TRACE_SN("cmd %p with sn %ld added to the head of active cmd " "list", cmd, cmd->sn); list_add(&cmd->cmd_list_entry, &cmd->cmd_lists->active_cmd_list); wake_up(&cmd->cmd_lists->cmd_list_waitQ); diff --git a/scst/src/scst_mem.c b/scst/src/scst_mem.c index 61e4759f4..6a0618715 100644 --- a/scst/src/scst_mem.c +++ b/scst/src/scst_mem.c @@ -1,7 +1,7 @@ /* * scst_sgv_pool.c * - * Copyright (C) 2006 Vladislav Bolkhovitin + * Copyright (C) 2006-2007 Vladislav Bolkhovitin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,9 +41,11 @@ * of the existing SLAB code. */ -atomic_t sgv_big_total_alloc; atomic_t sgv_other_total_alloc; +DECLARE_MUTEX(scst_sgv_pool_mutex); +LIST_HEAD(scst_sgv_pool_list); + static int scst_check_clustering(struct scatterlist *sg, int cur, int hint) { int res = -1; @@ -111,7 +113,8 @@ out_head: goto out; } -static void scst_free_sg_entries(struct scatterlist *sg, int sg_count) +static void scst_free_sys_sg_entries(struct scatterlist *sg, int sg_count, + void *priv) { int i; @@ -151,8 +154,23 @@ static void scst_free_sg_entries(struct scatterlist *sg, int sg_count) } } +static struct page *scst_alloc_sys_pages(struct scatterlist *sg, + gfp_t gfp_mask, void *priv) +{ + sg->page = alloc_pages(gfp_mask, 0); + sg->offset = 0; + sg->length = PAGE_SIZE; + TRACE_MEM("page=%p, sg=%p, priv=%p", sg->page, sg, priv); + if (sg->page == NULL) { + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of " + "sg page failed"); + } + return sg->page; +} + static int scst_alloc_sg_entries(struct scatterlist *sg, int pages, - unsigned long gfp_mask, int clustered, struct trans_tbl_ent *trans_tbl) + gfp_t gfp_mask, int clustered, struct trans_tbl_ent *trans_tbl, + const struct sgv_pool_alloc_fns *alloc_fns, void *priv) { int sg_count = 0; int pg, i, j; @@ -161,25 +179,23 @@ static int scst_alloc_sg_entries(struct scatterlist *sg, int pages, TRACE_MEM("pages=%d, clustered=%d", pages, clustered); #if 0 - mask |= __GFP_COLD; + gfp_mask |= __GFP_COLD; #endif #ifdef SCST_STRICT_SECURITY - mask |= __GFP_ZERO; + gfp_mask |= __GFP_ZERO; #endif for (pg = 0; pg < pages; pg++) { + void *rc; #ifdef DEBUG_OOM if ((scst_random() % 10000) == 55) - sg[sg_count].page = NULL; + void *rc = NULL; else #endif - sg[sg_count].page = alloc_pages(gfp_mask, 0); - if (sg[sg_count].page == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of " - "sg page failed"); + rc = alloc_fns->alloc_pages_fn(&sg[sg_count], gfp_mask, + priv); + if (rc == NULL) goto out_no_mem; - } - sg[sg_count].length = PAGE_SIZE; if (clustered) { merged = scst_check_clustering(sg, sg_count, merged); if (merged == -1) @@ -190,13 +206,16 @@ static int scst_alloc_sg_entries(struct scatterlist *sg, int pages, sg_count); } - if (clustered && trans_tbl) { + if (clustered && (trans_tbl != NULL)) { pg = 0; for (i = 0; i < pages; i++) { - int n = sg[i].length >> PAGE_SHIFT; + int n = (sg[i].length >> PAGE_SHIFT) + + ((sg[i].length & ~PAGE_MASK) != 0); trans_tbl[i].pg_count = pg; for (j = 0; j < n; j++) trans_tbl[pg++].sg_num = i+1; + TRACE_MEM("i=%d, n=%d, pg_count=%d", i, n, + trans_tbl[i].pg_count); } } @@ -205,118 +224,222 @@ out: return sg_count; out_no_mem: - scst_free_sg_entries(sg, sg_count); + alloc_fns->free_pages_fn(sg, sg_count, priv); sg_count = 0; goto out; } -struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, int size, - unsigned long gfp_mask, int atomic, int *count, - struct sgv_pool_obj **sgv) +static inline struct scatterlist *sgv_alloc_sg_entries(int pages_to_alloc, + unsigned long gfp_mask) +{ + int esz; + struct scatterlist *res; + + esz = pages_to_alloc * sizeof(*res); + res = (struct scatterlist*)kzalloc(esz, gfp_mask); + if (unlikely(res == NULL)) { + TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool_obj " + "SG vector failed (size %d)", esz); + } + + return res; +} + +struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size, + unsigned long gfp_mask, int flags, int *count, + struct sgv_pool_obj **sgv, void *priv) { struct sgv_pool_obj *obj; - int order, pages, cnt, sg; - struct scatterlist *res = NULL; + int order, pages, cnt; + struct scatterlist *res; + int pages_to_alloc; + struct kmem_cache *cache; + struct trans_tbl_ent *trans_tbl; + int no_cached = flags & SCST_POOL_ALLOC_NO_CACHED; - if (unlikely(size == 0)) - return NULL; + sBUG_ON(size == 0); pages = (size >> PAGE_SHIFT) + ((size & ~PAGE_MASK) != 0); order = get_order(size); - TRACE_MEM("size=%d, pages=%d, order=%d", size, pages, order); + TRACE_MEM("size=%d, pages=%d, order=%d, flags=%x, *sgv %p", size, pages, + order, flags, *sgv); - if (order >= SGV_POOL_ELEMENTS) { - obj = NULL; - if (atomic) - goto out; - atomic_inc(&sgv_big_total_alloc); - atomic_dec(&sgv_other_total_alloc); - res = scst_alloc(size, gfp_mask, pool->clustered, count); - goto out; + if (*sgv != NULL) { + obj = *sgv; + TRACE_MEM("Supplied sgv_obj %p", obj); + pages_to_alloc = obj->sg_count; + cache = obj->owner_cache; + trans_tbl = obj->trans_tbl; + EXTRACHECKS_BUG_ON(obj->sg_entries != NULL); + obj->sg_entries = sgv_alloc_sg_entries(pages_to_alloc, gfp_mask); + if (unlikely(obj->sg_entries == NULL)) + goto out_fail_free; + goto alloc; } - obj = kmem_cache_alloc(pool->caches[order], - gfp_mask & ~(__GFP_HIGHMEM|GFP_DMA)); - if (obj == NULL) { - if (!atomic) { - TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool_obj " - "failed (size %d)", size); + if ((order < SGV_POOL_ELEMENTS) && !no_cached) { + cache = pool->caches[order]; + obj = kmem_cache_alloc(cache, + gfp_mask & ~(__GFP_HIGHMEM|GFP_DMA)); + if (unlikely(obj == NULL)) { + TRACE(TRACE_OUT_OF_MEM, "Allocation of " + "sgv_pool_obj failed (size %d)", size); + goto out_fail; } - goto out; - } - - if (obj->owner_cache != pool->caches[order]) { - int esz, epg, eorder; - - if (atomic) - goto out_free; - - esz = (1 << order) * sizeof(obj->entries[0]); - epg = (esz >> PAGE_SHIFT) + ((esz & ~PAGE_MASK) != 0); - eorder = get_order(esz); - TRACE_MEM("Brand new sgv_obj %p (esz=%d, epg=%d, eorder=%d)", - obj, esz, epg, eorder); - - obj->eorder = eorder; - obj->entries = (struct scatterlist*)__get_free_pages( - gfp_mask|__GFP_ZERO, eorder); - if (obj->entries == NULL) { - TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool_obj " - "SG vector order %d failed", eorder); - goto out_free; + if (obj->sg_entries != NULL) { + TRACE_MEM("Cached sgv_obj %p", obj); + EXTRACHECKS_BUG_ON(obj->owner_cache != cache); + atomic_inc(&pool->acc.hit_alloc); + atomic_inc(&pool->cache_acc[order].hit_alloc); + goto success; } - - obj->sg_count = scst_alloc_sg_entries(obj->entries, (1 << order), - gfp_mask, pool->clustered, obj->trans_tbl); - if (obj->sg_count <= 0) - goto out_free_entries; - - obj->owner_cache = pool->caches[order]; + obj->owner_cache = cache; + pages_to_alloc = (1 << order); + if (flags & SCST_POOL_NO_ALLOC_ON_CACHE_MISS) { + if (flags & SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL) + goto out_return; + else + goto out_fail_free; + } + TRACE_MEM("Brand new sgv_obj %p", obj); + obj->sg_entries = sgv_alloc_sg_entries(pages_to_alloc, gfp_mask); + if (unlikely(obj->sg_entries == NULL)) + goto out_fail_free; + trans_tbl = obj->trans_tbl; + /* + * No need to clear trans_tbl, if needed, it will be fully + * rewritten in scst_alloc_sg_entries() + */ } else { - TRACE_MEM("Cached sgv_obj %p", obj); - atomic_inc(&pool->acc.hit_alloc); - atomic_inc(&pool->cache_acc[order].hit_alloc); + int sz; + pages_to_alloc = pages; + if (flags & SCST_POOL_NO_ALLOC_ON_CACHE_MISS) + goto out_return2; + cache = NULL; + sz = sizeof(*obj) + pages*sizeof(obj->sg_entries[0]); + obj = kzalloc(sz, gfp_mask); + if (unlikely(obj == NULL)) { + TRACE(TRACE_OUT_OF_MEM, "Allocation of " + "sgv_pool_obj failed (size %d)", size); + goto out_fail; + } + obj->sg_entries = (struct scatterlist*)obj->trans_tbl; + trans_tbl = NULL; + TRACE_MEM("Big or no_cached sgv_obj %p (size %d)", obj, sz); } + + obj->allocator_priv = priv; + obj->owner_pool = pool; + +alloc: + obj->sg_count = scst_alloc_sg_entries(obj->sg_entries, + pages_to_alloc, gfp_mask, pool->clustered, trans_tbl, + &pool->alloc_fns, priv); + if (unlikely(obj->sg_count <= 0)) { + if ((flags & SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL) && cache) { + kfree(obj->sg_entries); + obj->sg_entries = NULL; + goto out_return1; + } else + goto out_fail_free_sg_entries; + } + +success: atomic_inc(&pool->acc.total_alloc); - atomic_inc(&pool->cache_acc[order].total_alloc); - if (pool->clustered) - cnt = obj->trans_tbl[pages-1].sg_num; - else - cnt = pages; - sg = cnt-1; - obj->orig_sg = sg; - obj->orig_length = obj->entries[sg].length; - if (pool->clustered) { - obj->entries[sg].length = - (pages - obj->trans_tbl[sg].pg_count) << PAGE_SHIFT; - } - if (size & ~PAGE_MASK) { - obj->entries[sg].length -= PAGE_SIZE - (size & ~PAGE_MASK); + if (cache) { + int sg; + atomic_inc(&pool->cache_acc[order].total_alloc); + if (pool->clustered) + cnt = obj->trans_tbl[pages-1].sg_num; + else + cnt = obj->sg_count; + sg = cnt-1; + obj->orig_sg = sg; + obj->orig_length = obj->sg_entries[sg].length; + if (pool->clustered) { + obj->sg_entries[sg].length = + (pages - obj->trans_tbl[sg].pg_count) << PAGE_SHIFT; + } + } else { + cnt = obj->sg_count; + if (no_cached) + atomic_inc(&pool->other_alloc); + else + atomic_inc(&pool->big_alloc); } + *count = cnt; - - TRACE_MEM("sgv_obj=%p (size=%d, pages=%d, " - "sg_count=%d, count=%d, last_len=%d)", obj, size, pages, - obj->sg_count, *count, obj->entries[obj->orig_sg].length); - - res = obj->entries; + res = obj->sg_entries; *sgv = obj; + if (size & ~PAGE_MASK) + obj->sg_entries[cnt-1].length -= PAGE_SIZE - (size & ~PAGE_MASK); + + TRACE_MEM("sgv_obj=%p, sg_entries %p (size=%d, pages=%d, sg_count=%d, " + "count=%d, last_len=%d)", obj, obj->sg_entries, size, pages, + obj->sg_count, *count, obj->sg_entries[obj->orig_sg].length); + out: return res; -out_free_entries: - free_pages((unsigned long)obj->entries, obj->eorder); - obj->entries = NULL; +out_return: + obj->allocator_priv = priv; + obj->owner_pool = pool; -out_free: - kmem_cache_free(pool->caches[order], obj); - obj = NULL; +out_return1: + *sgv = obj; + obj->sg_count = pages_to_alloc; + TRACE_MEM("Returning failed sgv_obj %p (count %d)", obj, *count); + +out_return2: + *count = pages_to_alloc; + res = NULL; + goto out; + +out_fail_free_sg_entries: + if (cache) { + kfree(obj->sg_entries); + obj->sg_entries = NULL; + } + +out_fail_free: + if (cache) + kmem_cache_free(pool->caches[order], obj); + else + kfree(obj); + +out_fail: + res = NULL; + *count = 0; + *sgv = NULL; + TRACE_MEM("%s", "Allocation failed"); goto out; } -static void sgv_ctor(void *data, struct kmem_cache *c, unsigned long flags) +void *sgv_get_priv(struct sgv_pool_obj *sgv) +{ + return sgv->allocator_priv; +} + +void sgv_pool_free(struct sgv_pool_obj *sgv) +{ + TRACE_MEM("Freeing sgv_obj %p, owner_cache %p, sg_entries %p, " + "sg_count %d, allocator_priv %p", sgv, sgv->owner_cache, + sgv->sg_entries, sgv->sg_count, sgv->allocator_priv); + if (sgv->owner_cache != NULL) { + if (likely(sgv->sg_entries != NULL)) + sgv->sg_entries[sgv->orig_sg].length = sgv->orig_length; + kmem_cache_free(sgv->owner_cache, sgv); + } else { + sgv->owner_pool->alloc_fns.free_pages_fn(sgv->sg_entries, + sgv->sg_count, sgv->allocator_priv); + kfree(sgv); + } + return; +} + +static void sgv_ctor(void *data, struct kmem_cache *c, unsigned long flags) { struct sgv_pool_obj *obj = data; @@ -326,46 +449,27 @@ static void sgv_ctor(void *data, struct kmem_cache *c, unsigned long flags) TRACE_MEM("Constructor for sgv_obj %p", obj); memset(obj, 0, sizeof(*obj)); + return; } -static void __sgv_dtor(void *data, int pages) +static void sgv_dtor(void *data, struct kmem_cache *k, unsigned long f) { struct sgv_pool_obj *obj = data; - TRACE_MEM("Destructor for sgv_obj %p", obj); - if (obj->entries) { - scst_free_sg_entries(obj->entries, obj->sg_count); - free_pages((unsigned long)obj->entries, obj->eorder); + if (obj->sg_entries) { + obj->owner_pool->alloc_fns.free_pages_fn(obj->sg_entries, + obj->sg_count, obj->allocator_priv); + kfree(obj->sg_entries); } + return; } -#define SGV_DTOR_NAME(order) sgv_dtor##order -#define SGV_DTOR(order) static void sgv_dtor##order(void *d, struct kmem_cache *k, \ - unsigned long f) { __sgv_dtor(d, 1 << order); } - -SGV_DTOR(0); -SGV_DTOR(1); -SGV_DTOR(2); -SGV_DTOR(3); -SGV_DTOR(4); -SGV_DTOR(5); -SGV_DTOR(6); -SGV_DTOR(7); -SGV_DTOR(8); -SGV_DTOR(9); -SGV_DTOR(10); - -typedef void (*dtor_t)(void *, struct kmem_cache *, unsigned long); - -dtor_t cache_dtors[SGV_POOL_ELEMENTS] = - { SGV_DTOR_NAME(0), SGV_DTOR_NAME(1), SGV_DTOR_NAME(2), SGV_DTOR_NAME(3), - SGV_DTOR_NAME(4), SGV_DTOR_NAME(5), SGV_DTOR_NAME(6), SGV_DTOR_NAME(7), - SGV_DTOR_NAME(8), SGV_DTOR_NAME(9), SGV_DTOR_NAME(10) }; - struct scatterlist *scst_alloc(int size, unsigned long gfp_mask, int use_clustering, int *count) { struct scatterlist *res; int pages = (size >> PAGE_SHIFT) + ((size & ~PAGE_MASK) != 0); + struct sgv_pool_alloc_fns sys_alloc_fns = { + scst_alloc_sys_pages, scst_free_sys_sg_entries }; TRACE_ENTRY(); @@ -376,7 +480,7 @@ struct scatterlist *scst_alloc(int size, unsigned long gfp_mask, goto out; *count = scst_alloc_sg_entries(res, pages, gfp_mask, use_clustering, - NULL); + NULL, &sys_alloc_fns, NULL); if (*count <= 0) goto out_free; @@ -395,7 +499,7 @@ out_free: void scst_free(struct scatterlist *sg, int count) { TRACE_MEM("Freeing sg=%p", sg); - scst_free_sg_entries(sg, count); + scst_free_sys_sg_entries(sg, count, NULL); kfree(sg); } @@ -408,10 +512,22 @@ int sgv_pool_init(struct sgv_pool *pool, const char *name, int clustered) TRACE_ENTRY(); memset(pool, 0, sizeof(*pool)); - pool->clustered = clustered; - TRACE_MEM("sizeof(*obj)=%zd, clustered=%d, sizeof(obj->trans_tbl[0])=%zd", - sizeof(*obj), clustered, sizeof(obj->trans_tbl[0])); + atomic_set(&pool->other_alloc, 0); + atomic_set(&pool->big_alloc, 0); + atomic_set(&pool->acc.total_alloc, 0); + atomic_set(&pool->acc.hit_alloc, 0); + + pool->clustered = clustered; + pool->alloc_fns.alloc_pages_fn = scst_alloc_sys_pages; + pool->alloc_fns.free_pages_fn = scst_free_sys_sg_entries; + + TRACE_MEM("name %s, sizeof(*obj)=%zd, clustered=%d, " + "sizeof(obj->trans_tbl[0])=%zd", name, sizeof(*obj), clustered, + sizeof(obj->trans_tbl[0])); + + strncpy(pool->name, name, sizeof(pool->name)-1); + pool->name[sizeof(pool->name)-1] = '\0'; for(i = 0; i < SGV_POOL_ELEMENTS; i++) { int size, pages; @@ -427,7 +543,7 @@ int sgv_pool_init(struct sgv_pool *pool, const char *name, int clustered) scnprintf(pool->cache_names[i], sizeof(pool->cache_names[i]), "%s-%luK", name, (PAGE_SIZE >> 10) << i); pool->caches[i] = kmem_cache_create(pool->cache_names[i], - size, 0, SCST_SLAB_FLAGS, sgv_ctor, cache_dtors[i]); + size, 0, SCST_SLAB_FLAGS, sgv_ctor, sgv_dtor); if (pool->caches[i] == NULL) { TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool cache " "%s(%d) failed", name, i); @@ -435,6 +551,10 @@ int sgv_pool_init(struct sgv_pool *pool, const char *name, int clustered) } } + down(&scst_sgv_pool_mutex); + list_add_tail(&pool->sgv_pool_list_entry, &scst_sgv_pool_list); + up(&scst_sgv_pool_mutex); + res = 0; out: @@ -464,9 +584,22 @@ void sgv_pool_deinit(struct sgv_pool *pool) pool->caches[i] = NULL; } + down(&scst_sgv_pool_mutex); + list_del(&pool->sgv_pool_list_entry); + up(&scst_sgv_pool_mutex); + TRACE_EXIT(); } +void sgv_pool_set_allocator(struct sgv_pool *pool, + struct page *(*alloc_pages_fn)(struct scatterlist *, gfp_t, void *), + void (*free_pages_fn)(struct scatterlist *, int, void *)) +{ + pool->alloc_fns.alloc_pages_fn = alloc_pages_fn; + pool->alloc_fns.free_pages_fn = free_pages_fn; + return; +} + struct sgv_pool *sgv_pool_create(const char *name, int clustered) { struct sgv_pool *pool; @@ -510,7 +643,6 @@ int scst_sgv_pools_init(struct scst_sgv_pools *pools) TRACE_ENTRY(); - atomic_set(&sgv_big_total_alloc, 0); atomic_set(&sgv_other_total_alloc, 0); res = sgv_pool_init(&pools->norm, "sgv", 0); diff --git a/scst/src/scst_mem.h b/scst/src/scst_mem.h index 13350d378..2d59b9228 100644 --- a/scst/src/scst_mem.h +++ b/scst/src/scst_mem.h @@ -1,7 +1,7 @@ /* * scst_sgv_pool.h * - * Copyright (C) 2006 Vladislav Bolkhovitin + * Copyright (C) 2006-2007 Vladislav Bolkhovitin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,7 +27,7 @@ /* * sg_num is indexed by the page number, pg_count is indexed by the sg number. * Made in one entry to simplify the code (eg all sizeof(*) parts) and save - * the CPU cache for non-clustered case. + * some CPU cache for non-clustered case. */ struct trans_tbl_ent { unsigned short sg_num; @@ -37,11 +37,12 @@ struct trans_tbl_ent { struct sgv_pool_obj { struct kmem_cache *owner_cache; - int eorder; + struct sgv_pool *owner_pool; int orig_sg; int orig_length; int sg_count; - struct scatterlist *entries; + struct scatterlist *sg_entries; + void *allocator_priv; struct trans_tbl_ent trans_tbl[0]; }; @@ -50,14 +51,28 @@ struct sgv_pool_acc atomic_t total_alloc, hit_alloc; }; +struct sgv_pool_alloc_fns +{ + struct page *(*alloc_pages_fn)(struct scatterlist *sg, gfp_t gfp_mask, + void *priv); + void (*free_pages_fn)(struct scatterlist *sg, int sg_count, + void *priv); +}; + struct sgv_pool { - struct sgv_pool_acc acc; - struct sgv_pool_acc cache_acc[SGV_POOL_ELEMENTS]; - unsigned int clustered:1; + unsigned int clustered; + struct sgv_pool_alloc_fns alloc_fns; /* 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 */ struct kmem_cache *caches[SGV_POOL_ELEMENTS]; + + atomic_t big_alloc, other_alloc; + struct sgv_pool_acc acc; + struct sgv_pool_acc cache_acc[SGV_POOL_ELEMENTS]; + char cache_names[SGV_POOL_ELEMENTS][25]; + char name[25]; + struct list_head sgv_pool_list_entry; }; struct scst_sgv_pools @@ -69,29 +84,17 @@ struct scst_sgv_pools #endif }; -extern atomic_t sgv_big_total_alloc; extern atomic_t sgv_other_total_alloc; +extern struct semaphore scst_sgv_pool_mutex; +extern struct list_head scst_sgv_pool_list; -extern struct sgv_pool *sgv_pool_create(const char *name, int clustered); -extern void sgv_pool_destroy(struct sgv_pool *pool); - -extern int sgv_pool_init(struct sgv_pool *pool, const char *name, +int sgv_pool_init(struct sgv_pool *pool, const char *name, int clustered); -extern void sgv_pool_deinit(struct sgv_pool *pool); - -extern struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, int size, - unsigned long gfp_mask, int atomic, int *count, - struct sgv_pool_obj **sgv); -static inline void sgv_pool_free(struct sgv_pool_obj *sgv) -{ - TRACE_MEM("Freeing sgv_obj %p", sgv); - sgv->entries[sgv->orig_sg].length = sgv->orig_length; - kmem_cache_free(sgv->owner_cache, sgv); -} +void sgv_pool_deinit(struct sgv_pool *pool); static inline struct scatterlist *sgv_pool_sg(struct sgv_pool_obj *obj) { - return obj->entries; + return obj->sg_entries; } extern int scst_sgv_pools_init(struct scst_sgv_pools *pools); diff --git a/scst/src/scst_module.c b/scst/src/scst_module.c index 52136ff5c..dea93d54f 100644 --- a/scst/src/scst_module.c +++ b/scst/src/scst_module.c @@ -1,7 +1,7 @@ /* * scst_module.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * Support for loading target modules. The usage is similar to scsi_module.c diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index d0da96988..61648a250 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -1,7 +1,7 @@ /* * scst_priv.h * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * This program is free software; you can redistribute it and/or @@ -55,8 +55,8 @@ extern unsigned long scst_trace_flag; ~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG) */ #define SCST_DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \ - TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \ - TRACE_RETRY) + TRACE_LINE | TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | \ + TRACE_MGMT_DEBUG | TRACE_RETRY) #define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args) @@ -214,7 +214,7 @@ extern int __scst_add_cmd_threads(int num); extern void __scst_del_cmd_threads(int num); extern spinlock_t scst_temp_UA_lock; -extern uint8_t scst_temp_UA[SCSI_SENSE_BUFFERSIZE]; +extern uint8_t scst_temp_UA[SCST_SENSE_BUFFERSIZE]; extern struct scst_cmd *__scst_check_deferred_commands( struct scst_tgt_dev *tgt_dev); @@ -392,20 +392,6 @@ static inline int scst_is_implicit_hq(struct scst_cmd *cmd) ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16))))); } -/* - * Returns 1, if cmd's CDB is locally handled by SCST and 0 otherwise. - * Dev handlers parse() and dev_done() not called for such commands. - */ -static inline int scst_is_cmd_local(struct scst_cmd *cmd) -{ - int res = 0; - switch (cmd->cdb[0]) { - case REPORT_LUNS: - res = 1; - } - return res; -} - /* * Some notes on devices "blocking". Blocking means that no * commands will go from SCST to underlying SCSI device until it @@ -424,7 +410,7 @@ static inline void __scst_block_dev(struct scst_device *dev) } static inline void scst_block_dev(struct scst_device *dev, - unsigned int outstanding) + int outstanding) { spin_lock_bh(&dev->dev_lock); __scst_block_dev(dev); @@ -536,6 +522,10 @@ static inline void scst_set_sense(uint8_t *buffer, int len, int key, static inline void scst_check_restore_sg_buff(struct scst_cmd *cmd) { if (cmd->sg_buff_modified) { + TRACE_MEM("cmd %p, sg %p, orig_sg_entry %d, " + "orig_entry_len %d, orig_sg_cnt %d", cmd, cmd->sg, + cmd->orig_sg_entry, cmd->orig_entry_len, + cmd->orig_sg_cnt); cmd->sg[cmd->orig_sg_entry].length = cmd->orig_entry_len; cmd->sg_cnt = cmd->orig_sg_cnt; } diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index f6116e0b2..9e37e0b40 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -1,7 +1,7 @@ /* * scst_proc.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * This program is free software; you can redistribute it and/or @@ -948,9 +948,8 @@ int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type) TRACE_ENTRY(); - if (dev_type->proc_dev_type_root) { - goto out; - } + sBUG_ON(dev_type->proc_dev_type_root); + /* create the proc directory entry for the dev type handler */ dev_type->proc_dev_type_root = proc_mkdir(dev_type->name, scst_proc_scsi_tgt); @@ -961,15 +960,17 @@ int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type) } scst_dev_handler_type_proc_data.data = dev_type; - p = scst_create_proc_entry(dev_type->proc_dev_type_root, + if (dev_type->type >= 0) { + p = scst_create_proc_entry(dev_type->proc_dev_type_root, SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, &scst_dev_handler_type_proc_data); - if (p == NULL) { - PRINT_ERROR_PR("Not enough memory to register dev " - "handler entry %s in /proc/%s/%s", - SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, - SCST_PROC_ENTRY_NAME, dev_type->name); - goto out_remove; + if (p == NULL) { + PRINT_ERROR_PR("Not enough memory to register dev " + "handler entry %s in /proc/%s/%s", + SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, + SCST_PROC_ENTRY_NAME, dev_type->name); + goto out_remove; + } } if (dev_type->read_proc || dev_type->write_proc) { @@ -991,8 +992,9 @@ out: return res; out_remove1: - remove_proc_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, - dev_type->proc_dev_type_root); + if (dev_type->type >= 0) + remove_proc_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, + dev_type->proc_dev_type_root); out_remove: remove_proc_entry(dev_type->name, scst_proc_scsi_tgt); @@ -1006,16 +1008,16 @@ void scst_cleanup_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type) { TRACE_ENTRY(); - if (dev_type->proc_dev_type_root) { + sBUG_ON(dev_type->proc_dev_type_root == NULL); + + if (dev_type->type >= 0) { remove_proc_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, dev_type->proc_dev_type_root); - if (dev_type->read_proc || dev_type->write_proc) { - remove_proc_entry(dev_type->name, - dev_type->proc_dev_type_root); - } - remove_proc_entry(dev_type->name, scst_proc_scsi_tgt); - dev_type->proc_dev_type_root = NULL; } + if (dev_type->read_proc || dev_type->write_proc) + remove_proc_entry(dev_type->name, dev_type->proc_dev_type_root); + remove_proc_entry(dev_type->name, scst_proc_scsi_tgt); + dev_type->proc_dev_type_root = NULL; TRACE_EXIT(); return; @@ -1208,7 +1210,7 @@ static int scst_proc_assign_handler(char *buf) { int res = 0; char *p = buf, *e, *ee; - int host, channel = 0, id = 0, lun = 0; + unsigned int host, channel = 0, id = 0, lun = 0; struct scst_device *d, *dev = NULL; struct scst_dev_type *dt, *handler = NULL; @@ -1308,7 +1310,7 @@ static int scst_proc_groups_devices_write(struct file *file, const char __user * { int res = length, action, virt = 0, rc, read_only = 0; char *buffer, *p, *e = NULL; - int host, channel = 0, id = 0, lun = 0, virt_lun; + 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; struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp; struct scst_device *d, *dev = NULL; @@ -1671,7 +1673,7 @@ static int scst_dev_handler_type_info_show(struct seq_file *seq, void *v) TRACE_ENTRY(); seq_printf(seq, "%d - %s\n", dev_type->type, - dev_type->type > ARRAY_SIZE(scst_proc_dev_handler_type) ? + dev_type->type > (int)ARRAY_SIZE(scst_proc_dev_handler_type) ? "unknown" : scst_proc_dev_handler_type[dev_type->type]); TRACE_EXIT(); @@ -1721,37 +1723,41 @@ static struct scst_proc_data scst_sessions_proc_data = { .show = scst_sessions_info_show, }; -static int scst_do_sgv_read(struct seq_file *seq, const struct sgv_pool *pool, const char *name) +static void scst_do_sgv_read(struct seq_file *seq, const struct sgv_pool *pool) { int i; - seq_printf(seq, "\n%-20s %-11d %-11d\n", name, atomic_read(&pool->acc.hit_alloc), + seq_printf(seq, "\n%-30s %-11d %-11d\n", pool->name, + atomic_read(&pool->acc.hit_alloc), atomic_read(&pool->acc.total_alloc)); for (i = 0; i < SGV_POOL_ELEMENTS; i++) { - seq_printf(seq, " %-18s %-11d %-11d\n", pool->cache_names[i], + seq_printf(seq, " %-28s %-11d %-11d\n", pool->cache_names[i], atomic_read(&pool->cache_acc[i].hit_alloc), atomic_read(&pool->cache_acc[i].total_alloc)); } - return 0; + + seq_printf(seq, " %-28s %-11d %-11d\n", "big/other", atomic_read(&pool->big_alloc), + atomic_read(&pool->other_alloc)); + + return; } static int scst_sgv_info_show(struct seq_file *seq, void *v) { + struct sgv_pool *pool; + TRACE_ENTRY(); - seq_printf(seq, "%-20s %-11s %-11s", "Name", "Hit", "Total"); + seq_printf(seq, "%-30s %-11s %-11s", "Name", "Hit", "Total"); - scst_do_sgv_read(seq, &scst_sgv.norm, "sgv"); - scst_do_sgv_read(seq, &scst_sgv.norm_clust, "sgv-clust"); - scst_do_sgv_read(seq, &scst_sgv.dma, "sgv-dma"); + down(&scst_sgv_pool_mutex); + list_for_each_entry(pool, &scst_sgv_pool_list, sgv_pool_list_entry) { + scst_do_sgv_read(seq, pool); + } + up(&scst_sgv_pool_mutex); -#ifdef SCST_HIGHMEM - scst_do_sgv_read(seq, &scst_sgv.highmem, "sgv-highmem"); -#endif - - seq_printf(seq, "\n%-32s %-11d\n", "big", atomic_read(&sgv_big_total_alloc)); - seq_printf(seq, "%-32s %-11d\n", "other", atomic_read(&sgv_other_total_alloc)); + seq_printf(seq, "\n%-42s %-11d\n", "other", atomic_read(&sgv_other_total_alloc)); TRACE_EXIT(); return 0; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 49ded0581..7ad7aa398 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -1,7 +1,7 @@ /* * scst_targ.c * - * Copyright (C) 2004-2006 Vladislav Bolkhovitin + * Copyright (C) 2004-2007 Vladislav Bolkhovitin * and Leonid Stoljar * * This program is free software; you can redistribute it and/or @@ -32,7 +32,6 @@ static void scst_cmd_set_sn(struct scst_cmd *cmd); static int __scst_init_cmd(struct scst_cmd *cmd); -static int scst_process_active_cmd(struct scst_cmd *cmd, int context); static inline void scst_schedule_tasklet(struct scst_cmd *cmd) { @@ -83,7 +82,7 @@ struct scst_cmd *scst_rx_cmd(struct scst_session *sess, cmd->lun = scst_unpack_lun(lun, lun_len); - if (cdb_len <= MAX_COMMAND_SIZE) { + if (cdb_len <= SCST_MAX_CDB_SIZE) { memcpy(cmd->cdb, cdb, cdb_len); cmd->cdb_len = cdb_len; } @@ -112,6 +111,8 @@ static int scst_init_cmd(struct scst_cmd *cmd, int context) rc = __scst_init_cmd(cmd); if (unlikely(rc > 0)) goto out_redirect; + else if (unlikely(rc != 0)) + goto out; /* Small context optimization */ if (((context == SCST_CONTEXT_TASKLET) || @@ -249,7 +250,7 @@ active: case SCST_CONTEXT_THREAD: spin_lock_irqsave(&cmd->cmd_lists->cmd_list_lock, flags); TRACE_DBG("Adding cmd %p to active cmd list", cmd); - if (unlikely(cmd->head_of_queue)) + if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) list_add(&cmd->cmd_list_entry, &cmd->cmd_lists->active_cmd_list); else @@ -272,8 +273,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) struct scst_device *dev = cmd->dev; struct scst_info_cdb cdb_info; int atomic = scst_cmd_atomic(cmd); - int orig_bufflen = cmd->bufflen; - int set_dir = 1; + int orig_bufflen; TRACE_ENTRY(); @@ -285,6 +285,11 @@ static int scst_parse_cmd(struct scst_cmd *cmd) goto out; } + cmd->inc_expected_sn_on_done = dev->handler->inc_expected_sn_on_done; + + if (cmd->skip_parse) + goto call_parse; + /* * Expected transfer data supplied by the SCSI transport via the * target driver are untrusted, so we prefer to fetch them from CDB. @@ -319,8 +324,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) SCST_LOAD_SENSE(scst_sense_invalid_opcode)); goto out_xmit; } - } - else { + } else { PRINT_ERROR_PR("Unknown opcode 0x%02x for %s and " "target %s not supplied expected values. " "Returning INVALID OPCODE.", cmd->cdb[0], @@ -363,14 +367,32 @@ static int scst_parse_cmd(struct scst_cmd *cmd) goto out_xmit; } +call_parse: + orig_bufflen = cmd->bufflen; + if (likely(!scst_is_cmd_local(cmd))) { TRACE_DBG("Calling dev handler %s parse(%p)", dev->handler->name, cmd); TRACE_BUFF_FLAG(TRACE_SEND_BOT, "Parsing: ", cmd->cdb, cmd->cdb_len); state = dev->handler->parse(cmd, &cdb_info); + /* Caution: cmd can be already dead here */ TRACE_DBG("Dev handler %s parse() returned %d", dev->handler->name, state); + switch (state) { + case SCST_CMD_STATE_NEED_THREAD_CTX: + TRACE_DBG("Dev handler %s parse() requested thread " + "context, rescheduling", dev->handler->name); + res = SCST_CMD_STATE_RES_NEED_THREAD; + goto out; + + case SCST_CMD_STATE_STOP: + TRACE_DBG("Dev handler %s parse() requested stop " + "processing", dev->handler->name); + res = SCST_CMD_STATE_RES_CONT_NEXT; + goto out; + } + if (state == SCST_CMD_STATE_DEFAULT) state = SCST_CMD_STATE_PREPARE_SPACE; } @@ -390,32 +412,32 @@ static int scst_parse_cmd(struct scst_cmd *cmd) if (cmd->data_len == -1) cmd->data_len = cmd->bufflen; - if (cmd->data_buf_alloced && (orig_bufflen < cmd->bufflen)) { + if (cmd->data_buf_alloced && (orig_bufflen > cmd->bufflen)) { PRINT_ERROR_PR("Target driver supplied data buffer (size %d), " - "is less, than required (size %d)", orig_bufflen, - cmd->bufflen); + "is less, than required (size %d)", cmd->bufflen, + orig_bufflen); goto out_error; } #ifdef EXTRACHECKS - if (state != SCST_CMD_STATE_NEED_THREAD_CTX) { - if (((cmd->data_direction == SCST_DATA_UNKNOWN) && - (state != SCST_CMD_STATE_DEV_PARSE)) || - ((cmd->bufflen != 0) && - (cmd->data_direction == SCST_DATA_NONE)) || - ((cmd->bufflen == 0) && - (cmd->data_direction != SCST_DATA_NONE)) || - ((cmd->bufflen != 0) && (cmd->sg == NULL) && - (state > SCST_CMD_STATE_PREPARE_SPACE))) - { - PRINT_ERROR_PR("Dev handler %s parse() returned " - "invalid cmd data_direction %d, " - "bufflen %zd or state %d (opcode 0x%x)", - dev->handler->name, - cmd->data_direction, cmd->bufflen, - state, cmd->cdb[0]); - goto out_error; - } + if ((state != SCST_CMD_STATE_XMIT_RESP) && + (((cmd->data_direction == SCST_DATA_UNKNOWN) && + (state != SCST_CMD_STATE_DEV_PARSE)) || + ((cmd->bufflen != 0) && + (cmd->data_direction == SCST_DATA_NONE) && + (cmd->status == 0)) || + ((cmd->bufflen == 0) && + (cmd->data_direction != SCST_DATA_NONE)) || + ((cmd->bufflen != 0) && (cmd->sg == NULL) && + (state > SCST_CMD_STATE_PREPARE_SPACE)))) + { + PRINT_ERROR_PR("Dev handler %s parse() returned " + "invalid cmd data_direction %d, " + "bufflen %zd or state %d (opcode 0x%x)", + dev->handler->name, + cmd->data_direction, cmd->bufflen, + state, cmd->cdb[0]); + goto out_error; } #endif @@ -431,14 +453,6 @@ static int scst_parse_cmd(struct scst_cmd *cmd) res = SCST_CMD_STATE_RES_CONT_SAME; break; - - case SCST_CMD_STATE_NEED_THREAD_CTX: - TRACE_DBG("Dev handler %s parse() requested thread " - "context, rescheduling", dev->handler->name); - res = SCST_CMD_STATE_RES_NEED_THREAD; - set_dir = 0; - break; - default: if (state >= 0) { PRINT_ERROR_PR("Dev handler %s parse() returned " @@ -452,7 +466,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd) goto out_error; } - if ((cmd->resp_data_len == -1) && set_dir) { + if (cmd->resp_data_len == -1) { if (cmd->data_direction == SCST_DATA_READ) cmd->resp_data_len = cmd->bufflen; else @@ -572,23 +586,29 @@ static void scst_low_cur_max_cmd_mem(void) static int scst_prepare_space(struct scst_cmd *cmd) { - int r, res = SCST_CMD_STATE_RES_CONT_SAME; + int r = 0, res = SCST_CMD_STATE_RES_CONT_SAME; TRACE_ENTRY(); if (cmd->data_direction == SCST_DATA_NONE) goto prep_done; - r = scst_check_mem(cmd); - if (unlikely(r != 0)) - goto out; - if (cmd->data_buf_tgt_alloc) { int orig_bufflen = cmd->bufflen; + TRACE_MEM("%s", "Custom tgt data buf allocation requested"); + + if (unlikely(cmd->data_buf_alloced)) { + PRINT_ERROR_PR("Target driver %s requested own data " + "allocation, but dev handler %s already " + "allocated them", cmd->tgtt->name, + cmd->dev->handler->name); + goto out_error; + } + r = cmd->tgtt->alloc_data_buf(cmd); if (r > 0) - r = scst_alloc_space(cmd); + goto alloc; else if (r == 0) { cmd->data_buf_alloced = 1; if (unlikely(orig_bufflen < cmd->bufflen)) { @@ -596,16 +616,23 @@ static int scst_prepare_space(struct scst_cmd *cmd) "buffer (size %d), is less, than " "required (size %d)", orig_bufflen, cmd->bufflen); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_hardw_error)); - cmd->state = SCST_CMD_STATE_DEV_DONE; - res = SCST_CMD_STATE_RES_CONT_SAME; - goto out; + goto out_error; } } - } else - r = scst_alloc_space(cmd); + goto check; + } +alloc: + if (!cmd->data_buf_alloced) { + r = scst_check_mem(cmd); + if (unlikely(r != 0)) + goto out; + r = scst_alloc_space(cmd); + } else { + TRACE_MEM("%s", "data_buf_alloced set, returning"); + } + +check: if (r != 0) { if (scst_cmd_atomic(cmd)) { TRACE_MEM("%s", "Atomic memory allocation failed, " @@ -667,6 +694,12 @@ out_no_space: cmd->state = SCST_CMD_STATE_DEV_DONE; res = SCST_CMD_STATE_RES_CONT_SAME; goto out; + +out_error: + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); + cmd->state = SCST_CMD_STATE_DEV_DONE; + res = SCST_CMD_STATE_RES_CONT_SAME; + goto out; } void scst_restart_cmd(struct scst_cmd *cmd, int status, int pref_context) @@ -898,7 +931,7 @@ void scst_proccess_redirect_cmd(struct scst_cmd *cmd, int context, scst_check_retries(cmd->tgt); spin_lock_irqsave(&cmd->cmd_lists->cmd_list_lock, flags); TRACE_DBG("Adding cmd %p to active cmd list", cmd); - if (unlikely(cmd->head_of_queue)) + if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) list_add(&cmd->cmd_list_entry, &cmd->cmd_lists->active_cmd_list); else @@ -975,6 +1008,24 @@ void scst_rx_data(struct scst_cmd *cmd, int status, int pref_context) return; } +static void scst_inc_check_expected_sn(struct scst_cmd *cmd) +{ + struct scst_cmd *c; + + if (likely(cmd->queue_type != SCST_CMD_QUEUE_HEAD_OF_QUEUE)) + scst_inc_expected_sn(cmd->tgt_dev, cmd->sn_slot); + c = scst_check_deferred_commands(cmd->tgt_dev); + if (c != NULL) { + unsigned long flags; + spin_lock_irqsave(&c->cmd_lists->cmd_list_lock, flags); + TRACE_SN("Adding cmd %p to active cmd list", c); + list_add_tail(&c->cmd_list_entry, + &c->cmd_lists->active_cmd_list); + wake_up(&c->cmd_lists->cmd_list_waitQ); + spin_unlock_irqrestore(&c->cmd_lists->cmd_list_lock, flags); + } +} + static void scst_do_cmd_done(struct scst_cmd *cmd, int result, const uint8_t *rq_sense, int rq_sense_len, int resid) { @@ -982,6 +1033,9 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result, TRACE_ENTRY(); + if (cmd->inc_expected_sn_on_done) + scst_inc_check_expected_sn(cmd); + cmd->status = result & 0xff; cmd->msg_status = msg_byte(result); cmd->host_status = host_byte(result); @@ -1117,7 +1171,7 @@ static void scst_cmd_done(void *data, char *sense, int result, int resid) if (cmd == NULL) goto out; - scst_do_cmd_done(cmd, result, sense, SCSI_SENSE_BUFFERSIZE, resid); + scst_do_cmd_done(cmd, result, sense, SCST_SENSE_BUFFERSIZE, resid); cmd->state = SCST_CMD_STATE_DEV_DONE; @@ -1136,6 +1190,9 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state) scst_dec_on_dev_cmd(cmd, 0); + if (cmd->inc_expected_sn_on_done) + scst_inc_check_expected_sn(cmd); + if (next_state == SCST_CMD_STATE_DEFAULT) next_state = SCST_CMD_STATE_DEV_DONE; @@ -1740,7 +1797,7 @@ inc: */ tgt_dev->expected_sn++; smp_mb(); /* write must be before def_cmd_count read */ - TRACE_SN("Next expected_sn: %d", tgt_dev->expected_sn); + TRACE_SN("Next expected_sn: %ld", tgt_dev->expected_sn); out: return; @@ -1751,7 +1808,7 @@ static int scst_send_to_midlev(struct scst_cmd *cmd) int res, rc; struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; struct scst_device *dev = cmd->dev; - int expected_sn; + typeof(tgt_dev->expected_sn) expected_sn; int count; TRACE_ENTRY(); @@ -1780,7 +1837,7 @@ static int scst_send_to_midlev(struct scst_cmd *cmd) EXTRACHECKS_BUG_ON(cmd->no_sn); - if (unlikely(cmd->head_of_queue)) { + if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) { /* * W/o get() there will be a race, when cmd is executed and * destroyed before "goto out_unplug" @@ -1817,8 +1874,8 @@ static int scst_send_to_midlev(struct scst_cmd *cmd) cmd->state = SCST_CMD_STATE_DEV_DONE; res = SCST_CMD_STATE_RES_CONT_SAME; } else { - TRACE_SN("Deferring cmd %p (sn=%d, " - "expected_sn=%d, hq_cmd_active=%d)", cmd, + TRACE_SN("Deferring cmd %p (sn=%ld, " + "expected_sn=%ld, hq_cmd_active=%d)", cmd, cmd->sn, expected_sn, test_bit(SCST_TGT_DEV_HQ_ACTIVE, &tgt_dev->tgt_dev_flags)); @@ -1830,7 +1887,7 @@ static int scst_send_to_midlev(struct scst_cmd *cmd) __scst_dec_on_dev_cmd(dev, cmd_blocking); goto out_dec_cmd_count; } else { - TRACE_SN("Somebody incremented expected_sn %d, " + TRACE_SN("Somebody incremented expected_sn %ld, " "continuing", expected_sn); tgt_dev->def_cmd_count--; spin_unlock_irq(&tgt_dev->sn_lock); @@ -1841,7 +1898,8 @@ exec: count = 0; while(1) { atomic_t *slot = cmd->sn_slot; - int hq = cmd->head_of_queue; + int hq = (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE); + int inc_expected_sn_on_done = cmd->inc_expected_sn_on_done; rc = scst_do_send_to_midlev(cmd); if (rc == SCST_EXEC_NEED_THREAD) { TRACE_DBG("%s", "scst_do_send_to_midlev() requested " @@ -1865,7 +1923,7 @@ exec: sBUG_ON(rc != SCST_EXEC_COMPLETED); /* !! At this point cmd can be already freed !! */ count++; - if (likely(!hq)) + if ( !inc_expected_sn_on_done && likely(!hq)) scst_inc_expected_sn(tgt_dev, slot); cmd = scst_check_deferred_commands(tgt_dev); if (cmd == NULL) @@ -2245,7 +2303,7 @@ static int scst_xmit_response(struct scst_cmd *cmd) * commands. */ if (unlikely(!cmd->sent_to_midlev) && (cmd->tgt_dev != NULL)) { - TRACE_SN("cmd %p was not sent to mid-lev (sn %d)", + TRACE_SN("cmd %p was not sent to mid-lev (sn %ld)", cmd, cmd->sn); scst_unblock_deferred(cmd->tgt_dev, cmd); cmd->sent_to_midlev = 1; @@ -2438,7 +2496,7 @@ static void scst_cmd_set_sn(struct scst_cmd *cmd) if (likely(tgt_dev->num_free_sn_slots >= 0)) { if (atomic_inc_return(tgt_dev->cur_sn_slot) == 1) { tgt_dev->curr_sn++; - TRACE_SN("Incremented curr_sn %d", + TRACE_SN("Incremented curr_sn %ld", tgt_dev->curr_sn); } cmd->sn_slot = tgt_dev->cur_sn_slot; @@ -2481,7 +2539,6 @@ ordered: case SCST_CMD_QUEUE_HEAD_OF_QUEUE: TRACE(TRACE_SCSI|TRACE_SCSI_SERIALIZING, "HQ cmd %p " "(op %x)", cmd, cmd->cdb[0]); - cmd->head_of_queue = 1; spin_lock_irqsave(&tgt_dev->sn_lock, flags); /* Add in the head as required by SAM */ list_add(&cmd->sn_cmd_list_entry, &tgt_dev->hq_cmd_list); @@ -2495,8 +2552,8 @@ ordered: goto ordered; } - TRACE_SN("cmd(%p)->sn: %d (tgt_dev %p, *cur_sn_slot %d, " - "num_free_sn_slots %d, prev_cmd_ordered %d, " + TRACE_SN("cmd(%p)->sn: %ld (tgt_dev %p, *cur_sn_slot %d, " + "num_free_sn_slots %d, prev_cmd_ordered %ld, " "cur_sn_slot %d)", cmd, cmd->sn, tgt_dev, atomic_read(tgt_dev->cur_sn_slot), tgt_dev->num_free_sn_slots, tgt_dev->prev_cmd_ordered, @@ -2651,7 +2708,7 @@ restart: spin_lock(&cmd->cmd_lists->cmd_list_lock); TRACE_MGMT_DBG("Adding cmd %p to active cmd list", cmd); - if (unlikely(cmd->head_of_queue)) + if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) list_add(&cmd->cmd_list_entry, &cmd->cmd_lists->active_cmd_list); else @@ -2720,7 +2777,7 @@ int scst_init_cmd_thread(void *arg) } /* Called with no locks held */ -static int scst_process_active_cmd(struct scst_cmd *cmd, int context) +void scst_process_active_cmd(struct scst_cmd *cmd, int context) { int res; @@ -2813,8 +2870,8 @@ static int scst_process_active_cmd(struct scst_cmd *cmd, int context) } else sBUG(); - TRACE_EXIT_RES(res); - return res; + TRACE_EXIT(); + return; } /* Called under cmd_list_lock and IRQs disabled */ @@ -2829,17 +2886,12 @@ static void scst_do_job_active(struct list_head *cmd_list, #endif while (!list_empty(cmd_list)) { - int rc; struct scst_cmd *cmd = list_entry(cmd_list->next, typeof(*cmd), cmd_list_entry); TRACE_DBG("Deleting cmd %p from active cmd list", cmd); list_del(&cmd->cmd_list_entry); spin_unlock_irq(cmd_list_lock); - rc = scst_process_active_cmd(cmd, context); - if (unlikely((rc != SCST_CMD_STATE_RES_CONT_NEXT) && - (rc != SCST_CMD_STATE_RES_NEED_THREAD))) { - sBUG(); - } + scst_process_active_cmd(cmd, context); spin_lock_irq(cmd_list_lock); } @@ -3302,7 +3354,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) } scst_cmd_get(cmd); spin_unlock_irq(&sess->sess_list_lock); - TRACE(TRACE_MGMT, "Cmd %p for tag %d (sn %d) found, " + TRACE(TRACE_MGMT, "Cmd %p for tag %d (sn %ld) found, " "aborting it", cmd, mcmd->tag, cmd->sn); mcmd->cmd_to_abort = cmd; scst_abort_cmd(cmd, mcmd, 0, 1); @@ -3646,36 +3698,38 @@ static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd) mcmd->sess->tgt->tgtt->name); } - switch (mcmd->fn) { - case SCST_ABORT_TASK_SET: - case SCST_CLEAR_TASK_SET: - case SCST_LUN_RESET: - scst_unblock_dev(mcmd->mcmd_tgt_dev->dev); - break; + if (mcmd->mcmd_tgt_dev != NULL) { + switch (mcmd->fn) { + case SCST_ABORT_TASK_SET: + case SCST_CLEAR_TASK_SET: + case SCST_LUN_RESET: + scst_unblock_dev(mcmd->mcmd_tgt_dev->dev); + break; - case SCST_TARGET_RESET: - case SCST_ABORT_ALL_TASKS: - case SCST_NEXUS_LOSS: - down(&scst_mutex); - list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { - scst_unblock_dev(dev); + case SCST_TARGET_RESET: + case SCST_ABORT_ALL_TASKS: + case SCST_NEXUS_LOSS: + down(&scst_mutex); + list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { + scst_unblock_dev(dev); + } + up(&scst_mutex); + break; + + case SCST_NEXUS_LOSS_SESS: + case SCST_ABORT_ALL_TASKS_SESS: + down(&scst_mutex); + list_for_each_entry(tgt_dev, &mcmd->sess->sess_tgt_dev_list, + sess_tgt_dev_list_entry) { + scst_unblock_dev(tgt_dev->dev); + } + up(&scst_mutex); + break; + + case SCST_CLEAR_ACA: + default: + break; } - up(&scst_mutex); - break; - - case SCST_NEXUS_LOSS_SESS: - case SCST_ABORT_ALL_TASKS_SESS: - down(&scst_mutex); - list_for_each_entry(tgt_dev, &mcmd->sess->sess_tgt_dev_list, - sess_tgt_dev_list_entry) { - scst_unblock_dev(tgt_dev->dev); - } - up(&scst_mutex); - break; - - case SCST_CLEAR_ACA: - default: - break; } mcmd->tgt_priv = NULL;