mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-17 10:41:26 +00:00
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1122 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2209 lines
70 KiB
HTML
2209 lines
70 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
|
<HTML>
|
|
<HEAD>
|
|
<META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.21">
|
|
<TITLE>Generic SCSI Target Middle Level for Linux</TITLE>
|
|
</HEAD>
|
|
<BODY>
|
|
<H1>Generic SCSI Target Middle Level for Linux</H1>
|
|
|
|
<H2>Vladislav Bolkhovitin</H2>Version 0.9.5 2006/12/01, actual for SCST 0.9.5 and later
|
|
<HR>
|
|
<EM>This document describes SCSI target mid-level for Linux (SCST), its
|
|
architecture and drivers from the driver writer's point of view. </EM>
|
|
<HR>
|
|
<H2><A NAME="s1">1. Introduction</A></H2>
|
|
|
|
<P>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:</P>
|
|
<P>
|
|
<UL>
|
|
<LI> 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.
|
|
</LI>
|
|
<LI> 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.
|
|
</LI>
|
|
<LI> Complete SMP support.
|
|
</LI>
|
|
<LI> Undertakes most problems, related to execution contexts, thus
|
|
practically eliminating one of the most complicated problem in the
|
|
kernel drivers development. For example, target drivers for Marvell SAS
|
|
adapters or for InfiniBand SRP are less 3000 lines of code long.
|
|
</LI>
|
|
<LI> Performs all required pre- and post- processing of incoming
|
|
requests and all necessary error recovery functionality.
|
|
</LI>
|
|
<LI> 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:
|
|
|
|
<UL>
|
|
<LI> Generation of necessary UNIT ATTENTIONs, their storage and
|
|
delivery to all connected remote initiators (sessions).
|
|
</LI>
|
|
<LI> RESERVE/RELEASE functionality.
|
|
</LI>
|
|
<LI> CA/ACA conditions.
|
|
</LI>
|
|
<LI> All types of RESETs and other task management functions.
|
|
</LI>
|
|
<LI> 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.
|
|
</LI>
|
|
<LI> Other necessary functionality (task attributes, etc.) as
|
|
specified in SAM-2, SPC-2, SAM-3, SPC-3 and other SCSI standards.
|
|
</LI>
|
|
</UL>
|
|
|
|
</LI>
|
|
<LI> 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.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>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.</P>
|
|
<P>Interface between SCST and the target drivers is based on work, done by
|
|
University of New Hampshire Interoperability Labs (UNH IOL).</P>
|
|
<P>All described below data structures and function could be found in
|
|
<B>scst.h</B>. The SCST's Internet page is
|
|
<A HREF="http://scst.sourceforge.net">http://scst.sourceforge.net</A>.</P>
|
|
|
|
<H2><A NAME="s2">2. Terms and Definitions</A></H2>
|
|
|
|
<P><B>SCSI initiator device</B></P>
|
|
<P>A SCSI device that originates service and task management requests to be
|
|
processed by a SCSI target device and receives device service and task
|
|
management responses from SCSI target devices.</P>
|
|
<P>Think of the 'SCSI LLDD' as a BE (Back End) driver.</P>
|
|
<P><B>SCSI target device</B></P>
|
|
<P>A SCSI device that receives device service and task management requests
|
|
for processing and sends device service and task management responses
|
|
to SCSI initiator devices or drivers. </P>
|
|
<P>Think of the 'Target Driver' as an FE (Front End) driver.</P>
|
|
<P>The FE driver interfaces to the initiators (via the
|
|
storage-fabric-cloud) and also to the upper edge of the SCST. Whereas
|
|
the BE driver interfaces to the targets, i.e. disk-enclosures/JBODs/tapes etc.
|
|
and also to the bottom edge of the SCST.</P>
|
|
<P><B>SCST session</B></P>
|
|
<P>SCST session is the object that describes relationship between a remote
|
|
initiator and SCST via a target driver. All the commands from the remote
|
|
initiator is passed to SCST in the session. For example, for connection
|
|
oriented protocols, like iSCSI, SCST session could be mapped to the TCP
|
|
connection (as well as iSCSI session). SCST session is the close
|
|
equivalent of I_T nexus object.</P>
|
|
<P><B>Local SCSI initiator</B></P>
|
|
<P>A SCSI initiator that is located on the same host as SCST subsystem.
|
|
Examples are sg and st drivers.</P>
|
|
<P><B>Remote SCSI initiator</B></P>
|
|
<P>A SCSI initiator that is located on the remote host for SCST subsystem
|
|
and makes client connections to SCST via SCSI target drivers.</P>
|
|
<P><B>SCSI target driver</B></P>
|
|
<P>A Linux hardware or logical driver that acts as a SCSI target for remote
|
|
SCSI initiators, i.e. accepts remote connections, passes incoming SCSI
|
|
requests to SCST and sends SCSI responses from SCST back to their
|
|
originators.</P>
|
|
<P><B>Device handler driver</B></P>
|
|
<P>Also known as "device type specific driver" or "dev handler", is plugin
|
|
for SCST, which helps SCST to analyze incoming requests and determine
|
|
parameters, specific to various types of devices as well as perform some
|
|
processing. See appropriate section for details.</P>
|
|
|
|
<H2><A NAME="s3">3. SCST Architecture</A></H2>
|
|
|
|
<P>
|
|
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.</P>
|
|
<P>
|
|
<FIGURE>
|
|
<EPS FILE="fig1.png">
|
|
<IMG SRC="fig1.png">
|
|
<CAPTION> <BR> Interaction between SCST, its drivers and Linux SCSI subsystem.</CAPTION>
|
|
</FIGURE>
|
|
</P>
|
|
|
|
<H2><A NAME="s4">4. Target driver registration</A></H2>
|
|
|
|
<P>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:</P>
|
|
|
|
<H2><A NAME="ss4.1">4.1 Structure scst_tgt_template</A>
|
|
</H2>
|
|
|
|
<P>
|
|
<PRE>
|
|
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);
|
|
}
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>sg_tablesize</B> - allows checking whether scatter/gather can be
|
|
used or not and, if yes, sets the maximum supported count of
|
|
scatter/gather entries
|
|
</LI>
|
|
<LI><B>name</B> - the name of the template. Must be unique to identify
|
|
the template. Must be defined.
|
|
</LI>
|
|
<LI><B>unchecked_isa_dma</B> - true, if this target adapter uses
|
|
unchecked DMA onto an ISA bus.
|
|
</LI>
|
|
<LI><B>use_clustering</B> - true, if this target adapter wants to use
|
|
clustering (i.e. smaller number of segments).
|
|
</LI>
|
|
<LI><B>xmit_response_atomic</B>, <B>rdy_to_xfer_atomic</B> - true, if the
|
|
corresponding function supports execution in the atomic (non-sleeping)
|
|
context.
|
|
</LI>
|
|
<LI><B>int (* detect) (struct scst_tgt_template *tgt_template)</B> - this
|
|
function is intended to detect the target adapters that are present in
|
|
the system. Each found adapter should be registered by calling
|
|
<B>scst_register()</B>. The function should return a value >= 0 to signify
|
|
the number of detected target adapters. A negative value should be
|
|
returned whenever there is an error. Must be defined.
|
|
</LI>
|
|
<LI><B>int (* release)(struct scst_tgt *tgt)</B> - this function is
|
|
intended to free up the resources allocated to the device. The function
|
|
should return 0 to indicate successful release or a negative value if
|
|
there are some issues with the release. In the current version of SCST
|
|
the return value is ignored. Must be defined.
|
|
</LI>
|
|
<LI><B>int (* xmit_response)(struct scst_cmd *cmd)</B> - this
|
|
function is equivalent to the SCSI queuecommand(). The target should
|
|
transmit the response data and the status in the struct scst_cmd. See
|
|
below for details. Must be defined.
|
|
</LI>
|
|
<LI><B>int (* rdy_to_xfer)(struct scst_cmd *cmd)</B> - this function
|
|
informs the driver that data buffer corresponding to the said command
|
|
have now been allocated and it is OK to receive data for this command.
|
|
This function is necessary because a SCSI target does not have any
|
|
control over the commands it receives. Most lower-level protocols have a
|
|
corresponding function which informs the initiator that buffers have
|
|
been allocated e.g., XFER_RDY in Fibre Channel. After the data is
|
|
actually received the low-level driver should call <B>scst_rx_data()</B>
|
|
in order to continue processing this command. Returns one of the
|
|
<B>SCST_TGT_RES_*</B> constants, described below. Pay attention to
|
|
"atomic" attribute of the command, which can be get via
|
|
<B>scst_cmd_atomic()</B>: it is true if the function called in the atomic
|
|
(non-sleeping) context. Must be defined.
|
|
</LI>
|
|
<LI><B>void (*on_free_cmd)(struct scst_cmd *cmd)</B> - this function
|
|
called to notify the driver that the command is about to be freed.
|
|
Necessary, because for aborted commands <B>xmit_response()</B> could not be
|
|
called. Could be used on IRQ context. Must be defined.
|
|
</LI>
|
|
<LI><B>void (* task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd)</B> -
|
|
this function informs the driver that a received task management
|
|
function has been completed. Completion status could be get via
|
|
<B>scst_mgmt_cmd_get_status()</B>. No return value expected. Must be
|
|
defined, if the target supports task management functionality.
|
|
</LI>
|
|
<LI><B>int (* report_aen)(int mgmt_fn, const uint8_t *lun, int
|
|
lun_len)</B> - this function is used for Asynchronous Event Notification.
|
|
It is the responsibility of the driver to notify any/all initiators
|
|
about the Asynchronous Event reported. Returns one of the
|
|
<B>SCST_TGT_RES_*</B> constants, described below. Must be defined, if
|
|
low-level protocol supports AEN. This feature is not implemented yet.
|
|
</LI>
|
|
<LI><B>int (*proc_info) (char *buffer, char **start, off_t offset,
|
|
int length, int *eof, struct scst_tgt *tgt, int inout)</B> - this function
|
|
can be used to export the driver's statistics and other information to
|
|
the world outside the kernel. Parameters:
|
|
|
|
<OL>
|
|
<LI> <B>buffer, start, offset, length, eof</B> - have the same
|
|
meaning as for <B>read_proc_t</B> function of the kernel
|
|
</LI>
|
|
<LI> <B>tgt</B> - pointer to the target, for which the function
|
|
is called
|
|
</LI>
|
|
<LI> <B>inout</B> - read/write direction flag, 0 - for reads, other
|
|
value - for writes
|
|
</LI>
|
|
</OL>
|
|
|
|
|
|
If the driver needs to create additional files in its /proc
|
|
subdirectory, it can use <B>scst_proc_get_tgt_root()</B> function to get
|
|
the root proc_dir_entry.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Functions <B>xmit_response()</B>, <B>rdy_to_xfer()</B> are expected to be
|
|
non-blocking, i.e. return immediately and don't wait for actual data
|
|
transfer to finish. Blocking in such command could negatively impact on
|
|
overall system performance. If blocking is necessary, it is worth to
|
|
consider creating dedicated thread(s) in target driver, to which the
|
|
commands would be passed and which would perform blocking operations
|
|
instead of SCST. If the function allowed to sleep or not is defined by
|
|
"atomic" attribute of the cmd that can be get via
|
|
<B>scst_cmd_atomic()</B>, which is true, if sleeping is not allowed. In
|
|
this case, if the function requires sleeping, it can return
|
|
<B>SCST_TGT_RES_NEED_THREAD_CTX</B> in order to be recalled in the thread
|
|
context, where sleeping is allowed.</P>
|
|
<P>Functions <B>task_mgmt_fn_done()</B> and <B>report_aen()</B> are recommended
|
|
to be non-blocking as well. Blocking there will stop all management
|
|
processing for all target drivers in the system (there is only one
|
|
management thread in the system).</P>
|
|
<P>Functions <B>xmit_response()</B>, <B>rdy_to_xfer()</B> and <B>report_aen()</B>
|
|
can return the following error codes:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>SCST_TGT_RES_SUCCESS</B> - success.
|
|
</LI>
|
|
<LI><B>SCST_TGT_RES_QUEUE_FULL</B> - internal device queue is full, retry
|
|
again later.
|
|
</LI>
|
|
<LI><B>SCST_TGT_RES_NEED_THREAD_CTX</B> - it is impossible to complete
|
|
requested task in atomic context. The command should be restarted in the
|
|
thread context as described above.
|
|
</LI>
|
|
<LI><B>SCST_TGT_RES_FATAL_ERROR</B> - fatal error, i.e. it is unable to
|
|
perform requested operation. If returned by <B>xmit_response()</B> the
|
|
command will be destroyed, if by <B>rdy_to_xfer()</B>,
|
|
<B>xmit_response()</B> will be called with <B>HARDWARE ERROR</B> sense data.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>More about <B>xmit_response()</B></H3>
|
|
|
|
<P>
|
|
As already written above, function <B>xmit_response()</B> should transmit
|
|
the response data and the status from the cmd parameter. Either it
|
|
should transmit the data or the status is defined by bits of the value,
|
|
returned by <B>scst_cmd_get_tgt_resp_flags()</B>. They are:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>SCST_TSC_FLAG_DATA</B> - set if there are data to be sent
|
|
</LI>
|
|
<LI><B>SCST_TSC_FLAG_STATUS</B> - set if the command is finished and
|
|
there is status/sense to be sent
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>If <B>SCST_TSC_FLAG_DATA</B> is set, the data contained in the buffer,
|
|
returned by <B>scst_cmd_get_buffer()</B> (pay attention to
|
|
<B>scst_cmd_get_use_sg()</B> for scatter/gather) with length, returned by
|
|
<B>scst_cmd_get_resp_data_len()</B>. It is recommended to use
|
|
<B>scst_get_buf_*()</B>scst_put_buf()/ family of function instead of
|
|
direct access to the data buffers, because they hide all HIGHMEM and
|
|
SG/plain buffer issues.</P>
|
|
<P>If <B>SCST_TSC_FLAG_STATUS</B> is set the status could be received by the
|
|
appropriate <B>scst_cmd_get_*_status()</B> functions (see below).</P>
|
|
<P>The sense, if any, is contained in the buffer, returned by
|
|
<B>scst_cmd_get_sense_buffer()</B>, with length, returned by
|
|
<B>scst_cmd_get_sense_buffer_len()</B>. SCST always works in
|
|
<B>autosense</B> mode. If a low-level SCSI driver/device doesn't support
|
|
autosense mode, SCST will issue REQUEST SENSE command, if necessary.
|
|
Thus, if CHECK CONDITION established, target driver will always see
|
|
sense in the sense buffer and isn't required to request the sense
|
|
manually.</P>
|
|
<P>It is possible, that <B>SCST_TSC_FLAG_DATA</B> is set, but
|
|
<B>SCST_TSC_FLAG_STATUS</B> is not set. In this case the driver should
|
|
only transmit the data, but not finish the command and transmit the
|
|
status. Function <B>xmit_response()</B> will be called again either to
|
|
transmit the status or data once more.</P>
|
|
<P>After the response is completely sent, the target should call
|
|
<B>scst_tgt_cmd_done()</B> function in order to allow SCST to free the
|
|
command. </P>
|
|
<P>Function <B>xmit_response()</B> returns one of the <B>SCST_TGT_RES_*</B>
|
|
constants, described above. Pay attention to "atomic" attribute of the
|
|
cmd, which can be get via <B>scst_cmd_atomic()</B>: it is true if the
|
|
function called in the atomic (non-sleeping) context.</P>
|
|
|
|
<H2><A NAME="ss4.2">4.2 Target driver registration functions</A>
|
|
</H2>
|
|
|
|
<H3>scst_register_target_template()</H3>
|
|
|
|
<P>Function <B>scst_register_target_template()</B> is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_register_target_template(
|
|
struct scst_tgt_template *vtt)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>vtt</B> - pointer to the target driver template</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns 0 on success or appropriate error code otherwise.</P>
|
|
|
|
<H3>scst_register()</H3>
|
|
|
|
<P>Function <B>scst_register()</B> is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
struct scst_tgt *scst_register(
|
|
struct scst_tgt_template *vtt)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>vtt</B> - pointer to the target driver template</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns target structure based on template vtt or NULL in case of error.</P>
|
|
|
|
<H2><A NAME="s5">5. Target driver unregistration</A></H2>
|
|
|
|
<P>In order to unregister itself target driver should at first call
|
|
<B>scst_unregister()</B> for all its adapters and then call
|
|
<B>scst_unregister_target_template()</B> for its template.</P>
|
|
|
|
<H2><A NAME="ss5.1">5.1 scst_unregister()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_unregister()</B> is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_unregister(
|
|
struct scst_tgt *tgt)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>tgt</B> - pointer to the target driver structure</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss5.2">5.2 scst_unregister_target_template()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_unregister_target_template()</B> is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_unregister_target_template(
|
|
struct scst_tgt_template *vtt)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>vtt</B> - pointer to the target driver template</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="s6">6. SCST session registration</A></H2>
|
|
|
|
<P>When target driver determines that it needs to create new SCST session
|
|
(for example, by receiving new TCP connection), it should call
|
|
<B>scst_register_session()</B>, that is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
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));
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>tgt</B> - target
|
|
</LI>
|
|
<LI><B>atomic</B> - true, if the function called in the atomic context
|
|
</LI>
|
|
<LI><B>initiator_name</B> - remote initiator's name, any NULL-terminated
|
|
string, e.g. iSCSI name, which used as the key to found appropriate
|
|
access control group. Could be NULL, then "default" group is used. The
|
|
groups are set up via /proc interface.
|
|
</LI>
|
|
<LI><B>data</B> - data that will be used as the second
|
|
parameter for <B>bf</B>result_fn/()/ function
|
|
</LI>
|
|
<LI><B>result_fn</B> - pointer to the function that will be
|
|
asynchronously called when session initialization finishes. Can be NULL.
|
|
Parameters:
|
|
|
|
<UL>
|
|
<LI><B>sess</B> - session
|
|
</LI>
|
|
<LI><B>data</B> - target driver supplied to
|
|
<B>scst_register_session()</B> data
|
|
</LI>
|
|
<LI><B>result</B> - session initialization result, 0 on success or
|
|
appropriate error code otherwise
|
|
</LI>
|
|
</UL>
|
|
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>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 <B>scst_register_session()</B> is
|
|
called from atomic context, will be done in SCST thread context. In this
|
|
case <B>scst_register_session()</B> will return not completely initialized
|
|
session, but the target driver can supply commands to this session via
|
|
<B>scst_rx_cmd()</B>. Those commands processing will be delayed inside
|
|
SCST until the session initialization is finished, then their processing
|
|
will be restarted. The target driver will be notified about finish of
|
|
the session initialization by function <B>result_fn()</B>. On success the
|
|
target driver could do nothing, but if the initialization fails, the
|
|
target driver must ensure that no more new commands being sent or will
|
|
be sent to SCST after <B>result_fn()</B> returns. All already sent to SCST
|
|
commands for failed session will be returned in <B>xmit_response()</B>
|
|
with BUSY status. In case of failure the driver shall call
|
|
<B>scst_unregister_session()</B> inside <B>result_fn()</B>, it will NOT be
|
|
called automatically. Thus, <B>scst_register_session()</B> can be called
|
|
even on IRQ context. </P>
|
|
<P>Session registration is illustrated on Figure 2 and Figure 3.</P>
|
|
<P>
|
|
<FIGURE>
|
|
<EPS FILE="fig2.png">
|
|
<IMG SRC="fig2.png">
|
|
<CAPTION> <BR> Session registration when <B>atomic</B> parameter is false</CAPTION>
|
|
</FIGURE>
|
|
</P>
|
|
<P>
|
|
<FIGURE>
|
|
<EPS FILE="fig3.png">
|
|
<IMG SRC="fig3.png">
|
|
<CAPTION> <BR> Session registration when <B>atomic</B> parameter is true </CAPTION>
|
|
</FIGURE>
|
|
</P>
|
|
|
|
<H2><A NAME="s7">7. SCST session unregistration</A></H2>
|
|
|
|
<P>SCST session unregistration basically is the same, except that instead of
|
|
atomic parameter there is <B>wait</B> one.</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_unregister_session(
|
|
struct scst_session *sess,
|
|
int wait,
|
|
void (* unreg_done_fn)(
|
|
struct scst_session *sess))
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>sess</B> - session to be unregistered
|
|
</LI>
|
|
<LI><B>wait</B> - if true, instructs to wait until all commands, which
|
|
currently executing and belonged to the session, finished. Otherwise,
|
|
target driver should be prepared to receive <B>xmit_response()</B> for
|
|
the session after <B>scst_unregister_session()</B> returns.
|
|
</LI>
|
|
<LI><B>unreg_done_fn</B> - pointer to the function that will be
|
|
asynchronously called when the last session's command finishes and the
|
|
session is about to be completely freed. Can be NULL. Parameter:
|
|
|
|
<UL>
|
|
<LI><B>sess</B> - session
|
|
</LI>
|
|
</UL>
|
|
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>All outstanding commands will be finished regularly. After
|
|
<B>scst_unregister_session()</B> returned no new commands must be sent to
|
|
SCST via <B>scst_rx_cmd()</B>. Also, the caller must ensure that no
|
|
<B>scst_rx_cmd()</B> or <B>scst_rx_mgmt_fn_*()</B> is called in parallel
|
|
with <B>scst_unregister_session()</B>. </P>
|
|
<P>Function <B>scst_unregister_session()</B> can be called before
|
|
<B>result_fn()</B> of <B>scst_register_session()</B> called, i.e. during the
|
|
session registration/initialization.</P>
|
|
|
|
<H2><A NAME="s8">8. The commands processing and interaction between SCST and its drivers</A></H2>
|
|
|
|
<P>The commands processing by SCST started when target driver calls
|
|
<B>scst_rx_cmd()</B>. This function returns SCST's command. Then the target
|
|
driver finishes the command's initialization, if necessary, for
|
|
example, storing necessary target driver specific data there, and calls
|
|
<B>scst_cmd_init_done()</B> telling SCST that it can start the processing.
|
|
Then SCST translates the command's LUN to local device, determines the
|
|
command's data direction and required data buffer size by calling
|
|
appropriate device handler's <B>parse()</B> function. Then:</P>
|
|
<P>
|
|
<UL>
|
|
<LI>If the command required no data transfer, it will be passed to
|
|
SCSI mid-level directly or via device handler's <B>exec()</B> call.
|
|
</LI>
|
|
<LI>If the command is a <B>READ</B> command (data to the remote/local initiator),
|
|
necessary space will be allocated and then the command will be passed
|
|
to SCSI mid-level directly or via device handler's <B>exec()</B> call.
|
|
</LI>
|
|
<LI>If the command is a <B>WRITE</B> command (data from the remote/local initiator),
|
|
necessary space will be allocated, then the target's <B>rdy_to_xfer()</B>
|
|
function will be called, telling the target that the space is ready and
|
|
it can start data transferring. When all the data are read from the
|
|
target, it will call <B>scst_rx_data()</B>, and the command will be passed
|
|
to SCSI mid-level directly or via device handler's <B>exec()</B> call.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>When the command is finished by SCSI mid-level, device handler's
|
|
<B>dev_done()</B> is called to notify it about the command's
|
|
completion. Then in order to send the response the target's
|
|
<B>xmit_response()</B> is called. When the response, including data, if
|
|
any, is transmitted, the target will call <B>scst_tgt_cmd_done()</B>
|
|
telling SCST that it can free the command and its data buffer.</P>
|
|
<P>Then during the command's deallocation device handler's and the target's
|
|
<B>on_free_cmd()</B> will be called in this order, if set.</P>
|
|
<P>This sequence is illustrated on Figure 4. To simplify the picture, sign
|
|
"..." means SCST's waiting state for the corresponding command to
|
|
complete. During this state SCST and its drivers continue processing of
|
|
other commands, if there are any. One way arrow, for example to
|
|
<B>xmit_response()</B>, means that after this function returns, nothing
|
|
valuable for the current command will be done and SCST goes sleeping or
|
|
to the next command processing until corresponding event happens.</P>
|
|
<P>
|
|
<FIGURE>
|
|
<EPS FILE="fig4.png">
|
|
<IMG SRC="fig4.png">
|
|
<CAPTION> <BR> The commands processing flow</CAPTION>
|
|
</FIGURE>
|
|
</P>
|
|
<P>Additionally, before calling <B>scst_cmd_init_done()</B> the target driver can
|
|
set the following the command's flags or parameters:</P>
|
|
<P>
|
|
<UL>
|
|
<LI> <B>DATA_BUF_ALLOCED</B> - set if the data buffer is already
|
|
allocated. The flag is set via <B>scst_cmd_set_data_buff_alloced()</B> and
|
|
get via <B>scst_cmd_get_data_buff_alloced()</B>. Useful, for instance, for
|
|
iSCSI unsolicited data.
|
|
</LI>
|
|
<LI> Expected transfer length and direction via
|
|
<B>scst_cmd_set_expected()</B> as supplied by remote initiator, if any.
|
|
This values will be used only if the command's opcode is unknown for
|
|
SCST, for example for vendor-specific commands. If these values not set
|
|
and opcode isn't known, the command will be completed by SCST in
|
|
preprocessing phase with <B>INVALID OPCODE</B> sense.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss8.1">8.1 The commands processing functions</A>
|
|
</H2>
|
|
|
|
<H3>scst_rx_cmd()</H3>
|
|
|
|
<P>Function <B>scst_rx_cmd()</B> creates and sends new command to SCST. Returns
|
|
the command on success or NULL otherwise. It is defined as the
|
|
following:</P>
|
|
<P>
|
|
<PRE>
|
|
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)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>sess</B> - SCST's session
|
|
</LI>
|
|
<LI><B>lun</B> - pointer to device's LUN as specified in SCSI
|
|
Architecture Model 2/3 without any byte order translation. Extended
|
|
addressing method is not supported.
|
|
</LI>
|
|
<LI><B>lun_len</B> - LUN's length
|
|
</LI>
|
|
<LI><B>cdb</B> - SCSI CDB
|
|
</LI>
|
|
<LI><B>cdb_len</B> - CDB's length
|
|
</LI>
|
|
<LI><B>atomic</B> - if true, the command will be allocated with
|
|
GFP_ATOMIC flag, otherwise GFP_KERNEL will be used
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_init_done()</H3>
|
|
|
|
<P>Function <B>scst_cmd_init_done()</B> notifies SCST that the driver finished
|
|
its part of the command initialization, and the command is ready for
|
|
execution. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_cmd_init_done(
|
|
struct scst_cmd *cmd,
|
|
int pref_context)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - the command
|
|
</LI>
|
|
<LI><B>pref_context</B> - preferred command execution context. See
|
|
<B>SCST_CONTEXT_*</B> constants below for details.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_rx_data()</H3>
|
|
|
|
<P>Function <B>scst_rx_data()</B> notifies SCST that the driver received all
|
|
the necessary data and the command is ready for further processing. It
|
|
is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_rx_data(
|
|
struct scst_cmd *cmd,
|
|
int status,
|
|
int pref_context)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - the command
|
|
</LI>
|
|
<LI><B>status</B> - completion status, see below.
|
|
</LI>
|
|
<LI><B>pref_context</B> - preferred command execution context. See
|
|
<B>SCST_CONTEXT_*</B> constants below for details.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Parameter <B>status</B> can have one of the following values:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>SCST_RX_STATUS_SUCCESS</B> - success
|
|
</LI>
|
|
<LI><B>SCST_RX_STATUS_ERROR</B> - data receiving finished with error, so
|
|
SCST should set the sense and finish the command by calling
|
|
<B>xmit_response()</B>
|
|
</LI>
|
|
<LI><B>SCST_RX_STATUS_ERROR_SENSE_SET</B> - data receiving finished with
|
|
error and the sense is set, so SCST should finish the command by calling
|
|
<B>xmit_response()</B>
|
|
</LI>
|
|
<LI><B>SCST_RX_STATUS_ERROR_FATAL</B> - data receiving finished with
|
|
fatal error, so SCST should finish the command, but don't call
|
|
<B>xmit_response()</B>. In this case the driver must free all associated
|
|
with the command data before calling <B>scst_rx_data()</B>.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_tgt_cmd_done()</H3>
|
|
|
|
<P>Function <B>scst_tgt_cmd_done()</B> notifies SCST that the driver sent the
|
|
data and/or response. It must not been called if there are an error and
|
|
<B>xmit_response()</B> returned something other, than
|
|
<B>SCST_TGT_RES_SUCCESS</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_tgt_cmd_done(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:
|
|
<UL>
|
|
<LI><B>cmd</B> - the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss8.2">8.2 The commands processing context</A>
|
|
</H2>
|
|
|
|
<P>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. </P>
|
|
<P>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 <B>scst_threads</B> module parameter. Similarly, as many tasklets
|
|
created as there are processors in the system.</P>
|
|
<P>Each command can be processed in one of four contexts:</P>
|
|
<P>
|
|
<OL>
|
|
<LI>Directly, i.e. in the caller's context, without limitations</LI>
|
|
<LI>Directly atomically, i.e. with sleeping forbidden</LI>
|
|
<LI>In the SCST's internal per processor or per session thread</LI>
|
|
<LI>In the SCST's per processor tasklet</LI>
|
|
</OL>
|
|
</P>
|
|
<P>The target driver sets this context as pref_context parameter for
|
|
<B>scst_cmd_init_done()</B> and <B>scst_rx_data()</B>. Additionally, target's
|
|
template's <B>xmit_response_atomic</B> and <B>rdy_to_xfer_atomic</B> flags
|
|
have direct influence on the context. If one of them is false, the
|
|
corresponding function will never be called in the atomic context and,
|
|
if necessary, the command will be rescheduled to one of the SCST's
|
|
threads.</P>
|
|
<P>SCST in some circumstances can change preferred context to less
|
|
restrictive one, for example, for large data buffer allocation, if
|
|
there is not enough GFP_ATOMIC memory. </P>
|
|
|
|
<H3>Preferred context constants</H3>
|
|
|
|
<P>There are the following preferred context constants: </P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>SCST_CONTEXT_DIRECT</B> - sets direct command processing (i.e.
|
|
regular function calls in the current context) sleeping is allowed, no
|
|
context restrictions. Supposed to be used when calling from thread
|
|
context where no locks are held and the driver's architecture allows
|
|
sleeping without performance degradation or anything like that.
|
|
</LI>
|
|
<LI><B>SCST_CONTEXT_DIRECT_ATOMIC</B> - sets direct command processing
|
|
(i.e. regular function calls in the current context), sleeping is not
|
|
allowed. Supposed to be used when calling on thread context where there
|
|
are locks held, when calling on softirq context or the driver's
|
|
architecture does not allow sleeping without performance degradation or
|
|
anything like that.
|
|
</LI>
|
|
<LI><B>SCST_CONTEXT_TASKLET</B> - tasklet or thread context required for
|
|
the command processing. Supposed to be used when calling from IRQ
|
|
context.
|
|
</LI>
|
|
<LI><B>SCST_CONTEXT_THREAD</B> - thread context required for the
|
|
command processing. Supposed to be used if the driver's architecture
|
|
does not allow using any of above.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="s9">9. Task management functions</A></H2>
|
|
|
|
<P>There are the following task management functions supported:</P>
|
|
<P>
|
|
<UL>
|
|
<LI> <B>SCST_ABORT_TASK</B> - <B>ABORT_TASK</B> task management function,
|
|
aborts the specified task (command). Returns completion status via
|
|
<B>task_mgmt_fn_done()</B> when the command (task) is actually aborted.
|
|
</LI>
|
|
<LI> <B>SCST_ABORT_TASK_SET</B> - <B>ABORT_TASK_SET</B> task management
|
|
function, aborts all tasks (commands) on the specified device. Returns
|
|
the success via <B>task_mgmt_fn_done()</B> immediately, not waiting for
|
|
the commands being actually aborted.
|
|
</LI>
|
|
<LI> <B>SCST_CLEAR_ACA</B> - <B>CLEAR_ACA</B> task management function,
|
|
currently does nothing.
|
|
</LI>
|
|
<LI> <B>SCST_CLEAR_TASK_SET</B> - <B>CLEAR_TASK_SET</B> task management
|
|
function, the same as <B>SCST_ABORT_TASK_SET</B>.
|
|
</LI>
|
|
<LI> <B>SCST_LUN_RESET</B> - <B>LUN_RESET</B> task management function,
|
|
implemented via <B>scsi_reset_provider()</B> call for the specified device
|
|
with <B>SCSI_TRY_RESET_DEVICE</B> parameter.
|
|
</LI>
|
|
<LI> <B>SCST_TARGET_RESET</B> - <B>TARGET_RESET</B> task management
|
|
function, implemented via <B>scsi_reset_provider()</B> call for all the
|
|
hosts in the system (one device per each host) with
|
|
<B>SCSI_TRY_RESET_BUS</B> parameter at first and then, if failed, with
|
|
<B>SCSI_TRY_RESET_HOST</B>.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss9.1">9.1 scst_rx_mgmt_fn_tag()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_rx_mgmt_fn_tag()</B> tells SCST to perform the specified
|
|
task management function, based on the command's tag. Can be used only
|
|
for <B>SCST_ABORT_TASK</B>.</P>
|
|
<P>It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_rx_mgmt_fn_tag(
|
|
struct scst_session *sess,
|
|
int fn,
|
|
uint32_t tag,
|
|
int atomic,
|
|
void *tgt_specific)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI> <B>sess</B> - the session, on which the command should be performed.
|
|
</LI>
|
|
<LI> <B>fn</B> - task management function, one of the constants above.
|
|
</LI>
|
|
<LI> <B>tag</B> - the command's tag.
|
|
</LI>
|
|
<LI> <B>atomic</B> - true, if the function called in the atomic context.
|
|
</LI>
|
|
<LI> <B>tgt_specific</B> - pointer to the target driver specific data,
|
|
can be retrieved in <B>task_mgmt_fn_done()</B> via
|
|
<B>scst_mgmt_cmd_get_status()</B> function.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>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 <B>task_mgmt_fn_done()</B>
|
|
driver's callback.</P>
|
|
|
|
<H2><A NAME="ss9.2">9.2 scst_rx_mgmt_fn_lun()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_rx_mgmt_fn_lun()</B> tells SCST to perform the specified
|
|
task management function, based on the LUN. Currently it can be used for
|
|
any function, except <B>SCST_ABORT_TASK</B>.</P>
|
|
<P>It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_rx_mgmt_fn_lun(
|
|
struct scst_session *sess,
|
|
int fn,
|
|
const uint8_t *lun,
|
|
int lun_len,
|
|
int atomic,
|
|
void *tgt_specific);
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI> <B>sess</B> - the session, on which the command should be performed.
|
|
</LI>
|
|
<LI> <B>fn</B> - task management function, one of the constants above.
|
|
</LI>
|
|
<LI> <B>lun</B> - LUN, the format is the same as for <B>scst_rx_cmd()</B>.
|
|
</LI>
|
|
<LI> <B>lun_len</B> - LUN's length.
|
|
</LI>
|
|
<LI> <B>atomic</B> - true, if the function called in the atomic context.
|
|
</LI>
|
|
<LI> <B>tgt_specific</B> - pointer to the target driver specific data,
|
|
can be retrieved in <B>task_mgmt_fn_done()</B> via
|
|
<B>scst_mgmt_cmd_get_status()</B> function.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>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 <B>task_mgmt_fn_done()</B>
|
|
driver's callback.</P>
|
|
|
|
<H2><A NAME="s10">10. Device specific drivers (device handlers)</A></H2>
|
|
|
|
<P>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:</P>
|
|
<P>
|
|
<UL>
|
|
<LI>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:
|
|
|
|
<UL>
|
|
<LI> Improves security and reliability by not trusting the data
|
|
supplied by remote initiator via SCSI low-level protocol.
|
|
</LI>
|
|
<LI> 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.
|
|
</LI>
|
|
</UL>
|
|
|
|
</LI>
|
|
<LI>To process some exceptional conditions, like ILI on tape devices.
|
|
</LI>
|
|
<LI>To initialize incoming commands with some device-specific
|
|
parameters, like timeout value.
|
|
</LI>
|
|
<LI>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.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>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.</P>
|
|
<P>Without appropriate device handler SCST hides devices of this type from
|
|
remote initiators and returns <B>HARDWARE ERROR</B> sense data to any
|
|
requests to them.</P>
|
|
|
|
<H2><A NAME="ss10.1">10.1 Device specific driver registration</A>
|
|
</H2>
|
|
|
|
<H3>scst_register_dev_driver()</H3>
|
|
|
|
<P>To work with SCST a device specific driver must register itself in SCST by
|
|
calling <B>scst_register_dev_driver()</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_register_dev_driver(
|
|
struct scst_dev_type *dev_type)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>dev_type</B> - device specific driver's description structure</LI>
|
|
</UL>
|
|
</P>
|
|
<P>The function returns 0 on success or appropriate error code otherwise.</P>
|
|
|
|
<H3>Structure <B>scst_dev_type</B></H3>
|
|
|
|
<P>Structure <B>scst_dev_type</B> is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
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;
|
|
}
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>name</B> - the name of the device handler. Must be defined and
|
|
unique
|
|
</LI>
|
|
<LI><B>type</B> - SCSI type of the supported device. Must be defined.
|
|
</LI>
|
|
<LI><B>parse_atomic</B>, <B>exec_atomic</B>, <B>dev_done_atomic</B> - true,
|
|
if corresponding function supports execution in the atomic
|
|
(non-sleeping) context
|
|
</LI>
|
|
<LI><B>int (*init) (struct scst_dev_type *dev_type)</B> - called on the
|
|
device handler load, before the first attach(). Returns 0 on success,
|
|
error code otherwise.
|
|
</LI>
|
|
<LI><B>void (*release) (struct scst_dev_type *dev_type)</B> - called on
|
|
the device handler unload, after final detach()
|
|
</LI>
|
|
<LI><B>int (*attach) (struct scst_device *dev)</B> - called when new
|
|
device is attaching to the device handler
|
|
</LI>
|
|
<LI><B>void (*detach) (struct scst_device *dev)</B> - called when new
|
|
device is detaching from the device handler
|
|
</LI>
|
|
<LI><B>int (*attach_tgt) (struct scst_tgt_device *tgt_dev)</B> - called
|
|
when new tgt_device (session) is attaching to the device handler
|
|
</LI>
|
|
<LI><B>void (*detach_tgt) (struct scst_tgt_device *tgt_dev)</B> - called
|
|
when tgt_device (session) is detaching from the device handler
|
|
</LI>
|
|
<LI><B>int (*parse) (struct scst_cmd *cmd, const struct scst_info_cdb
|
|
*cdb_info)</B> - called to parse CDB from the command. It should initialize
|
|
<B>cmd->bufflen</B> and <B>cmd->data_direction</B> (see below
|
|
<B>SCST_DATA_*</B> constants) if necessary, otherwise defaults based on
|
|
<B>cdb_info</B> will be used. Parameter <B>cdb_info</B> provides some info
|
|
about the CDB (see below). Pay attention to "atomic" attribute of the
|
|
cmd, which can be via by <B>scst_cmd_atomic()</B>: it is true if the
|
|
function called in the atomic (non-sleeping) context. Returns the
|
|
command's next state or <B>SCST_CMD_STATE_DEFAULT</B>, if the next default
|
|
state should be used, or <B>SCST_CMD_STATE_NEED_THREAD_CTX</B> if the
|
|
function called in atomic context, but requires sleeping. In the last
|
|
case, the function will be recalled in the thread context, where
|
|
sleeping is allowed. Additionally, <B>SCST_CMD_DATA_BUF_ALLOCED</B> flag
|
|
can be set by <B>parse()</B> (see above). Must be defined.
|
|
</LI>
|
|
<LI><B>int (*exec) (struct scst_cmd *cmd, void (*scst_cmd_done)( struct
|
|
scst_cmd *cmd, int next_state))</B> - called to execute CDB. The result of
|
|
the CDB execution is reported via <B>scst_cmd_done()</B> callback. Pay
|
|
attention to "atomic" attribute of the command, which can be get via
|
|
<B>scst_cmd_atomic()</B>: it is true if the function called in the
|
|
atomic (non-sleeping) context. For <B>scst_cmd_done()</B> parameter
|
|
<B>next_state</B> is the command's next state or
|
|
<B>SCST_CMD_STATE_DEFAULT</B>, if the next default state should be used.
|
|
Using this function modules <B>devdisk_perf</B> and <B>devtape_perf</B> were
|
|
implemented. These modules in their <B>exec()</B> method skip (pretend to
|
|
execute) all READ and WRITE operations and thus allow direct link
|
|
performance measurements without overhead of actual data transferring
|
|
from/to underlying SCSI device. See also <B>scst_is_cmd_local()</B> below.
|
|
Returns:
|
|
|
|
<UL>
|
|
<LI><B>SCST_EXEC_COMPLETED</B> - the command is done, go to
|
|
other ones
|
|
</LI>
|
|
<LI><B>SCST_EXEC_NEED_THREAD</B> - thread context is required to
|
|
execute the command. <B>Exec()</B> will be called again in the
|
|
thread context.
|
|
</LI>
|
|
<LI><B>SCST_EXEC_NOT_COMPLETED</B> - the command should be sent
|
|
to SCSI mid-level.
|
|
</LI>
|
|
</UL>
|
|
|
|
</LI>
|
|
<LI><B>int (*dev_done) (struct scst_cmd *cmd)</B> - called to notify
|
|
device handler about the result of the command's execution and perform
|
|
some post processing. If <B>parse()</B> function is called,
|
|
<B>dev_done()</B> is guaranteed to be called as well. The command's fields
|
|
<B>tgt_resp_flags</B> and <B>resp_data_len</B> should be set by this
|
|
function, but SCST offers good defaults. Pay attention to "atomic"
|
|
attribute of the command, which can be get via
|
|
<B>scst_cmd_atomic()</B>: it is true if the function called in the
|
|
atomic (non-sleeping) context. Returns the command's next state or
|
|
<B>SCST_CMD_STATE_DEFAULT</B>, if the next default state should be used,
|
|
or <B>SCST_CMD_STATE_NEED_THREAD_CTX</B> if the function called in atomic
|
|
context, but requires sleeping. In the last case, the function will be
|
|
recalled in the thread context, where sleeping is allowed.
|
|
</LI>
|
|
<LI><B>int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, struct
|
|
scst_tgt_dev *tgt_dev, struct scst_cmd *cmd_to_abort)</B> - called to
|
|
execute a task management command. Returns:
|
|
|
|
<UL>
|
|
<LI><B>SCST_DEV_TM_COMPLETED_SUCCESS</B> - the command is done
|
|
with success, no further actions required
|
|
</LI>
|
|
<LI><B>SCST_DEV_TM_COMPLETED_FAILED</B> - the command is failed,
|
|
no further actions required
|
|
</LI>
|
|
<LI><B>SCST_DEV_TM_NOT_COMPLETED</B> - regular standard actions
|
|
for the command should be done
|
|
</LI>
|
|
</UL>
|
|
|
|
|
|
<B>NOTE</B>: for <B>SCST_ABORT_TASK</B> called under spinlock
|
|
</LI>
|
|
<LI><B>void (*on_free_cmd) (struct scst_cmd *cmd)</B> - called to notify
|
|
device handler that the command is about to be freed. Could be called on
|
|
IRQ context.
|
|
</LI>
|
|
<LI><B>int (*proc_info) (char *buffer, char **start, off_t offset,
|
|
int length, int *eof, struct scst_dev_type *dev_type, int inout)</B> - this
|
|
function can be used to export the handler's statistics and other
|
|
information to the world outside the kernel. Parameters:
|
|
|
|
<OL>
|
|
<LI> <B>buffer, start, offset, length, eof</B> - have the same
|
|
meaning as for <B>read_proc_t</B> function of the kernel
|
|
</LI>
|
|
<LI> <B>dev_type</B> - pointer to the device handler, for which
|
|
the function is called
|
|
</LI>
|
|
<LI> <B>inout</B> - read/write direction flag, 0 - for reads, other
|
|
value - for writes
|
|
</LI>
|
|
</OL>
|
|
|
|
|
|
If the driver needs to create additional files in its /proc
|
|
subdirectory, it can use <B>scst_proc_get_dev_type_root()</B> function to get
|
|
the root proc_dir_entry.
|
|
</LI>
|
|
<LI><B>struct module *module</B> - pointer to device handler's module
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Structure <B>scst_info_cdb</B> is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
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;
|
|
}
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>flags</B> - CDB's flags can be (OR'ed):
|
|
|
|
<UL>
|
|
<LI><B>SCST_TRANSFER_LEN_TYPE_FIXED</B> - set if data length in
|
|
CDB set in blocks
|
|
</LI>
|
|
<LI><B>SCST_SMALL_TIMEOUT</B> - set if CDB requires small timeout
|
|
</LI>
|
|
<LI><B>SCST_LONG_TIMEOUT</B> - set if CDB requires long timeout
|
|
</LI>
|
|
</UL>
|
|
|
|
</LI>
|
|
<LI><B>direction</B> - one of the <B>SCST_DATA_*</B> constants (see below)
|
|
</LI>
|
|
<LI><B>transfer_len</B> - CDB's data length as set in CDB
|
|
</LI>
|
|
<LI><B>cdb_len</B> - CDB's length
|
|
</LI>
|
|
<LI><B>op_name</B> - the name of the command
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Field <B>cmd->data_direction</B>, set by <B>parse()</B>, can have one of the
|
|
following values:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>SCST_DATA_UNKNOWN</B> - data flow direction is unknown
|
|
</LI>
|
|
<LI><B>SCST_DATA_WRITE</B> - data flow direction is <B>WRITE</B> (from
|
|
target to initiator)
|
|
</LI>
|
|
<LI><B>SCST_DATA_READ</B> - data flow direction is <B>READ</B> (from
|
|
initiator to target)
|
|
</LI>
|
|
<LI><B>SCST_DATA_NONE</B> - there is no data transfer
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss10.2">10.2 Device specific driver unregistration</A>
|
|
</H2>
|
|
|
|
<P>Device specific driver is unregistered by calling
|
|
<B>scst_unregister_dev_driver()</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_unregister_dev_driver(
|
|
struct scst_dev_type *dev_type)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>dev_type</B> - device specific driver's description structure</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="s11">11. SCST commands' states</A></H2>
|
|
|
|
<P>
|
|
There are the following states, which a SCST command passes through
|
|
during execution and which could be returned by device handler's
|
|
<B>parse()</B> and <B>dev_done()</B> (but not all states are allowed to be
|
|
returned): </P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>SCST_CMD_STATE_INIT_WAIT</B> - the command is created, but
|
|
<B>scst_cmd_init_done()</B> not called
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_INIT</B> - LUN translation (i.e. <B>cmd->tgt_dev</B>
|
|
assignment) state
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_REINIT</B> - again LUN translation, used if device
|
|
handler wants to restart the command on another LUN
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_DEV_PARSE</B> - device handler's <B>parse()</B> is going
|
|
to be called
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_PREPARE_SPACE</B> - allocation of the command's
|
|
data buffer
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_RDY_TO_XFER</B> - target driver's
|
|
<B>rdy_to_xfer()</B> is going to be called
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_DATA_WAIT</B> - waiting for data from the initiator
|
|
(until <B>scst_rx_data()</B> called)
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_SEND_TO_MIDLEV</B> - the command is going to be
|
|
sent to SCSI mid-level for execution
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_EXECUTING</B> - waiting for the command's execution
|
|
finish
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_DEV_DONE</B> - device handler's <B>dev_done()</B> is
|
|
going to be called
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_XMIT_RESP</B> - target driver's
|
|
<B>xmit_response()</B> is going to be called
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_XMIT_WAIT</B> - waiting for data/response's
|
|
transmission finish (until <B>scst_tgt_cmd_done()</B> called)
|
|
</LI>
|
|
<LI><B>SCST_CMD_STATE_FINISHED</B> - the command finished and going to be
|
|
freed
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="s12">12. SCST's structures manipulation functions</A></H2>
|
|
|
|
<P>Target drivers must not directly access any fields in SCST's structures,
|
|
they must use only described below functions.</P>
|
|
|
|
<H2><A NAME="ss12.1">12.1 SCST target driver manipulation functions</A>
|
|
</H2>
|
|
|
|
<H3>scst_tgt_get_tgt_specific() and scst_tgt_set_tgt_specific()</H3>
|
|
|
|
<P>
|
|
Function <B>scst_tgt_get_tgt_specific()</B> returns pointer to the target
|
|
driver specific data, set by <B>scst_tgt_set_tgt_specific()</B>. It is
|
|
defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_tgt_get_tgt_specific(
|
|
struct scst_tgt *tgt)
|
|
</PRE>
|
|
</P>
|
|
<P>Function <B>scst_tgt_set_tgt_specific()</B> stores the target driver
|
|
specific data that could be retrieved later by
|
|
by<B>scst_tgt_get_tgt_specific()</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_tgt_set_tgt_specific(
|
|
struct scst_tgt *tgt,
|
|
void *val)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>tgt</B> - pointer to the SCST target structure</LI>
|
|
<LI><B>val</B> - pointer to the target driver specific data</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss12.2">12.2 SCST session manipulation functions</A>
|
|
</H2>
|
|
|
|
<H3>scst_sess_get_tgt_specific() and scst_sess_set_tgt_specific()</H3>
|
|
|
|
<P>Function <B>scst_sess_get_tgt_specific()</B> returns pointer to the target
|
|
driver specific data, set by <B>scst_sess_set_tgt_specific()</B>. It is
|
|
defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_sess_get_tgt_specific(
|
|
struct scst_session *sess)
|
|
</PRE>
|
|
</P>
|
|
<P>Function <B>scst_sess_set_tgt_specific()</B> stores the target driver
|
|
specific data that could be retrieved later by
|
|
by<B>scst_sess_get_tgt_specific()</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_sess_set_tgt_specific(
|
|
struct scst_session *sess,
|
|
void *val)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>sess</B> - pointer to the SCST session structure</LI>
|
|
<LI><B>val</B> - pointer to the target driver specific data</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss12.3">12.3 SCST command manipulation functions</A>
|
|
</H2>
|
|
|
|
<H3>scst_cmd_atomic()</H3>
|
|
|
|
<P>Function <B>scst_cmd_atomic()</B> returns true if the command is
|
|
being executed in the atomic context or false otherwise. It is defined
|
|
as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_cmd_atomic(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command to check</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_session()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_session()</B> returns the command's session. It
|
|
is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
struct scst_session *scst_cmd_get_session(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_resp_data_len()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_resp_data_len()</B> returns the command's
|
|
response data length. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
unsigned int scst_cmd_get_resp_data_len(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_tgt_resp_flags()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_tgt_resp_flags()</B> returns the command's
|
|
response data response flags (SCST_TSC_FLAG_* constants). It is defined
|
|
as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_cmd_get_tgt_resp_flags(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_buffer()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_buffer()</B> returns the command's data buffer.
|
|
It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_cmd_get_buffer(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
<P>It is recommended to use <B>scst_get_buf_*()</B>scst_put_buf()/ family of
|
|
function instead of direct access to the data buffers, because they hide
|
|
all HIGHMEM and SG/plain buffer issues.</P>
|
|
|
|
<H3>scst_cmd_get_bufflen()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_bufflen()</B> returns the command's data buffer
|
|
length. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
unsigned int scst_cmd_get_bufflen(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
<P>It is recommended to use <B>scst_get_buf_*()</B>scst_put_buf()/ family of
|
|
function instead of direct access to the data buffers, because they hide
|
|
all HIGHMEM and SG/plain buffer issues.</P>
|
|
|
|
<H3>scst_cmd_get_use_sg()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_use_sg()</B> returns the command's <B>use_sg</B>
|
|
value. Its meaning is the same as for <B>scsi_cmnd</B>. The function is
|
|
defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
unsigned short scst_cmd_get_use_sg(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
<P>It is recommended to use <B>scst_get_buf_*()</B>scst_put_buf()/ family of
|
|
function instead of direct access to the data buffers, because they hide
|
|
all HIGHMEM and SG/plain buffer issues.</P>
|
|
|
|
<H3>scst_cmd_get_data_direction()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_data_direction()</B> returns the command's data
|
|
direction (SCST_DATA_* constants). It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
scst_data_direction scst_cmd_get_data_direction(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_status()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_status()</B> returns the status byte from
|
|
host device. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
uint8_t scst_cmd_get_status(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_masked_status()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_masked_status()</B> returns the status byte set
|
|
from host device by status_byte(). It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
uint8_t scst_cmd_get_masked_status(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_msg_status()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_msg_status()</B> returns the status from host
|
|
adapter itself. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
uint8_t scst_cmd_get_msg_status(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_host_status()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_host_status()</B> returns the status set by
|
|
low-level driver to indicate its status. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
uint16_t scst_cmd_get_host_status(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_driver_status()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_driver_status()</B> returns the status set by
|
|
SCSI mid-level. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
uint16_t scst_cmd_get_driver_status(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_sense_buffer()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_sense_buffer()</B> returns pointer to the sense
|
|
buffer. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
uint8_t *scst_cmd_get_sense_buffer(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_sense_buffer_len()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_sense_buffer_len()</B> returns the sense buffer
|
|
length. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_cmd_get_sense_buffer_len(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_tag() and scst_cmd_set_tag()</H3>
|
|
|
|
<P> Function <B>scst_cmd_get_tag()</B> returns the command's tag, set by
|
|
<B>scst_cmd_set_tag()</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
uint32_t scst_cmd_get_tag(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Function <B>scst_cmd_set_tag()</B> sets command's tag that could be
|
|
retrieved later by <B>scst_cmd_get_tag()</B>. It is defined as the
|
|
following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_cmd_set_tag(
|
|
struct scst_cmd *cmd,
|
|
uint32_t tag)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
<LI><B>tag</B> - the tag</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_tgt_specific() and scst_cmd_get_tgt_specific_lock()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_get_tgt_specific()</B> and
|
|
<B>scst_cmd_get_tgt_specific_lock()</B> return pointer to the target
|
|
driver specific data, set by <B>scst_cmd_set_tgt_specific()</B> or
|
|
<B>scst_cmd_set_tgt_specific_lock()</B>. Both function are basically the
|
|
same, but the later one additionally takes lock, which helps to prevent
|
|
some races. See <B>scst_find_cmd()</B> below for details.</P>
|
|
<P>They are defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_cmd_get_tgt_specific(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_cmd_get_tgt_specific_lock(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_set_tgt_specific() and scst_cmd_set_tgt_specific_lock()</H3>
|
|
|
|
<P>Functions <B>scst_cmd_set_tgt_specific()</B> and
|
|
<B>scst_cmd_set_tgt_specific_lock()</B> store the target driver specific
|
|
data, that could be retrieved later by <B>scst_cmd_get_tgt_specific()</B>
|
|
or <B>scst_cmd_get_tgt_specific_lock()</B>. Both function are basically
|
|
the same, but the later one additionally takes lock, which helps to
|
|
prevent some races. See <B>scst_find_cmd()</B> below for details.</P>
|
|
<P>They are defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_cmd_set_tgt_specific(
|
|
struct scst_cmd *cmd,
|
|
void *val)
|
|
</PRE>
|
|
</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_cmd_set_tgt_specific_lock(
|
|
struct scst_cmd *cmd,
|
|
void *val)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
<LI><B>val</B> - pointer to the target driver specific data</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_get_data_buff_alloced() and scst_cmd_set_data_buff_alloced()</H3>
|
|
|
|
<P>Function <B>scst_cmd_get_data_buff_alloced()</B> returns the state of the
|
|
<B>SCST_CMD_DATA_BUF_ALLOCED</B> flag. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_cmd_get_data_buff_alloced(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Function <B>scst_cmd_set_data_buff_alloced()</B> tells SCST that the data
|
|
buffer is alloced by target driver or device handler by setting the
|
|
<B>SCST_CMD_DATA_BUF_ALLOCED</B> flag on. Could be useful, for instance,
|
|
for iSCSI unsolicited data. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_cmd_set_data_buff_alloced(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_cmd_set_expected(), scst_cmd_is_expected_set(),scst_cmd_get_expected_data_direction() and scst_cmd_get_expected_transfer_len()</H3>
|
|
|
|
<P>Function <B>scst_cmd_set_expected()</B> tells SCST expected data transfer
|
|
direction and its length, as supplied by remote initiator. It is defined
|
|
as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_cmd_set_expected(
|
|
struct scst_cmd *cmd,
|
|
scst_data_direction expected_data_direction,
|
|
unsigned int expected_transfer_len)
|
|
</PRE>
|
|
</P>
|
|
<P>Function <B>scst_cmd_is_expected_set()</B> returns true, if the expected
|
|
values were set by target driver and false otherwise. It is defined as
|
|
the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_cmd_is_expected_set(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Function <B>scst_cmd_get_expected_data_direction()</B> returns expected
|
|
data direction set by target driver, if any. If this value was not set,
|
|
the return value is undefined. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
scst_data_direction scst_cmd_get_expected_data_direction(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Function <B>scst_cmd_get_expected_transfer_len()</B> returns expected
|
|
transfer length set by target driver, if any. If this value was not set,
|
|
the return value is undefined. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
unsigned int scst_cmd_get_expected_transfer_len(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
<LI><B>expected_data_direction</B> - expected data direction</LI>
|
|
<LI><B>expected_transfer_len</B> - expected transfer length</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_get_buf_first(), scst_get_buf_next(),scst_put_buf() and scst_get_buf_count()</H3>
|
|
|
|
<P>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.</P>
|
|
<P>Function <B>scst_get_buf_first()</B> starts access to the data. It is defined
|
|
as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_get_buf_first(
|
|
struct scst_cmd *cmd,
|
|
uint8_t **buf)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
<LI><B>buf</B> - pointer, where pointer to the first data chunk will be put</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns the length of the chunk of data for success, 0 for the end of
|
|
data, negative error code otherwise. </P>
|
|
<P>Function <B>scst_get_buf_next()</B> continues access to the data. It is defined
|
|
as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_get_buf_next(
|
|
struct scst_cmd *cmd,
|
|
uint8_t **buf)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
<LI><B>buf</B> - pointer, where pointer to the next data chunk will be put</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns the length of the chunk of data for success, 0 for the end of
|
|
data, negative error code otherwise. </P>
|
|
<P>Function <B>scst_put_buf()</B> tells SCST that the user of the chunk of
|
|
data, returned by <B>scst_get_buf_first()</B> or <B>scst_get_buf_next()</B>,
|
|
finished accessing the data. This function must be called for all chunks
|
|
of data, returned by <B>scst_get_buf_first()</B> or
|
|
<B>scst_get_buf_next()</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_put_buf(
|
|
struct scst_cmd *cmd,
|
|
uint8_t *buf)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
<LI><B>buf</B> - pointer to the data chunk</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Function <B>scst_get_buf_count()</B> returns the approximate higher
|
|
rounded count of data chunks that <B>scst_get_buf_[first|next]()</B> will
|
|
return. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_get_buf_count(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - pointer to the command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss12.4">12.4 SCST task management commands manipulation functions</A>
|
|
</H2>
|
|
|
|
<H3>scst_mgmt_cmd_get_tgt_specific()</H3>
|
|
|
|
<P>Function <B>scst_mgmt_cmd_get_tgt_specific()</B> returns pointer to the
|
|
target driver specific data, set on call of <B>scst_rx_mgmt_fn_tag()</B>
|
|
or <B>scst_rx_mgmt_fn_lun()</B>. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_mgmt_cmd_get_tgt_specific(
|
|
struct scst_mgmt_cmd *mcmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>mcmd</B> - pointer to the task management command</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H3>scst_mgmt_cmd_get_status()</H3>
|
|
|
|
<P>Functions <B>scst_mgmt_cmd_get_status()</B> returns task management
|
|
command's completion status. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void *scst_mgmt_cmd_get_status(
|
|
struct scst_mgmt_cmd *mcmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>mcmd</B> - pointer to the task management command</LI>
|
|
</UL>
|
|
</P>
|
|
<P>The following status values are possible:</P>
|
|
<P>
|
|
<UL>
|
|
<LI> SCST_MGMT_STATUS_SUCCESS - the task management command completed
|
|
successfully
|
|
</LI>
|
|
<LI> SCST_MGMT_STATUS_FAILED - the task management command failed.
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="s13">13. Miscellaneous functions</A></H2>
|
|
|
|
<H2><A NAME="ss13.1">13.1 scst_find_cmd_by_tag()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_find_cmd_by_tag()</B> is designed to find SCST's command
|
|
based on the supplied tag comparing it with one that previously set by
|
|
<B>scst_cmd_set_tag()</B>. This value should be set by the target driver
|
|
on the command's initialization time.</P>
|
|
<P>It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
struct scst_cmd *scst_find_cmd_by_tag(
|
|
struct scst_session *sess,
|
|
uint32_t tag)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>sess</B> - session to which the command belongs</LI>
|
|
<LI><B>tag</B> - the tag</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns found command or NULL otherwise.</P>
|
|
|
|
<H2><A NAME="ss13.2">13.2 scst_find_cmd()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_find_cmd()</B> is designed to find SCST's command. For example,
|
|
it can be used to find the command by internal serial number that was
|
|
supplied by a remote target's response.</P>
|
|
<P>It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
struct scst_cmd *scst_find_cmd(
|
|
struct scst_session *sess,
|
|
void *data,
|
|
int (*cmp_fn)(struct scst_cmd *cmd, void *data))
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>sess</B> - session to which the command belongs
|
|
</LI>
|
|
<LI><B>data</B> - comparison data that will be passed to <B>cmp_fn()</B>
|
|
as is
|
|
</LI>
|
|
<LI><B>cmp_fn</B> - comparison callback function that will be called for
|
|
each the session's command. Should return true if the command is found,
|
|
false otherwise. Parameters:
|
|
|
|
<UL>
|
|
<LI><B>cmd</B> - the command to compare</LI>
|
|
<LI><B>data</B> - comparison data.</LI>
|
|
</UL>
|
|
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns found command or NULL otherwise.</P>
|
|
<P><B>IMPORTANT</B></P>
|
|
<P>SCST is designed in a such way that any command is always processed only
|
|
by one thread at any time, so no locking is necessary. But there is one
|
|
exception from that rule, it is <B>scst_find_cmd()</B> function. Since it
|
|
calls the callback over all commands of the session in the internal
|
|
lists, despite of the command's current state, there is a race
|
|
possibility accessing to target specific data pointer between
|
|
<B>scst_cmd_set_tgt_specific()</B> caller and <B>cmp_fn()</B>, which usually
|
|
calls <B>scst_cmd_get_tgt_specific()</B> from the different context. The
|
|
only place, where it is safe to call <B>scst_cmd_set_tgt_specific()</B>
|
|
without the race probability, is between <B>scst_rx_cmd()</B> and
|
|
<B>scst_cmd_init_done()</B>. Thus, if you call
|
|
<B>scst_cmd_set_tgt_specific()</B> only there, there is nothing to worry,
|
|
always use the functions without "lock" suffix. Otherwise, be careful
|
|
and, if necessary, use "lock" functions. In addition, <B>cmp_fn()</B> is
|
|
allowed to use only target specific data and forbidden to call any
|
|
SCST's functions.</P>
|
|
|
|
<H2><A NAME="ss13.3">13.3 scst_get_cdb_info()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_get_cdb_info()</B> provides various CDB info. It is
|
|
defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_get_cdb_info(
|
|
const uint8_t *cdb_p,
|
|
int dev_type,
|
|
struct scst_info_cdb *info_p)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cdb_p</B> - pointer to CDB
|
|
</LI>
|
|
<LI><B>dev_type</B> - SCSI device type
|
|
</LI>
|
|
<LI><B>info_p</B> - the result structure, see description in device
|
|
handler's <B>parse()</B> chapter
|
|
</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns 0 on success, -1 otherwise.</P>
|
|
|
|
<H2><A NAME="ss13.4">13.4 scst_to_dma_dir()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_to_dma_dir()</B> translates SCST's data direction to
|
|
DMA one. It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_to_dma_dir(
|
|
int scst_dir)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>scst_dir</B> - one of the <B>SCST_DATA_*</B> constants</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns the corresponding <B>PCI_DMA_*</B> constant.</P>
|
|
|
|
<H2><A NAME="ss13.5">13.5 scst_is_cmd_local()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_is_cmd_local()</B> checks if the command is handled
|
|
by SCST (i.e. locally, as, e.g., REPORT LUNS command). Intended to be
|
|
used in device handler's <B>exec()</B>, when the device handler wants to
|
|
perform all the commands, except ones that should be done by SCST
|
|
itself. </P>
|
|
<P>It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_is_cmd_local(
|
|
struct scst_cmd *cmd)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>cmd</B> - the command, which CDB should be checked</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns 1, if the command's CDB is locally handled by SCST or 0
|
|
otherwise</P>
|
|
|
|
<H2><A NAME="ss13.6">13.6 scst_register_virtual_device() and scst_unregister_virtual_device()</A>
|
|
</H2>
|
|
|
|
<P>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.</P>
|
|
<P>Function <B>scst_register_virtual_device()</B> registers a virtual device.
|
|
During the registration the device handlers functions <B>init()</B> and
|
|
<B>attach()</B> will be called, if defined. The function is defined as the
|
|
following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_register_virtual_device(
|
|
struct scst_dev_type *dev_handler)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>dev_handler</B> - device handler's descriptor</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns assigned to the device ID on success, or negative value otherwise.</P>
|
|
<P>Function <B>scst_unregister_virtual_device()</B> unregisters a virtual
|
|
device. During the unregistration the device handlers functions
|
|
<B>detach()</B> and <B>release()</B> will be called, if defined. The
|
|
function is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_unregister_virtual_device(
|
|
int id)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>id</B> - the device's ID, returned by
|
|
<B>scst_register_virtual_device()</B></LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss13.7">13.7 scst_add_threads() and scst_del_threads()</A>
|
|
</H2>
|
|
|
|
<P>These functions allows to add or delete some SCST threads. For example,
|
|
if <B>exec()</B> function in your device handler works synchronously, i.e.
|
|
wait for job's completion, in order to prevent performance loss you
|
|
can add for SCST as many threads as there are devices serviced by your
|
|
device handler.</P>
|
|
<P>Function <B>scst_add_threads()</B> starts requested number of threads. It
|
|
is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
int scst_add_threads(
|
|
int num)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>num</B> - number of the threads to start</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns 0 on success, error code otherwise.</P>
|
|
<P>Function <B>scst_del_threads()</B> stops requested number of threads. It
|
|
is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
void scst_del_threads(
|
|
int num)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>num</B> - number of the threads to stop</LI>
|
|
</UL>
|
|
</P>
|
|
|
|
<H2><A NAME="ss13.8">13.8 scst_proc_get_tgt_root()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_proc_get_tgt_root()</B> returns target driver's root
|
|
entry in SCST's /proc hierarchy. The driver can create own
|
|
files/directories here, which should be deleted in the driver's
|
|
release(). It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
struct proc_dir_entry *scst_proc_get_tgt_root(
|
|
struct scst_tgt_template *vtt)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>vtt</B> - pointer to the driver's template</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns proc_dir_entry on success, NULL otherwise.</P>
|
|
|
|
<H2><A NAME="ss13.9">13.9 scst_proc_get_dev_type_root()</A>
|
|
</H2>
|
|
|
|
<P>Function <B>scst_proc_get_dev_type_root()</B> returns device handler's
|
|
root entry in SCST's /proc hierarchy. The driver can create own
|
|
files/directories here, which should be deleted in the driver's
|
|
detach() or release(). It is defined as the following:</P>
|
|
<P>
|
|
<PRE>
|
|
struct proc_dir_entry *scst_proc_get_dev_type_root(
|
|
struct scst_dev_type *dtt)
|
|
</PRE>
|
|
</P>
|
|
<P>Where:</P>
|
|
<P>
|
|
<UL>
|
|
<LI><B>dtt</B> - pointer to the handler's description structure</LI>
|
|
</UL>
|
|
</P>
|
|
<P>Returns proc_dir_entry on success, NULL otherwise.</P>
|
|
|
|
</BODY>
|
|
</HTML>
|