自分の勉強用にscsi_mid_low_api.txtを訳したのでメモ。
Introduction
この文書はLinuxのSCSI mid levelドライバとSCSI lower levelドライバの間のインターフェースの概略を説明するものである。Lower level ドライバ(以後、LLDs)はhost bus adapter(以後、HBA)ドライバとhostドライバ(以後、HD)など様々なものから呼ばれます。この文脈における"host"はIOバス(例. PCI または ISA)とSCSIトランスポート上に存在する一つのSCSI Initiator Portをつなぐブリッジを意味します。”initiator"ポート(SCSIの専門用語です。SAM-3を参照ください)は"target" SCSI port(例. ディスク)にSCSIコマンドを送ります。稼働中のシステムではたくさんのLLDsが存在するかもしれませんが、ハードウェアタイプにつき一つのみ適用されます。大半のLLDsは一つ以上のSCSI HBAを制御できます。いくつかのHBAは複数のhostを含みます。
SCSI transportはLinuxにおいて既にそれ自身のsubsystemをもつ外部バスである場合もあります。(例. USBとieee1394).そのような場合、SCSI subsystem のLLDは他のドライバのsubsystemへのソフトウェアブリッジとなります。例はusb-storageドライバ(dirvers/usb/storageにあります)とiee1394/sbp2ドライバ(drivers/ieee1394 にあります)です。
例えば、aic7xxx LLDはその会社の7xxx chipシリーズを採用しているAdaptec SCSI parallel interface(SPI)コントローラを制御します。Aic7xxx LLDはカーネルに組み込む、またはモジュールとして読み込むことができます。Linuxシステムにおいて稼働するaic7xxx LLDは一つだけですが、それはたくさんのHBAを制御するかもしれません。これらのHBAはPCIのドーターボードとして存在するかマザーボード上に組み込まれているかもしれません(あるいは両方の場合もあります。)いくつかのaic7xxxベースのHBAはデュアルコントローラです。従って2つのhostを表します。最新のHBAと同様に、aic7xxxのそれぞれのhostは自身のPCIデバイスアドレスを持っています。[SCSI hostとPCIデバイスの1対1対応は一般的ですが、必須ではありません。(例. ISA adpterを使うとき)]
SCSI mid層はLLDをSCSI upper層のドライバやblock層のドライバのような他の層から隔離します。
この文書はLinux kernel version 2.6.8に大体一致しています。
Documentation
SCSIの文書ディレクトリはLinxuソースツリーの中にあり、通常はDocumentation/scsiになります。大半の文書はplainテキスト(すなわちASCII)です。このファイルはscsi_mid_low_api.txtというファイル名で、該当ディレクトリで見つけられます。この文書のより最近のコピーは
http://web.archive.org/web/20070107183357rn_1/sg.torque.net/scsi/
で見つかるかもしれません。たくさんのLLDsはそこで文書化されています。(例. Aic7xxx.txt)。SCSI mid levelはLinux Kernel 2.4系のSCSI subsystemについて述べている文書へのurlを含むscsi.txtで簡単に述べられています。2つのupper levelドライバはそのディレクトリに文書を持っています。すなわち st.txt(SCSI tape driver)とscsi-generic.txt(sg driverのためのもの)です。
いくつかのLLDsに関する文書またはurlはCソースコード中あるいは、Cソースコードと同一のディレクトリで見つかるかもしれません。例えば、USB mass storage driverについてのURLを見つけるには、/usr/src/linux/drivers/usb/storage ディレクトリを見てください。
Driver structure
従来、SCSI subsystem用のLLDはdrivers/scsiディレクトリ中の少なくとも2つのファイルからなります。例えば、xyzと呼ばれるドライバはヘッダーファイルのxyz.hとソースファイルのxyz.cを持ちます。[実際には、一つのファイルにすべて記述できない明確な理由はありません。ヘッダーファイルが余分なこともあります]。様々なOSに移植されるいくつかのドライバは2つ以上のファイルを持ちます。例えば、aic7xxxドライバはgeneric用とOS特有のもの用(例. FreeBSDとLinux)に分割されたファイルを持ちます。そのようなドライバはdrivers/scsiディレクトリ以下にそれら自身のディレクトリを作る傾向にあります。
新しいLLDをLinuxへ追加するときは、以下のファイル(drivers/scsiディレクトリで見つかります)は注意する必要があります。MakefileとKconfigです。既存のLLDがどのように構成されているか調べることがおそらく最善です。
2.5系のdevelopment kernelが2.6系のproduction系に進化していく過程で、変更はこのインターフェースに導入されています。この例の一つが2つのモデルを有効にしているドライバの初期化コードです。古い初期化は、HBAドライバのロード時に検出されたhostに基づくものです。類似のものは2.4系で見つかります。これは"passive"初期化モデルと呼ばれます。新しいモデルはLLDの生存期間中にHBAにhotplugとunpluggedを許します。そしてこれは"hotplug"初期化モデルと呼ばれます。新しいモデルは、永続的に接続される従来のSCSIデバイスとhotplugされる最新のSCSIデバイス(例. USBまたはIEEE1394接続のデジカメ)を両方同じように扱えるため好ましいです。両方の初期化モデルは以下のセクションで議論します。
SCSI subsystemmへのLLDインターフェースは様々な方法があります:
a) Mid levelから直接関数を実行する
b) Mid levelから提供される関数登録用の関数ポインタを経由する。Mid levelは将来どこかのタイミングでこれらの関数を実行します。LLDはこれらの関数の実装を提供します。
c) Mid levelによって維持されているよく知られているデータ構造のインスタンスに直にアクセスする
a)グループの関数は以下の”Mid level supplied functions "の章でリスト化しています。
b)グループの関数は以下の”Interface functions"の章でリスト化しています。それらの関数ポインタは"struct scsi_host_template"の中に存在し、scsi_host_alloc()(*注1)によって渡されます。LLDが提供したくない関数インターフェースはscsi_host_templateの対応するメンバをNULLに設定します。ファイルスコープでのscsi_host_templateのインスタンスの定義は、明示的に初期化されていない関数ポインタをNULLに設定します。
c)グループの使い方は、特に"hotplug"環境のときは注意が必要です。LLDはmid levelや他のレイヤーと共有しているインスタンスの生存期間に気づかなければなりません。
LLD内で定義されているすべての関数とファイルスコープで定義されているすべてのデータはstaticであるべきです。例えば、xxxという名称のLLDで定義されているslave_alloc()関数は、"static int xxx_slave_alloc(struct scsi_device sdev) { / code */}"と定義することができます。
(*注1) scsi_host_alloc()関数はほとんどの場合、scsi_register()関数と置き換えられます。scsi_register()とscsi_unregister()関数はpassive初期化モデルのサポート維持用に残っています。
Hotplug initialization model
このモデルではLLDがSCSI subsystemからSCSI hostを導入するときと、取り除くときを制御します。Hostはドライバ初期化と同じくらいの速さで導入され、またドライバがshutdownされるのと同じくらい遅く取り除かれます。通常、ドライバはHBAが検出されたことを示すsysfs probe()コールバックに応答します。新しいデバイスがLLDが制御したいものであることを確認後、LLDはHBAを初期化しそれからSCSI mid levelに新しいhostを登録します。
LLDの初期化中に、ドライバはHBAが発見されるのを期待する適切なIOバスにドライバ自身を登録すべきです。(例. PCIバス)。これはおそらくsysfs経由で可能です。いくつかのドライバパラメータ(特にドライバがロードされた後に書き込み可能なもの)はこの時点でsysfsにも登録可能です。SCSI mid levelはLLDが最初のHBAを登録したとき、LLDを認識するようになります。
しばらくしてから、LLDはHBAとそれに続くLLDとmid levelの間の一般的な呼び出しシーケンスを認識します。この例は3つのSCSIデバイス中、最初の2つのみが応答を返す新しく導入されたHBAをスキャンするmid levelを示します。
HBA PROBE: assume 2 SCSI devices found in scan
LLD mid level LLD
===-------------------=========--------------------===------
scsi_host_alloc() -->
scsi_add_host() ---->
scsi_scan_host() -------+
|
slave_alloc()
slave_configure() --> scsi_adjust_queue_depth()
|
slave_alloc()
slave_configure()
|
slave_alloc() ***
slave_destroy() ***
------------------------------------------------------------
もしLLDがデフォルトのqueueの設定を調節したい場合は、slave_configure()ルーチンの中でscsi_adjust_queue_depth()を起動します。
*** mid levelがスキャンを試しても応答がないscsi deviceに対しては、slave_alloc(), slave_destroy()のペアが呼ばれます。
HBAが取り外されるときは、アンロードされたLLDモジュールに関連付けられた通常のシャットダウン処理の一部か、sysfsのremove() コールバックが起動されたことによって意図された"hot unplug"への応答かもしれません。いずれの場合にしろシーケンスは同じです。
HBA REMOVE: assume 2 SCSI devices attached
LLD mid level LLD
===----------------------=========-----------------===------
scsi_remove_host() ---------+
|
slave_destroy()
slave_destroy()
scsi_host_put()
------------------------------------------------------------
Scsi_Host構造体のインスタンス(scsi_host_alloc()の戻り値のポインタのことです)を追いかけるのはLLDにとって役に立つかもしれません。そのようなインスタンスはmid-levelによって所有されます。Scsi_Host構造体のインスタンスはリファレンスカウントが0になったときにscsi_host_put()経由で解放されます。
マウントしたファイルシステム上のSCSIコマンドを処理中のdiskを制御するHBAをHot unplugするのは興味深い状況です。リファレンスカウントのロジックはmid-levelに関係するたくさんの問題に対処するためにmid-levelに導入されています。以下のリファレンスカウントの章を見てください。
hotplugの概念はSCSIデバイスに拡張されるかもしれません。現在、HBAが追加されたとき、scsi_scan_host()関数はHBAのSCSI transportに取り付けられたSCSI deviceへのスキャンを行います。より新しいSCSI transportでは、scan処理が完了したあとにHBAが新しいSCSIデバイスに気づくようになるかもしれません。LLDはmid-levelにSCSIデバイスを気づかせるために以下のシーケンスを利用可能です。
SCSI DEVICE hotplug
LLD mid level LLD
===-------------------=========--------------------===------
scsi_add_device() ------+
|
slave_alloc()
slave_configure() [--> scsi_adjust_queue_depth()]
------------------------------------------------------------
同様の仕方で、LLDはSCSIデバイスの取り外されたか、SCSIデバイスへの接続が中断されたことに気づけるようになるかもしれません。いくつかの既存のSCSIトランスポート(例.SPI)はmid-levelによって発行されるSCSIデバイスをofflineにするSCSIコマンドが失敗するまで、SCSIデバイスが取り外されたことを気づかないかもしれません。SCSIデバイスの取り外しを検知するLLDは以下のシーケンスによってupper layerからの取り外しを開始できます。
SCSI DEVICE hot unplug
LLD mid level LLD
===----------------------=========-----------------===------
scsi_remove_device() -------+
|
slave_destroy()
------------------------------------------------------------
LLDにとってscsi_device構造体のインスタンスを追いかけることは役に立つかもしれません(ポインタはslave_alloc()とslave_configure()コールバックへのパラメータとして渡されます)。そのようなインスタンスはmid-levelによって所有されます。scsi_device構造体はslave_destroy()のあとに解放されます。
Passive initialization model
これらの古いLLDはソースコードの中に"scsi_module.c"と呼ばれるファイルを含んでいます。そのファイルを機能させるために、driver_templateと呼ばれるscsi_host_template構造体のインスタンスを定義する必要があります。このモデルの典型的なコードシーケンスの使い方は以下です:
static struct scsi_host_template driver_template = {
...
};
#include "scsi_module.c"
Scsi_module.cファイルは以下の2つの関数を含みます:
- Init_this_scsi_driver() LLDが初期化されるときに実行されます(i.e 起動時 or モジュールロード時)
- Exit_this_scsi_driver() LLDがシャットダウンされるときに実行されます(i.e モジュールアンロード時)
注意: これらの関数は__initと__exit修飾詞でタグ付けされているので、LLDは明示的にコールすべきではありません。(カーネルが実行するので)
これは2つのホストが認識され(detect()が2を返す)、それぞれのホスト上でSCSIバススキャンを実行して1つのSCSIデバイスを発見する例です。(2個めのSCSIデバイスが応答を返さなかった)。
LLD mid level LLD
===----------------------=========-----------------===------
init_this_scsi_driver() ----+
|
detect() -----------------+
| |
| scsi_register()
| scsi_register()
|
slave_alloc()
slave_configure() --> scsi_adjust_queue_depth()
slave_alloc() ***
slave_destroy() ***
|
slave_alloc()
slave_configure()
slave_alloc() ***
slave_destroy() ***
------------------------------------------------------------
Mid-levelはtagged queuingをoffにしてscsi_adjust_queue_depth()を呼び出し、そのホスト用にqueue lengthとして"cmd_per_lun"を設定します。これらの設定はLLDによって提供されるslave_configure()で上書き可能です。
*** mid levelがスキャンを試みて応答をかえさなかったSCSIデバイス用に、slave_alloc()とslave_detroy()のペアが呼ばれます。
以下はLLDがシャットダウンするシーケンスです。
LLD mid level LLD
===----------------------=========-----------------===------
exit_this_scsi_driver() ----+
|
slave_destroy()
release() --> scsi_unregister()
|
slave_destroy()
release() --> scsi_unregister()
------------------------------------------------------------
LLDはslave_destroy()を定義する必要はありません(i.e. 定義するかはオプショナルです)。
"passive initialization"モデルの欠点はホストの登録と解除がLLDの初期化とシャットダウンに結びついていることです。いったんLLDが初期化されると、ドライバのシャットダウンや再初期化なしに新しく出現したホスト(e.g hotplug経由)を簡単に追加することができません。両方の初期化モデルに対応したLLDを書くことは可能かもしれません。
Reference Counting
Scsi_Host構造体にはリファレンスカウントインフラが追加されました。これによって、それらを利用する様々なSCSIレイヤを通してScsi_Hostインスタンスの所有権が効果的に広がります。以前は、そのようなインスタンスはmid levelによって排他的に所有されていました。LLDはこれらのリファレンスカウントを直接操作する必要は普段はありませんが、いくつかのケースでは操作する必要があります。
Scsi_Host構造体に関連した興味深い3つのリファレンスカウントの関数があります。
- scsi_host_alloc(): リファレンスカウントを1に設定したScsi_Host構造体の新しいインスタンスへのポインタを返します。^^
- scsi_host_get(): 与えられたインスタンスのリファレンスカウントに1加算します。
- scsi_host_put(): 与えられたインスタンスのリファレンスカウントを1減算します。もしリファレンスカウントが0になったら、与えられたインスタンスを解放します
Scsi_device構造体にはリファレンスカウントインフラが追加されました。それらを利用する様々なSCSIレイヤを通してScsi_deviceインスタンスの所有権が効果的に広がります。以前は、そのようなインスタンスはmid levelによって排他的に所有されていました。Include/scsi/scsi_device.hの終わりにむかって宣言されたアクセス関数を見てください。もしLLDがScsi_deviceインスタンスへのポインタのコピーを持ち続けたいなら、そのリファレンスカウントを押し上げるためにscsi_device_get()を使うべきです。そのポインタが終了したときは、そのリファレンスカウントを減らすためにscsi_device_put()を使用できます(そして潜在的にはそれを削除します)。
^^ Scsi_Host構造体は実際にはこれらの関数によって並行に操作される2つのリファレンスカウントを持ちます。(shost_devとshost_gendevのこと?)
Conventions
まずリーナス・トーバルズCのコーディングスタイルについての考えはDocumentation/CodingStyleファイルで見つけることができる。
次に、struct tagsのために類義語を導入したtypedefを禁止する動きがあります。SCSI subsystemにおいてはまだ両方とも見つけることができますが、そのようなtypedefは将来の削除をより簡単にするために一つのファイルscsi_typedefs.hに移されています。例えば:
"typedef struct scsi_cmnd Scsi_Cmnd;"
また、大部分のC99のエンハンスは関連するgccのコンパイラによってそれらがサポートされる拡張を奨励します。そのため、C99スタイルの構造体と配列の初期化子は必要な場合には奨励されます。度を越さないでください、VLAsはまだ適切にサポートされていません。これに対する例外は、"//"スタイルのコメントを使うことです。/* */スタイルのコメントがLinuxではまだ好まれます。
よく書かれ、テストされ文書化されたコードは上記の慣例に従うように再整形する必要はありません。例えば、aic7xxxドライバはFreeBSDとAdaptecの研究所からLinuxへやって来ます。きっとFreeBSDとAdaptecは彼ら自身のコーディングの慣例を持っています。
Mid level supplied functions
これらの関数はLLDによって利用されるためにSCSI mid levelによって提供されます。これらの関数の名前(すなわちエントリポイント)はLLDがそれらにアクセスできるようにexportされます。カーネルはどんなLLDが初期化されるより前にSCSI mid levelをロードし初期化されるように整えます。以下の関数はアルファベット順に並べられ、それらの関数名はすべて"scsi_"から始まります。
Summary:
scsi_activate_tcq - turn on tag command queueing
scsi_add_device - creates new scsi device (lu) instance
scsi_add_host - perform sysfs registration and set up transport class
scsi_adjust_queue_depth - change the queue depth on a SCSI device
scsi_bios_ptable - return copy of block device's partition table
scsi_block_requests - prevent further commands being queued to given host
scsi_deactivate_tcq - turn off tag command queueing
scsi_host_alloc - return a new scsi_host instance whose refcount==1
scsi_host_get - increments Scsi_Host instance's refcount
scsi_host_put - decrements Scsi_Host instance's refcount (free if 0)
scsi_partsize - parse partition table into cylinders, heads + sectors
scsi_register - create and register a scsi host adapter instance.
scsi_remove_device - detach and remove a SCSI device
scsi_remove_host - detach and remove all SCSI devices owned by host
scsi_report_bus_reset - report scsi _bus_ reset observed
scsi_scan_host - scan SCSI bus
scsi_track_queue_full - track successive QUEUE_FULL events
scsi_unblock_requests - allow further commands to be queued to given host
scsi_unregister - [calls scsi_host_put()]
Details:
省略
Interface Functions
インターフェース関数はLLDによって定義され、それらの関数ポインタはscsi_host_alloc()[または scsi_register()/init_this_scsi_driver()]によって割り当てられたscsi_host_template構造体のインスタンスに配置されます。いくつかは必須です。インターフェース関数はstaticで宣言されるべきです。受け入れられた慣例は"xyz"というドライバはそれのslave_configure()関数を static int xyz_slave_configure(struct scsi_device *sdev)として宣言します。そして、以下に並べられているすべてのインターフェース関数も同様です。
この関数へのポインタはscsi_host_template構造体インスタンスのslave_configureメンバに配置されるべきです。そのようなインスタンスへのポインタはmid levelのscsi_host_alloc()[または scsi_register()/init_this_scsi_driver()]へ渡されるべきです。
インターフェース関数はまたinclude/scsi/scsi_host.hファイルのscsi_host_template構造体の定義箇所のすぐ上に記述されています。場合によっては、詳細はscsi_host.h内のそれより下に記述されています。
インターフェース関数はアルファベット順に以下に記載します。
Summary:
bios_param - fetch head, sector, cylinder info for a disk
detect - detects HBAs this driver wants to control
eh_timed_out - notify the host that a command timer expired
eh_abort_handler - abort given command
eh_bus_reset_handler - issue SCSI bus reset
eh_device_reset_handler - issue SCSI device reset
eh_host_reset_handler - reset host (host bus adapter)
info - supply information about given host
ioctl - driver can respond to ioctls
proc_info - supports /proc/scsi/{driver_name}/{host_no}
queuecommand - queue scsi command, invoke 'done' on completion
release - release all resources associated with given host
slave_alloc - prior to any commands being sent to a new device
slave_configure - driver fine tuning for given device after attach
slave_destroy - given device is about to be shut down
Details:
省略
Data Structures
struct scsi_host_template
LLD単位で一つの"struct scsi_host_template"があります***。それはドライバのヘッダファイルで静的なファイルスコープとして初期化されます。その方法では明示的に初期化されないメンバーは0またはNULLが設定されます。関心のあるメンバーは以下です:
name - ドライバ名(スペースは含んでもよいが、80文字までにしてください)
proc_name - "/proc/scsi//"のと "drivers"ディレクトリの一つにあるsysfsによって使われます。更に、"proc_name"はUnixファイル名で受け付けられる文字のみ含むべきです。
(*queuecommand)() - mid levelがSCSIコマンドをLLDに注入するために利用する主要なコールバックです。
scsi_host_template構造体はinclude/scsi/scsi_host.hで定義とコメントをされています。
*** 極端な状況では、一つのドライバがいくつかの異なるハードウェアのクラスを制御するなら、一つのドライバが複数のインスタンスを持つかもしれません。(e.g. LLDがISAカードとPCIカード両方を扱い、それぞれのクラスに対して分離されたscsi_host_template構造体を持ちます)。
struct Scsi_Host
LLDが制御するhost(HBA)につき一つのScsi_Host構造体があります。Scsi_Host構造体は"struct scsi_host_template"と共通してたくさんのメンバーを持ちます。新しいScsi_Host構造体のインスタンスが作成されると(hosts.cにあるscsi_host_alloc()によって作成される)、それらの共通のメンバーはドライバのscsi_host_template構造体のインスタンスから初期化されます。関心のあるメンバーは以下です:
host_no - このホストを特定するためのシステム全体で唯一の番号です。0から昇順に値が振られます。
can_queue - 0より大きい値でなければなりません。can_queueの値より多くのコマンドをアダプタへ送ってはいけません。
this_id - SCSIホスト(イニシエータ)のIDを表します。不明なら-1を設定します。
sg_tablesize - ホストが許容する最大のスキャッターギャザーの要素数です。0ならそのホストはスキャッターギャザーをサポートしていないことを意図します。
max_sectors - 一つのSCSIコマンドで許容される最大のセクター数です(通常は512byteです)。初期値0は、現在は1024を設定するSCSI_DEFAULT_MAX_SECTORS(scsi_host.hで定義されています)の設定を導きます。これまでのところ、max_sectorsが定義されていないときディスクの最大転送長は512KBです。このサイズはディスクファームウェアのアップロードには十分ではないかもしれないことに注意してください。
cmd_per_lun - ホストによって制御されるデバイスにキューイングできる最大のコマンド数です。LLDがscsi_adjust_queue_depth()を呼ぶことで上書きします。
unchecked_isa_dma -1 =>のときは RAMの下位16MBのみ使います(ISAのDMAアドレッシングの制限)、0 >= のときは32bit以上のDMAアドレス空間をフルに使えます。
use_clustering - 1=>mid levelのSCSIコマンドはマージ可能です,
0=>SCSIコマンドのマージは不許可です
hostt - 生成されたScsi_Host構造体のインスタンスからドライバのscsi_host_template構造体へのポインタです。
hostt->proc_name -LLDの名前です。これはsysfsが使うドライバの名前です。
transportt - scsi_transport_templateインスタンスへのポインタです(もしあれば)。FCとSPIのtransportは現在サポートされています。
sh_list - すべてのScsi_Hostインスタンスへの双方向リンクドリストです。(現在やhost_noの昇順にならんでいます)。
my_devices - このホストに属するscsi_device構造体への双方向リンクドリストです。 hostdata[0] - Scsi_Host構造体の最後にあるLLD用の予約領域です。サイズはscsi_host_alloc()またはscsi_register()の第2引数によって設定されます。
vendor_id - Scsi_Hostのためにベンダーが提供しているLLDを特定するユニークな値です。大半はベンダー特湯のメッセージ要求を検証するのに使います。値は識別子の型とベンダー特有の値で構成されます。有効なフォーマットの記述はscsi_netlink.hを見てください。
scsi_host構造体はinclude/scsi/scsi_host.hで定義されています。
struct scsi_device
一般的に、1つのホスト上の各SCSI LU毎にこの構造体のインスタンスが1つあります。ホストへ接続されたSCSIデバイスはチャネル番号、ターゲットID、logical unit number(lun)によって一意に特定されます。この構造体はinclude/scsi/scsi_device.hで定義されています。
struct scsi_cmnd
この構造体のインスタンスはSCSIコマンドをLLDへ伝え、応答をmid levelへ返します。SCSI mid levelはscsi_adjust_queue_depth()(またはScsi_Host構造体のcmd_per_lun)によって意図された値以上のSCSIコマンドをLLDに対してキューイングはしません。それぞれのSCSIデバイスに対して少なくとも1つのscsi_cmnd構造体のインスタンスが利用可能でしょう。関心のあるメンバーは以下です:
cmnd - SCSI commandを保持する配列
cmnd_len - SCSI commandの配列長(in bytes)
sc_data_direction - データフェーズでのデータ転送方向。include/linux/dma-mapping.hの"enum dma_data_direction" を見てください
request_bufflen - 転送するデータバイトの数(0は転送データなし)
use_sg - ==0 -> スキャッターリストを使用しない。すなわち、request buffer へ/からの転送データ - >0 -> use_sg要素を用いたrequest_buffferのスキャッターギャザーリスト(実際には配列) request_buffer - use_sgの設定に依存したデータバッファーかスキャッターギャザーリストのどちらか。スキャッターギャザーの要素はinclude/asm/scatterlist.hのstruct scatterlistによって定義されている。
done - SCSIコマンドが完了(成功またはその他)したときにLLDによって起動されるべき関数ポインタ。LLDがコマンドを受け入れた(i.e queuecommand()が0を返したまたは返す)なら、LLDによってのみ呼ばれるべきです。LLDはqueuecommand()が終了する前にdoneを起動するかもしれません。
result - doneを呼ぶより前にLLDがによって設定されるべきです。値0はコマンドが成功したことを意図します。(そしてすべてのデータがSCSIターゲットデバイスへ/から転送されました。)resultは4つの関連するバイトとして閲覧される32bitのunsigned integerです。SCSIステータスの値はLSBです。Include/scsi/scsi.h のstatus_byte(), msg_byte(), host_byte(), driver_byte()マクロと関係する定数を見てください。
sense_buffer - SCSIステータス(resultのLSB)がCHECK CONDITION(2)に設定されたとき書かれるべき配列(最大長:SCSI_SENSE_BUFFERSIZEバイト)。CHECK CONDITIONが設定されたとき、sense_buffer[0]の上位4bitが値7なら、mid levelはsense_bufferの配列が有効なSCSIセンスバッファーを含んでいると仮定します。そうでなければ、mid levelはREQUESET SENSEのSCSIコマンドをセンスバッファーを獲得するために発行するでしょう。後者の戦略はコマンドのキューイングの存在においてエラーしがちである。そのためLLDはいつもauto-senseであるべきだ。
device - このコマンドに関連付けられたscsi_deviceオブジェクトへのポインタである
resid - LLDはこの符号付き整数に要求した転送長(i.e request_buffer)から実際に転送したバイト数を引いたものを設定すべきである。residは予め0が設定されており、もしunderrun(overrunはまれ)を検知できなかったらLLDはそれを無視できます。もし可能ならLLDはdoneの起動前にresidを設定すべきです。もっとも興味深いケースはunderrunするSCSIターゲットデバイス(e.g READs)からのデータ転送です。
underflow - LLDはこの値より実際に転送されたバイト数が少ないなら、resultの中に(DID_ERROR_ << 16)を配置すべきです。このチェックを実装し、DID_ERRORをレポートするよりログへエラーメッセージを吐き出すLLDはあまり多くありません。LLDにとってより良いのはresidを実装することです。
LLDはSCSIターゲットデバイスからのデータ転送にはresidを設定することをおすすめします(例. READs)。MEDIUM_ERRORとHARDWARE ERROR(可能ならRECOVERD ERROR)のセンスキーをもつようなデータ転送のときは、residを設定することは特に重要です。もしLLDがデータをどれくらい受け入れるかわからずに、それから最も安全なアプローチとしてデータを受け入れないようにするこれらの場合。例えば:有効なデータが受信されていないことを示すために、LLDはこれらのヘルパー関数を使うかもしれません:
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
SCpntはscsi_cmndオブジェクトへのポインタです。512バイトのブロックを3つだけ受信したことを示すために、residはこのように設定されます。
scsi_set_resid(SCpnt, scsi_bufflen(SCpnt) - (3 * 512));
scsi_cmnd構造体はinclude/scsi/scsi_cmnd.hで定義されています。
Locks
Scsi_Host構造体のインスタンスはscsi_host_alloc()で初期化されるScsi_Host::default_lockと呼ばれるspin_lockを持ちます。同じ関数内で、Scsi_Host::host_lockポインタはdefault_lockを指すように初期化されます。その後、lockとunlockの操作はScsi_Host::host_lockポインタを使用するmid levelによって行われます。以前のドライバはhost_lockを上書きできましたが、これはもう許可されていません。
Autosense
Autosense(or auto-sense)はSAM-2ドキュメントで、CHECK_CONDITIONの状態が発生したときに、アプリケーションクライアントへのセンスデータの自動的な応答がSCSIコマンドの完了と一致することと定義されています。LLDsはautosenseを演じるべきです。これはLLDがそれぞれによってCHECK CONDITIONを検知したときになされるべきです:
a) そのような応答においてさらなるデータを演じるためにSCSIプロトコルの命令をします。
b) または、LLDはREQUEST SENSEコマンドを自身に発行します
どちらの方法にせよ、CHECK CONDITIONが認識されたとき、mid levelはstruct scsi_cmnd::sense_buffer[0]をチェックして、LLDがautosenseを演じるかどうかを決定します。
もしこのバイトが7の上位4bit(0b0111の意味? or 0xf)を持っているなら、それからautosenseが起こったと想定されます。もし別の値を持っているなら(そしてこのバイトはそれぞれのコマンドの前に0に初期化されます)、mid levelはREQUESET SENSEコマンドを発行します。
キューイングされたコマンドが存在する場合、次のREQUESET SENSEまで失敗するコマンドからセンスバッファーデータを維持するnexusは同期がとれないかもしれません。これはLLDがautosenseを演じる最もよい理由です。