diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..3fc8dd41f --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,61 @@ +COMMAND=linuxdoc --backend= + +SOURCE_NAME=scst_pg + +SOURCE=$(SOURCE_NAME).sgml + +default: html txt pdf + +all: html txt pdf tex dvi ps info lyx rtf + +txt: $(SOURCE_NAME).txt + +html: $(SOURCE_NAME).html + +tex: $(SOURCE_NAME).tex + +dvi: $(SOURCE_NAME).dvi + +ps: $(SOURCE_NAME).ps + +pdf: $(SOURCE_NAME).pdf + +info: $(SOURCE_NAME).info + +lyx: $(SOURCE_NAME).lyx + +rtf: $(SOURCE_NAME).rtf + +$(SOURCE_NAME).txt: $(SOURCE) + $(COMMAND)txt $(SOURCE) + +$(SOURCE_NAME).html: $(SOURCE) + $(COMMAND)html --split=0 $(SOURCE) + +$(SOURCE_NAME).tex: $(SOURCE) + $(COMMAND)latex -o tex $(SOURCE) + +$(SOURCE_NAME).dvi: $(SOURCE) + $(COMMAND)latex -o dvi $(SOURCE) + +$(SOURCE_NAME).ps: $(SOURCE) + $(COMMAND)latex -o ps $(SOURCE) + +$(SOURCE_NAME).pdf: $(SOURCE) + $(COMMAND)latex -o pdf $(SOURCE) + +$(SOURCE_NAME).info: $(SOURCE) + $(COMMAND)info $(SOURCE) + +$(SOURCE_NAME).lyx: $(SOURCE) + $(COMMAND)lyx $(SOURCE) + +$(SOURCE_NAME).rtf: $(SOURCE) + $(COMMAND)rtf $(SOURCE) + +clean: + rm -f *.txt *.html *.tex *.dvi *.ps *.pdf *.info *.lyx *.rtf + +extraclean: clean + +.PHONY: all default html txt pdf tex dvi ps info lyx rtf clean extraclean diff --git a/doc/fig1.png b/doc/fig1.png new file mode 100644 index 000000000..94b9fa071 Binary files /dev/null and b/doc/fig1.png differ diff --git a/doc/fig2.png b/doc/fig2.png new file mode 100644 index 000000000..3bdf882dc Binary files /dev/null and b/doc/fig2.png differ diff --git a/doc/fig3.png b/doc/fig3.png new file mode 100644 index 000000000..e502f2dd3 Binary files /dev/null and b/doc/fig3.png differ diff --git a/doc/fig4.png b/doc/fig4.png new file mode 100644 index 000000000..667e41289 Binary files /dev/null and b/doc/fig4.png differ diff --git a/doc/scst_pg.sgml b/doc/scst_pg.sgml new file mode 100644 index 000000000..95d7e1fb7 --- /dev/null +++ b/doc/scst_pg.sgml @@ -0,0 +1,2278 @@ + + +
+ +Generic SCSI Target Middle Level for Linux + + + Vladislav Bolkhovitin < + + +Version 0.9.3-pre4 2006/02/07, actual for SCST 0.9.3-pre4 and later + + +This document describes SCSI target mid-level for Linux (SCST), its +architecture and drivers from the driver writer's point of view. + + + + +Introduction + +

+SCST is a SCSI target mid-level subsystem for Linux. It is designed to +provide unified, consistent interface between SCSI target drivers and +Linux kernel and simplify target drivers development as much as +possible. It has the following features: + + + + Very low overhead, fine-grained locks and simplest commands +processing path, which allow to reach maximum possible performance and +scalability that close to theoretical limit. + + Incoming requests can be processed in the caller's context or in +one of the internal SCST's tasklets, therefore no extra context switches +required. + + Complete SMP support. + + Undertakes most problems, related to execution contexts, thus +practically eliminating one of the most complicated problem in the +kernel drivers development. For example, a target driver for Qlogic +2200/2300 cards, which has all necessary features, is about 2000 +lines of code long, that is at least in several times less, than the +initiator one. + + Performs all required pre- and post- processing of incoming +requests and all necessary error recovery functionality. + + Emulates necessary functionality of SCSI host adapter, because +from a remote initiator's point of view SCST acts as a SCSI host with +its own devices. Some of the emulated functions are the following: + + + + Generation of necessary UNIT ATTENTIONs, their storage and + delivery to all connected remote initiators (sessions). + + RESERVE/RELEASE functionality. + + CA/ACA conditions. + + All types of RESETs and other task management functions. + + REPORT LUNS command as well as SCSI address space management + in order to have consistent address space on all remote initiators, + since local SCSI devices could not know about each other to report + via REPORT LUNS command. Additionally, SCST responds with error on + all commands to non-existing devices and provides access control + (not implemented yet), so different remote initiators could see + different set of devices. + + Other necessary functionality (task attributes, etc.) as + specified in SAM-2, SPC-2, SAM-3, SPC-3 and other SCSI standards. + + + + Device handlers architecture provides extra reliability and +security via verifying all incoming requests and allows to make any +additional requests processing, which is completely independent from +target drivers, for example, data caching or device dependent +exceptional conditions treatment. + + + +Interoperability between SCST and local SCSI initiators (like sd, st) is +the additional issue that SCST is going to address (it is not +implemented yet). It is necessary, because local SCSI initiators can +change the state of the device, for example RESERVE the device, or some +of its parameters and that would be done behind SCST, which could lead +to various problems. Thus, RESERVE/RELEASE commands, locally generated +UNIT ATTENTIONs, etc. should be intercepted and processed as if local +SCSI initiators act as remote SCSI initiators connected to SCST. This +feature requires some the kernel modification. Since in the current +version it is not implemented, SCST and the target drivers are able to +work with any unpatched 2.4 kernel version. + +Interface between SCST and the target drivers is based on work, done by +University of New Hampshire Interoperability Labs (UNH IOL). + +All described below data structures and function could be found in +. + +Terms and Definitions + +

SCST Architecture + +

+SCST accepts commands and passes them to SCSI mid-level at the same +way as SCSI high-level drivers (sg, sd, st) do. Figure 1 shows +interaction between SCST, its drivers and Linux SCSI subsystem. + +

+ + + + Interaction between SCST, its drivers and Linux SCSI subsystem. + +
+ +Target driver registration + +

+To work with SCST a target driver must register its template in SCST by +calling scst_register_target_template(). The template lets SCST know the +target driver's entry points. It is defined as the following: + +Structure scst_tgt_template + +

+ +struct scst_tgt_template +{ + int sg_tablesize; + const char name[15]; + + unsigned unchecked_isa_dma:1; + unsigned use_clustering:1; + + unsigned xmit_response_atomic:1; + unsigned rdy_to_xfer_atomic:1; + unsigned report_aen_atomic:1; + + int (* detect) (struct scst_tgt_template *tgt_template); + int (* release)(struct scst_tgt *tgt); + + int (* xmit_response)(struct scst_cmd *cmd); + int (* rdy_to_xfer)(struct scst_cmd *cmd); + + void (*on_free_cmd) (struct scst_cmd *cmd); + + void (* task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd); + void (* report_aen)(int mgmt_fn, const uint8_t *lun, int lun_len); + + int (*proc_info) (char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_tgt *tgt, int inout); +} + + +Where: + + + += 0 to signify +the number of detected target adapters. A negative value should be +returned whenever there is an error. Must be defined. + + + + + +If the driver needs to create additional files in its /proc +subdirectory, it can use + +Functions + + + +More about +As already written above, function + + + +If Target driver registration functions + +scst_register_target_template() + +

+Function +int scst_register_target_template( + struct scst_tgt_template *vtt) + + +Where: + + + + +Returns 0 on success or appropriate error code otherwise. + +scst_register() + +

+Function +struct scst_tgt *scst_register( + struct scst_tgt_template *vtt) + + +Where: + + + + +Returns target structure based on template vtt or NULL in case of error. + +Target driver unregistration + +

+In order to unregister itself target driver should at first call +scst_unregister() + +

+Function +void scst_unregister( + struct scst_tgt *tgt) + + +Where: + + + + +scst_unregister_target_template() + +

+Function +void scst_unregister_target_template( + struct scst_tgt_template *vtt) + + +Where: + + + + +SCST session registration + +

+When target driver determines that it needs to create new SCST session +(for example, by receiving new TCP connection), it should call + +struct scst_session *scst_register_session( + struct scst_tgt *tgt, + int atomic, + const char *initiator_name, + void *data, + void (*result_fn) ( + struct scst_session *sess, + void *data, + int result)); + + +Where: + + + + + + + + + +A session creation and initialization is a complex task, which requires +sleeping state, so it can't be fully done in interrupt context. +Therefore the "bottom half" of it, if + + + + Session registration when + + +

+ + + + Session registration when +
+ +SCST session unregistration + +

+SCST session unregistration basically is the same, except that instead of +atomic parameter there is +void scst_unregister_session( + struct scst_session *sess, + int wait, + void (* unreg_done_fn)( + struct scst_session *sess)) + + +Where: + + + + + + + + + +All outstanding commands will be finished regularly. After +The commands processing and interaction between SCST and its drivers + +

+The commands processing by SCST started when target driver calls + + +If the command required no data transfer, it will be passed to +SCSI mid-level directly or via device handler's If the command is If the command is + +When the command is finished by SCSI mid-level, device handler's + + + + + The commands processing flow + + + +Additionally, before calling + + Expected transfer length and direction via + + +The commands processing functions + +scst_rx_cmd() + +

+Function +struct scst_cmd *scst_rx_cmd( + struct scst_session *sess, + const uint8_t *lun, + int lun_len, + const uint8_t *cdb, + int cdb_len, + int atomic) + + +Where: + + + + + +scst_cmd_init_done() + +

+Function +void scst_cmd_init_done( + struct scst_cmd *cmd, + int pref_context) + + +Where: + + + + + +scst_rx_data() + +

+Function +void scst_rx_data( + struct scst_cmd *cmd, + int status, + int pref_context) + + +Where: + + + + + +Parameter + + + +scst_tgt_cmd_done() + +

+Function +void scst_tgt_cmd_done( + struct scst_cmd *cmd) + + +Where: + + + +The commands processing context + +

+Execution context often is a major problem in the kernel drivers +development, because many contexts, like IRQ one, greatly limit +available functionality, therefore require additional complex code in +order to pass processing to more simple context. SCST does its best to +undertake most of the context handling. + +On the initialization time SCST creates for internal command processing +as many threads as there are processors in the system or specified by +user via +Directly, i.e. in the caller's context, without limitations +Directly atomically, i.e. with sleeping forbidden +In the SCST's internal per processor or per session thread +In the SCST's per processor tasklet + + +The target driver sets this context as pref_context parameter for +Preferred context constants + +

+There are the following preferred context constants: + + + + + +Task management functions + +

+There are the following task management functions supported: + + + + + +scst_rx_mgmt_fn_tag() + +

+Function +int scst_rx_mgmt_fn_tag( + struct scst_session *sess, + int fn, + uint32_t tag, + int atomic, + void *tgt_specific) + + +Where: + + + + + +Returns 0 if the command was successfully created and scheduled for +execution, error code otherwise. On success, the completion status of +the command will be reported asynchronously via scst_rx_mgmt_fn_lun() + +

+Function +int scst_rx_mgmt_fn_lun( + struct scst_session *sess, + int fn, + const uint8_t *lun, + int lun_len, + int atomic, + void *tgt_specific); + + +Where: + + + + + +Returns 0 if the command was successfully created and scheduled for +execution, error code otherwise. On success, the completion status of +the command will be reported asynchronously via Device specific drivers (device handlers) + +

+Device specific drivers are plugins for SCST, which help SCST to analyze +incoming requests and determine parameters, specific to various types +of devices. Device handlers are intended for the following: + + + +To get data transfer length and direction directly from CDB and +current device's configuration exactly as an end-target SCSI device +does. This serves two purposes: + + + + Improves security and reliability by not trusting the data + supplied by remote initiator via SCSI low-level protocol. + + Some low-level SCSI protocols don't provide data transfer + length and direction, so that information can be get only + directly from CDB and current device's configuration. For + example, for tape devices to get data transfer size it might be + necessary to know block size setting. + + + +To process some exceptional conditions, like ILI on tape devices. + +To initialize incoming commands with some device-specific +parameters, like timeout value. + +To allow some additional device-specific commands pre-, post- +processing or alternative execution, like copying data from system +cache, and do that completely independently from target drivers. + + + +Device handlers performs very lightweight processing and therefore +should not considerably affect performance or CPU load. They are +considered to be part of SCST, so they could directly access any fields +in SCST's structures as well as use the corresponding functions. + +Without appropriate device handler SCST hides devices of this type from +remote initiators and returns Device specific driver registration + +scst_register_dev_driver() + +

+To work with SCST a device specific driver must register itself in SCST by +calling +int scst_register_dev_driver( + struct scst_dev_type *dev_type) + + +Where: + + + + +The function returns 0 on success or appropriate error code otherwise. + +Structure +Structure +struct scst_dev_type +{ + char name[15]; + int type; + + unsigned parse_atomic:1; + unsigned exec_atomic:1; + unsigned dev_done_atomic:1; + + int (*init) (struct scst_dev_type *dev_type); + void (*release) (struct scst_dev_type *dev_type); + + int (*attach) (struct scst_device *dev); + void (*detach) (struct scst_device *dev); + + int (*attach_tgt) (struct scst_tgt_device *tgt_dev); + void (*detach_tgt) (struct scst_tgt_device *tgt_dev); + + int (*parse) (struct scst_cmd *cmd); + int (*exec) (struct scst_cmd *cmd, + void (*scst_cmd_done)(struct scsi_cmnd *cmd, int next_state)); + int (*dev_done) (struct scst_cmd *cmd); + int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, + struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd_to_abort); + int (*on_free_cmd) (struct scst_cmd *cmd); + + int (*proc_info) (char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_dev_type *dev_type, + int inout) + + struct module *module; +} + + +Where: + + + +bufflen/ and data_direction/ (see below + + + + + + + + + + + + +If the driver needs to create additional files in its /proc +subdirectory, it can use + +Structure +struct scst_info_cdb +{ + enum scst_cdb_flags flags; + scst_data_direction direction; + unsigned int transfer_len; + unsigned short cdb_len; + const char *op_name; +} + + +Where: + + + + + + + + + +Field data_direction/, set by + + + +Device specific driver unregistration + +

+Device specific driver is unregistered by calling + +void scst_unregister_dev_driver( + struct scst_dev_type *dev_type) + + +Where: + + + + +SCST commands' states + +

+There are the following states, which a SCST command passes through +during execution and which could be returned by device handler's + + +tgt_dev/ +assignment) state + + + +SCST's structures manipulation functions + +

+Target drivers must not directly access any fields in SCST's structures, +they must use only described below functions. + +SCST target driver manipulation functions + +scst_tgt_get_tgt_specific() and scst_tgt_set_tgt_specific() + +

+Function +void *scst_tgt_get_tgt_specific( + struct scst_tgt *tgt) + + +Function +void scst_tgt_set_tgt_specific( + struct scst_tgt *tgt, + void *val) + + +Where: + + + + +SCST session manipulation functions + +scst_sess_get_tgt_specific() and scst_sess_set_tgt_specific() + +

+Function +void *scst_sess_get_tgt_specific( + struct scst_session *sess) + + +Function +void scst_sess_set_tgt_specific( + struct scst_session *sess, + void *val) + + +Where: + + + + +SCST command manipulation functions + +scst_cmd_atomic() + +

+Function +int scst_cmd_atomic( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_session() + +

+Function +struct scst_session *scst_cmd_get_session( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_resp_data_len() + +

+Function +unsigned int scst_cmd_get_resp_data_len( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_tgt_resp_flags() + +

+Function +int scst_cmd_get_tgt_resp_flags( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_buffer() + +

+Function +void *scst_cmd_get_buffer( + struct scst_cmd *cmd) + + +Where: + + + + +It is recommended to use scst_cmd_get_bufflen() + +

+Function +unsigned int scst_cmd_get_bufflen( + struct scst_cmd *cmd) + + +Where: + + + + +It is recommended to use scst_cmd_get_use_sg() + +

+Function +unsigned short scst_cmd_get_use_sg( + struct scst_cmd *cmd) + + +Where: + + + + +It is recommended to use scst_cmd_get_data_direction() + +

+Function +scst_data_direction scst_cmd_get_data_direction( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_status() + +

+Functions +uint8_t scst_cmd_get_status( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_masked_status() + +

+Functions +uint8_t scst_cmd_get_masked_status( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_msg_status() + +

+Functions +uint8_t scst_cmd_get_msg_status( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_host_status() + +

+Functions +uint16_t scst_cmd_get_host_status( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_driver_status() + +

+Functions +uint16_t scst_cmd_get_driver_status( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_sense_buffer() + +

+Functions +uint8_t *scst_cmd_get_sense_buffer( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_sense_buffer_len() + +

+Functions +int scst_cmd_get_sense_buffer_len( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_get_tag() and scst_cmd_set_tag() + +

Function +uint32_t scst_cmd_get_tag( + struct scst_cmd *cmd) + + +Function +void scst_cmd_set_tag( + struct scst_cmd *cmd, + uint32_t tag) + + +Where: + + + + +scst_cmd_get_tgt_specific() and scst_cmd_get_tgt_specific_lock() + +

+Functions +void *scst_cmd_get_tgt_specific( + struct scst_cmd *cmd) + + + +void *scst_cmd_get_tgt_specific_lock( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_set_tgt_specific() and scst_cmd_set_tgt_specific_lock() + +

+Functions +void *scst_cmd_set_tgt_specific( + struct scst_cmd *cmd, + void *val) + + + +void *scst_cmd_set_tgt_specific_lock( + struct scst_cmd *cmd, + void *val) + + +Where: + + + + +scst_cmd_get_data_buff_alloced() and scst_cmd_set_data_buff_alloced() + +

+Function +int scst_cmd_get_data_buff_alloced( + struct scst_cmd *cmd) + + +Function +void scst_cmd_set_data_buff_alloced( + struct scst_cmd *cmd) + + +Where: + + + + +scst_cmd_set_expected(), scst_cmd_is_expected_set(), +scst_cmd_get_expected_data_direction() and +scst_cmd_get_expected_transfer_len() + +

+Function +void scst_cmd_set_expected( + struct scst_cmd *cmd, + scst_data_direction expected_data_direction, + unsigned int expected_transfer_len) + + +Function +int scst_cmd_is_expected_set( + struct scst_cmd *cmd) + + +Function +scst_data_direction scst_cmd_get_expected_data_direction( + struct scst_cmd *cmd) + + +Function +unsigned int scst_cmd_get_expected_transfer_len( + struct scst_cmd *cmd) + + +Where: + + + + +scst_get_buf_first(), scst_get_buf_next(), +scst_put_buf() and scst_get_buf_count() + +

+These functions are designed to simplify and unify access to the +commands data (SG vector or plain data buffer) in all possible +conditions, including HIGHMEM environment, and should be used instead of +direct access. + +Function +int scst_get_buf_first( + struct scst_cmd *cmd, + uint8_t **buf) + + +Where: + + + + +Returns the length of the chunk of data for success, 0 for the end of +data, negative error code otherwise. + +Function +int scst_get_buf_next( + struct scst_cmd *cmd, + uint8_t **buf) + + +Where: + + + + +Returns the length of the chunk of data for success, 0 for the end of +data, negative error code otherwise. + +Function +void scst_put_buf( + struct scst_cmd *cmd, + uint8_t *buf) + + +Where: + + + + +Function +int scst_get_buf_count( + struct scst_cmd *cmd) + + +Where: + + + + +SCST task management commands manipulation functions + +scst_mgmt_cmd_get_tgt_specific() + +

+Function +void *scst_mgmt_cmd_get_tgt_specific( + struct scst_mgmt_cmd *mcmd) + + +Where: + + + + +scst_mgmt_cmd_get_status() + +

+Functions +void *scst_mgmt_cmd_get_status( + struct scst_mgmt_cmd *mcmd) + + +Where: + + + + +The following status values are possible: + + + + SCST_MGMT_STATUS_SUCCESS - the task management command completed +successfully + + SCST_MGMT_STATUS_FAILED - the task management command failed. + + + +Miscellaneous functions + +scst_find_cmd_by_tag() + +

+Function +struct scst_cmd *scst_find_cmd_by_tag( + struct scst_session *sess, + uint32_t tag) + + +Where: + + + + +Returns found command or NULL otherwise. + +scst_find_cmd() + +

+Function +struct scst_cmd *scst_find_cmd( + struct scst_session *sess, + void *data, + int (*cmp_fn)(struct scst_cmd *cmd, void *data)) + + +Where: + + + + + + + + +Returns found command or NULL otherwise. + +scst_get_cdb_info() + +

+Function +int scst_get_cdb_info( + const uint8_t *cdb_p, + int dev_type, + struct scst_info_cdb *info_p) + + +Where: + + + + + +Returns 0 on success, -1 otherwise. + +scst_to_dma_dir() + +

+Function +int scst_to_dma_dir( + int scst_dir) + + +Where: + + + + +Returns the corresponding scst_is_cmd_local() + +

+Function +int scst_is_cmd_local( + struct scst_cmd *cmd) + + +Where: + + + + +Returns 1, if the command's CDB is locally handled by SCST or 0 +otherwise + +scst_register_virtual_device() and scst_unregister_virtual_device() + +

+These functions provide a way for device handlers to register a virtual +(emulated) device, which will be visible only by remote initiators. For +example, FILEIO device handler uses files on file system to makes from +them virtual remotely available SCSI disks. + +Function +int scst_register_virtual_device( + struct scst_dev_type *dev_handler) + + +Where: + + + + +Returns assigned to the device ID on success, or negative value otherwise. + +Function +void scst_unregister_virtual_device( + int id) + + +Where: + + + + +scst_add_threads() and scst_del_threads() + +

+These functions allows to add or delete some SCST threads. For example, +if +int scst_add_threads( + int num) + + +Where: + + + + +Returns 0 on success, error code otherwise. + +Function +void scst_del_threads( + int num) + + +Where: + + + + +scst_proc_get_tgt_root() + +

+Function +struct proc_dir_entry *scst_proc_get_tgt_root( + struct scst_tgt_template *vtt) + + +Where: + + + + +Returns proc_dir_entry on success, NULL otherwise. + +scst_proc_get_dev_type_root() + +

+Function +struct proc_dir_entry *scst_proc_get_dev_type_root( + struct scst_dev_type *dtt) + + +Where: + + + + +Returns proc_dir_entry on success, NULL otherwise. + +