SCSI 磁带驱动程序

此文件包含有关 SCSI 磁带驱动程序的简要信息。该驱动程序目前由 Kai Mäkisara 维护(电子邮件:Kai.Makisara@kolumbus.fi

上次修改时间:2016 年 2 月 9 日星期二 21:54:16,作者:kai.makisara

基础知识

该驱动程序是通用的,即它不包含任何针对特定磁带驱动器的代码。磁带参数可以使用以下三种方法之一指定

1. 每个用户都可以使用 ioctl 直接指定他/她想要使用的磁带参数。这在管理上是一种非常简单灵活的方法,适用于单用户工作站。但是,在多用户环境中,下一个用户会发现磁带参数处于上一个用户离开它们时的状态。

2. 系统管理员(root)可以使用 MTSETDRVBUFFER ioctl 为某些磁带参数定义默认值,例如块大小和密度。这些参数可以被编程为在新磁带加载到驱动器中时生效,或者如果从磁带开头开始写入时生效。如果磁带驱动器能够很好地自动检测磁带格式(例如某些 QIC 驱动器),则第二种方法适用。结果是任何磁带都可以读取,可以使用现有格式继续写入,如果从头重写磁带(或首次写入新磁带),则使用默认格式。如果驱动器不能很好地执行自动检测,并且该设备存在单一的“合理”模式,则第一种方法适用。一个例子是仅在可变块模式下使用的 DAT 驱动器(我不知道这是否合理 :-))。

用户可以覆盖系统管理员定义的参数。更改会持续到再次生效默认值。

3. 默认情况下,可以使用次要编号(位 5 和 6)定义和选择最多四种模式。可以通过更改 st.h 中的 ST_NBR_MODE_BITS 来更改模式的数量。模式 0 对应于上述默认值。其他模式处于休眠状态,直到系统管理员(root)定义它们。当开始指定新模式时,模式 0 的配置用于为新模式的定义提供起点。

使用模式允许系统管理员为用户提供对某些用户无法直接访问的缓冲参数(缓冲和异步写入)的选择。这些模式还允许在多磁带操作中选择格式(当加载新磁带时,显式覆盖的参数会重置)。

如果使用多个模式,则所有模式都应包含相同参数集的定义。

许多 Unix 系统包含将不同模式与受支持的设备关联的内部表。Linux SCSI 磁带驱动程序不包含此类表(并且将来也不会这样做)。相反,可以制作一个实用程序,该实用程序获取设备发送的查询数据,扫描其数据库,并使用 ioctl 设置模式。另一种方法是制作一个小型脚本,该脚本使用 mt 来设置针对系统定制的默认值。

该驱动程序支持固定和可变块大小(在缓冲区限制内)。自动回绕(次要编号等于设备编号)和非回绕设备(次要编号为 128 + 设备编号)均已实现。

在可变块模式下,write() 中的字节计数确定磁带上物理块的大小。读取时,驱动器读取下一个磁带块,如果 read() 字节计数至少是块大小,则将数据返回给用户。否则,返回错误 ENOMEM。

在固定块模式下,驱动器和驱动程序之间的数据传输是块大小的倍数。write() 字节计数必须是块大小的倍数。读取时不是必需的,但为了可移植性,建议这样做。

提供支持更改磁带分区以及使用一个或两个分区对磁带进行分区。默认情况下,禁用每个驱动程序对分区磁带的支持,可以使用 ioctl MTSETDRVBUFFER 启用它。

默认情况下,该驱动程序在写入后关闭设备并且上次操作是写入时写入一个文件标记。可以选择写入两个文件标记。在这两种情况下,数据结束都通过连续两次读取返回零字节来表示。

在 SCSI 命令块中不设置立即位的情况下写入文件标记充当同步点,即在命令返回之前,驱动程序缓冲区中的所有剩余数据都写入到磁带。这可以确保在该点捕获写入错误,但这需要时间。在某些应用程序中,必须快速写入几个连续的文件。MTWEOFI 操作可用于在不刷新驱动程序缓冲区的情况下写入文件标记。在 close() 处写入文件标记始终会刷新驱动程序缓冲区。但是,如果之前的操作是 MTWEOFI,则 close() 不会写入文件标记。如果程序想要在文件之间关闭/打开磁带设备并想要跳过等待,可以使用它。

如果执行回绕、脱机、bsf 或查找并且之前的磁带操作是写入,则会在移动磁带之前写入文件标记。

编译选项在文件 linux/drivers/scsi/st_options.h 中定义。

4. 如果使用打开选项 O_NONBLOCK,即使驱动器未就绪,打开也会成功。如果未使用 O_NONBLOCK,则驱动程序会等待驱动器变为就绪状态。如果这在 ST_BLOCK_SECONDS 秒内没有发生,则打开失败,错误代码值为 EIO。使用 O_NONBLOCK,即使驱动器中存在写保护磁带,也可以打开设备进行写入(如果尝试,尝试写入某些内容的命令会返回错误)。

次要编号

如果为每个驱动器使用 4 种模式,则磁带驱动程序当前最多支持 2^17 个驱动器。

次要编号由以下位字段组成

dev_upper non-rew mode dev-lower
20 -  8     7    6 5  4      0

非回绕位始终是位 7(最低字节中的最高位)。定义模式的位位于非回绕位之下。其余位定义磁带设备编号。此编号与当次要编号仅为 8 位宽时使用的编号向后兼容。

Sysfs 支持

该驱动程序创建目录 /sys/class/scsi_tape,并使用与现有磁带设备对应的目录填充它。每种模式都有自动回绕和非回绕条目。名称为 stxy 和 nstxy,其中 x 是磁带编号,y 是与模式对应的字符(none、l、m、a)。例如,第一个磁带设备的目录是(假设四种模式):st0 nst0 st0l nst0l st0m nst0m st0a nst0a。

每个目录都包含条目:default_blksize default_compression default_density defined dev device driver。如果模式已定义,则文件“defined”包含 1,如果未定义,则包含 0。文件“default_*”包含用户设置的默认值。值 -1 表示未设置默认值。文件“dev”包含与此设备对应的设备编号。链接“device”和“driver”指向 SCSI 设备和驱动程序条目。

每个目录还包含条目“options”,该条目显示当前启用的驱动程序和模式选项。文件中的值是一个位掩码,其中位定义与使用 MTSETDRVBUFFER 设置选项时使用的位定义相同。

一个名为“tape”的链接是从 SCSI 设备目录到与模式 0 自动回绕设备(例如,st0)对应的类目录创建的。

磁带设备的 Sysfs 和统计信息

st 驱动程序在 sysfs 文件系统内维护磁带驱动器的统计信息。可以使用以下方法来定位可用的统计信息(假设 sysfs 安装在 /sys 中)

  1. 在目录 /sys/class/scsi_tape 上使用 opendir(3)

  2. 使用 readdir(3) 读取目录内容

  3. 使用 regcomp(3)/regexec(3) 将目录条目与扩展正则表达式“^st[0-9]+$”匹配

  4. 从 /sys/class/scsi_tape/<match>/stats 目录访问统计信息(其中 <match> 是来自 /sys/class/scsi_tape 且与扩展正则表达式匹配的目录条目)

使用此方法的原因是,指向同一磁带驱动器的所有字符设备都使用相同的统计信息。这意味着 st0 将具有与 nst0 相同的统计信息。

该目录包含以下统计信息文件

  1. in_flight
    • 当前对此设备的未完成 I/O 的数量。

  2. io_ns
    • 等待所有 I/O 完成(包括读取和写入)所花费的时间(以纳秒为单位)。这包括磁带移动命令,例如在文件或设置标记之间查找,以及隐式磁带移动,例如当使用关闭磁带设备时的回绕时。

  3. other_cnt
    • 除了读取或写入命令之外,向磁带驱动器发出的 I/O 的数量。完成这些命令所花费的时间使用以下计算:io_ms-read_ms-write_ms。

  4. read_byte_cnt
    • 从磁带驱动器读取的字节数。

  5. read_cnt
    • 向磁带驱动器发出的读取请求数。

  6. read_ns
    • 等待读取请求完成所花费的时间(以纳秒为单位)。

  7. write_byte_cnt
    • 写入磁带驱动器的字节数。

  8. write_cnt
    • 向磁带驱动器发出的写入请求数。

  9. write_ns
    • 等待写入请求完成所花费的时间(以纳秒为单位)。

  10. resid_cnt
    • 在读取或写入操作期间,发现残留量非零的次数。这应该意味着程序发出的读取操作大于磁带上的块大小。对于写入操作,并非所有数据都写入到磁带。

注意

当 I/O 开始时,`in_flight` 值会递增,而 I/O 本身直到完成才会添加到统计信息中。

在设备级别,`read_cnt`、`write_cnt` 和 `other_cnt` 的总和可能与 `iodone_cnt` 的值不同。磁带统计信息仅计算通过 `st` 模块发出的 I/O。

当进行读取操作时,统计信息在 I/O 进行期间可能在时间上不一致。各个值会被原子地读取和写入,但是当通过 sysfs 读取它们时,它们可能正在 I/O 开始或完成时被更新。

在更新任何统计信息之前,`in_flight` 中显示的值会递增;在更新统计信息后,当 I/O 完成时,该值会递减。当没有 `st` 驱动程序发出的未完成 I/O 时,`in_flight` 的值为 0。磁带统计信息不考虑通过 `sg` 设备执行的任何 I/O。

BSD 和 Sys V 语义

用户可以通过定义符号 `ST_SYSV` 的值来选择磁带驱动程序的这两种行为。当读取的文件关闭时,语义有所不同。BSD 语义将磁带停留在当前位置,而 SYS V 语义将磁带移动到下一个文件标记之后,除非刚刚越过了文件标记。

默认设置为 BSD 语义。

缓冲

驱动程序尝试直接与用户空间进行数据传输。如果不可能,则会使用在运行时分配的驱动程序缓冲区。如果整个传输无法进行直接 I/O,则会使用驱动程序缓冲区(即,不使用单个页面的反弹缓冲区)。由于以下几个原因,直接 I/O 可能无法进行,例如:

  • 一个或多个页面位于 HBA 无法访问的地址。

  • 传输中的页面数超过了 HBA 允许的分散/聚集段数。

  • 一个或多个页面无法锁定到内存中(在任何合理的情况下都不应发生)。

驱动程序缓冲区的大小始终至少为一个磁带块。在固定块模式下,最小缓冲区大小由 `ST_FIXED_BUFFER_BLOCKS`(以 1024 字节为单位)定义。对于小块大小,这允许缓冲多个块并使用一个 SCSI 读取或写入操作来传输所有块。如果 `ST_BUFFER_WRITES` 非零且未使用直接 I/O,则允许在固定块模式下跨写入调用缓冲数据。缓冲区分配使用大小为 2^n * (页面大小) 的内存块。因此,实际缓冲区大小可能大于允许的最小缓冲区大小。

请注意,如果使用直接 I/O,则小写入操作不会被缓冲。从 2.4 版本升级时,这可能会令人感到意外。在 2.4 版本中,小写入操作(例如,不带 -b 选项的 tar)可能具有良好的吞吐量,但在 2.6 版本中不再如此。可以关闭直接 I/O 来解决此问题,但更好的解决方案是使用更大的 `write()` 字节计数(例如,`tar -b 64`)。

异步写入。将缓冲区内容写入磁带的操作会启动,并且写入调用会立即返回。状态会在下一个磁带操作中检查。异步写入不使用直接 I/O,也不在固定块模式下进行。

在某些罕见情况下,如果磁带上的可用空间不足以在预警标记后刷新驱动程序缓冲区,则缓冲写入和异步写入可能会在多卷操作中引起问题。

固定块模式的预读 (ST_READ_AHEAD)。即使用户不想在此读取命令中获取所有数据,也会尝试填充缓冲区。对于那些不喜欢文件标记截断读取请求或不喜欢倒带的驱动器,应禁用此功能。

如果无法分配连续缓冲区,则会使用分散/聚集缓冲区(由物理内存中不连续的块组成的缓冲区)。为了支持所有 SCSI 适配器(包括那些不支持分散/聚集的适配器),缓冲区分配使用以下三种类型的块:

  1. 初始段,用于所有 SCSI 适配器,包括那些不支持分散/聚集的适配器。如果系统可以提供此大小的块(并且不大于 `ST_BUFFER_BLOCKS` 指定的缓冲区大小),则此缓冲区的大小将为 (`PAGE_SIZE << ST_FIRST_ORDER`) 字节。如果此大小不可用,则驱动程序会将大小减半,并再次尝试,直到大小为一页。`st_options.h` 中的默认设置使驱动程序尝试将所有缓冲区分配为一个块。

  2. 分配用于填充指定缓冲区大小的分散/聚集段,以便使用尽可能多的段,但段数不超过 `ST_FIRST_SG`。

  3. 如果必要,则使用 `ST_MAX_SG`(或模块参数 `max_sg_segs`)与阶段 1 和 2 中使用的段数之间的剩余段在运行时扩展缓冲区。如果 SCSI 适配器允许的分散/聚集段数小于指定的最大分散/聚集段数,则不会超过允许的最大段数。如果 SCSI 适配器允许的最大段数小于阶段 1 和 2 中使用的段数,则扩展缓冲区将始终失败。

写入时的 EOM 行为

当遇到介质末端预警时,当前写入操作将完成,并返回字节数。下一个写入操作将返回 -1,并将 `errno` 设置为 `ENOSPC`。要启用写入尾部,允许下一个写入操作继续,如果成功,则返回字节数。此后,将交替返回 -1 和字节数,直到遇到物理介质末端(或其他一些错误)。

模块参数

当驱动程序作为模块加载时,可以配置缓冲区大小、写入阈值和分配的最大缓冲区数。关键字为:

buffer_kbs=xxx

固定块模式下的缓冲区大小设置为 xxx 千字节。

write_threshold_kbs=xxx

写入阈值设置为 xxx 千字节。

max_sg_segs=xxx

最大分散/聚集段数。

try_direct_io=x

如果此值为非零,则尝试在用户缓冲区和磁带驱动器之间进行直接传输。

请注意,如果更改了缓冲区大小但未设置写入阈值,则写入阈值将设置为新的缓冲区大小 - 2 kB。

启动时配置

如果驱动程序编译到内核中,也可以使用例如 LILO 命令行设置相同的参数。首选语法是使用加载模块时使用的相同关键字,但在前面加上 ‘st.’。例如,要设置最大分散/聚集段数,应使用参数 ‘st.max_sg_segs=xx’(xx 是分散/聚集段数)。

为了兼容性,支持早期 2.5 和 2.4 内核版本的旧语法。可以使用与将驱动程序作为模块加载时相同的关键字。如果设置了多个参数,则关键字值对之间用逗号分隔(不允许有空格)。可以使用冒号代替等号。定义以字符串 `st=` 开头。这是一个示例:

st=buffer_kbs:64,write_threshold_kbs:60

还支持旧内核版本使用的以下语法:

st=aa[,bb[,dd]]

其中

  • aa 是固定块模式下的缓冲区大小,以 1024 字节为单位。

  • bb 是写入阈值,以 1024 字节为单位。

  • dd 是最大分散/聚集段数。

IOCTL

磁带定位和驱动器参数设置使用 `mtio.h` 中定义的 ioctl。磁带控制程序 `mt` 使用这些 ioctl。尝试找到一个支持所有 Linux SCSI 磁带 ioctl 的 mt,并且如果磁带内容将被修改(例如,查找 Linux ftp 站点上的 mt-st* 包;GNU mt 不会为例如擦除操作打开以进行写入)。

支持的 ioctl 为:

以下 ioctl 使用结构体 `mtop`:

MTFSF

向前跳过 `count` 个文件标记。磁带定位在文件标记之后。

MTFSFM

与上面相同,但磁带定位在文件标记之前。

MTBSF

向后跳过 `count` 个文件标记。磁带定位在文件标记之前。

MTBSFM

与上面相同,但磁带定位在文件标记之后。

MTFSR

向前跳过 `count` 个记录。

MTBSR

向后跳过 `count` 个记录。

MTFSS

向前跳过 `count` 个集合标记。

MTBSS

向后跳过 `count` 个集合标记。

MTWEOF

写入 `count` 个文件标记。

MTWEOFI

写入 `count` 个文件标记,并设置立即位(即,不等待数据写入磁带)。

MTWSM

写入 `count` 个集合标记。

MTREW

倒带磁带。

MTOFFL

将设备设置为离线(通常是倒带加弹出)。

MTNOP

除了刷新缓冲区之外,什么也不做。

MTRETEN

重新张紧磁带。

MTEOM

跳到已记录数据的末尾。

MTERASE

擦除磁带。如果参数为零,则使用短擦除命令。对于参数的所有其他值,使用长擦除命令。

MTSEEK

寻道到磁带块计数。对于 SCSI-1 驱动器,使用与 Tandberg 兼容的寻道 (QFA);对于 SCSI-2 驱动器,使用 SCSI-2 寻道。寻道后,状态中的文件和块号无效。

MTSETBLK

设置驱动器块大小。设置为零会将驱动器设置为可变块模式(如果适用)。

MTSETDENSITY

将驱动器密度代码设置为 `arg`。有关可用代码,请参阅驱动器文档。

MTLOCK 和 MTUNLOCK

显式锁定/解锁磁带驱动器门。

MTLOAD 和 MTUNLOAD

显式加载和卸载磁带。如果命令参数 x 介于 MT_ST_HPLOADER_OFFSET + 1 和 MT_ST_HPLOADER_OFFSET + 6 之间,则数字 x 会随命令一起发送到驱动器,并选择用于 HP C1553A 换带机的磁带槽。

MTCOMPRESSION

使用 SCSI 模式页 15 设置压缩或解压缩驱动器模式。请注意,一些驱动器使用其他方法来控制压缩。一些驱动器(例如 Exabytes)使用密度代码进行压缩控制。一些驱动器使用另一个模式页,但该页面尚未在驱动程序中实现。一些没有压缩功能的驱动器会接受任何压缩模式而不会出错。

MTSETPART

在下一次磁带操作时,将磁带移动到参数给定的分区。磁带定位的块是在新的活动分区中磁带先前定位的块,除非下一次磁带操作是 MTSEEK。在这种情况下,磁带将直接移动到 MTSEEK 指定的块。除非设置了 MT_ST_CAN_PARTITIONS,否则 MTSETPART 处于非活动状态。

MTMKPART

使用一个分区(参数为零)或两个分区(参数为非零)格式化磁带。如果参数为正数,则指定分区 1 的大小,单位为兆字节。对于 DDS 驱动器和一些早期的驱动器,这是磁带上物理上的第一个分区。如果参数为负数,则其绝对值指定分区 0 的大小,单位为兆字节。这是许多后期驱动器(例如 LTO-5 及更高版本的 LTO 驱动器)物理上的第一个分区。驱动器必须支持由发起者指定大小的分区。除非设置了 MT_ST_CAN_PARTITIONS,否则处于非活动状态。

MTSETDRVBUFFER

用于多种目的。该命令从带有掩码 MT_SET_OPTIONS 的计数中获得,低位用作参数。此命令仅允许超级用户(root)使用。子命令有:

  • 0

    驱动器缓冲区选项设置为参数。零表示不缓冲。

  • MT_ST_BOOLEANS

    设置缓冲选项。这些位是以下选项的新状态(启用/禁用)(括号中指定该选项是全局选项还是可以为每种模式指定不同的选项):

    MT_ST_BUFFER_WRITES

    写入缓冲(模式)

    MT_ST_ASYNC_WRITES

    异步写入(模式)

    MT_ST_READ_AHEAD

    预读(模式)

    MT_ST_TWO_FM

    写入两个文件标记(全局)

    MT_ST_FAST_EOM

    使用 SCSI 间距到 EOD(全局)

    MT_ST_AUTO_LOCK

    自动锁定驱动器门(全局)

    MT_ST_DEF_WRITES

    默认值仅适用于写入(模式)

    MT_ST_CAN_BSR

    可以使用后退多个记录来重新定位磁带(全局)

    MT_ST_NO_BLKLIMS

    驱动程序不从驱动器请求块限制(块大小只能更改为可变)(全局)

    MT_ST_CAN_PARTITIONS

    启用对分区磁带的支持(全局)

    MT_ST_SCSI2LOGICAL

    逻辑块号在 MTSEEK 和 MTIOCPOS 中用于 SCSI-2 驱动器,而不是设备相关地址。建议设置此标志,除非有使用设备相关地址(来自早期)的磁带(全局)

    MT_ST_SYSV

    设置 SYSV 语义(模式)

    MT_ST_NOWAIT

    为某些命令(例如,倒带)启用立即模式(即,不要等待命令完成)

    MT_ST_NOWAIT_EOF

    启用立即文件标记模式(即,当写入文件标记时,不要等待其完成)。请参阅关于 MTWEOFI 的 BASICS 注释,了解写入立即文件标记的潜在危险。

    MT_ST_SILI

    当在可变块模式下读取以提高读取块长度短于字节计数时的性能时,启用在 SCSI 命令中设置 SILI 位;仅当您确定驱动器支持 SILI 且 HBA 正确返回传输残余时才设置此位

    MT_ST_DEBUGGING

    调试(全局;调试必须编译到驱动程序中)

  • MT_ST_SETBOOLEANS,MT_ST_CLEARBOOLEANS

    设置或清除选项位。

  • MT_ST_WRITE_THRESHOLD

    将此设备的写入阈值设置为最低位指定的千字节。

  • MT_ST_DEF_BLKSIZE

    定义自动设置的默认块大小。值 0xffffff 表示不再使用默认值。

  • MT_ST_DEF_DENSITY,MT_ST_DEF_DRVBUFFER

    用于设置或清除密度(8 位)和驱动器缓冲区状态(3 位)。如果值为 MT_ST_CLEAR_DEFAULT(0xfffff),则不再使用默认值。否则,该值的最低位包含参数的新值。

  • MT_ST_DEF_COMPRESSION

    如果最低字节的值为 0xff,则不会使用压缩默认值。否则,最低位包含新的默认值。如果位 8-15 设置为非零数字,并且此数字不是 0xff,则该数字用作压缩算法。值 MT_ST_CLEAR_DEFAULT 可用于清除压缩默认值。

  • MT_ST_SET_TIMEOUT

    设置此设备的正常超时时间,以秒为单位。默认值为 900 秒(15 分钟)。超时时间应足够长,以便驱动器在读取/写入时进行重试。

  • MT_ST_SET_LONG_TIMEOUT

    设置用于已知需要长时间操作的长时间超时。默认值为 14000 秒(3.9 小时)。对于擦除操作,此值进一步乘以 8。

  • MT_ST_SET_CLN

    使用参数的最低 24 位设置清洁请求解释参数。如果从扩展的感知数据中找到清洁请求位模式,驱动程序可以设置通用状态位 GMT_CLN。当驱动器需要清洁时,许多驱动器会在扩展的感知数据中设置一个或多个位。这些位取决于设备。驱动程序会获得感知数据字节的编号(参数的最低八位;必须 >= 18(值 1-17 保留)且 <= 最大请求感知数据大小)、用于选择相关位的掩码(位 9-16)以及位模式(位 17-23)。如果位模式为零,则掩码下的一个或多个位表示清洁请求。如果模式为非零,则模式必须与屏蔽的感知数据字节匹配。

    (无论 MT_ST_SET_CLN 的设置如何,如果看到附加的感知代码和限定符 00h 17h,都会设置清洁位。)

以下 ioctl 使用结构 mtpos

MTIOCPOS

从驱动器读取当前位置。对 SCSI-1 驱动器使用 Tandberg 兼容的 QFA,对 SCSI-2 驱动器使用 SCSI-2 命令。

以下 ioctl 使用结构 mtget 返回状态

MTIOCGET

返回一些状态信息。将返回文件号和文件内的块号。当无法确定时(例如,在 MTBSF 之后),该块为 -1。驱动器类型为 MTISSCSI1 或 MTISSCSI2。自上次状态调用以来恢复的错误数存储在字段 mt_erreg 的低字中。当前块大小和密度代码存储在字段 mt_dsreg 中(子字段的偏移量为 MT_ST_BLKSIZE_SHIFT 和 MT_ST_DENSITY_SHIFT)。GMT_xxx 状态位反映驱动器状态。如果驱动器中没有磁带,则设置 GMT_DR_OPEN。GMT_EOD 表示已记录数据的末尾或磁带的末尾。GMT_EOT 表示磁带的末尾。

杂项编译选项

如果定义了 ST_RECOVERED_WRITE_FATAL,则恢复的写入错误被认为是致命的。

磁带设备的最大数量由定义 ST_MAX_TAPES 确定。如果在驱动程序初始化时检测到更多磁带,则会相应调整最大值。

可以通过定义 ST_NOWAIT 来启用从磁带定位 SCSI 命令立即返回。如果定义了此项,则用户应注意在先前的磁带操作完成之前,不要启动下一个磁带操作。驱动器和 SCSI 适配器应优雅地处理这种情况,但已知某些驱动器/适配器组合在这种情况下会挂起 SCSI 总线。

默认情况下,MTEOM 命令实现为间隔超过 32767 个文件标记。使用此方法,状态中的文件号是正确的。用户可以通过设置 ST_FAST_EOM 1(或使用 MT_ST_OPTIONS ioctl)来请求使用直接间隔到 EOD。在这种情况下,文件号将无效。

当使用预读或缓冲写入时,关闭文件后,文件中的位置可能不正确(正确的位置可能需要在多个记录上向后退)。如果在编译时定义了 ST_IN_FILE_POS 或通过 ioctl 为驱动器设置了 MT_ST_CAN_BSR 位,则可以获得文件中正确的位置。(如果用户没有请求那么远的数据,则驱动程序总是会退回预读跨越的文件标记。)

调试提示

调试代码现在默认编译,但调试通过内核模块参数 debug_flag 默认为 0 关闭。仍然可以使用 ioctl 打开和关闭调试。要在模块加载时启用调试,请将 debug_flag=1 添加到模块加载选项,调试输出不会太多。还可以通过向 sysfs 文件 /sys/bus/scsi/drivers/st/debug_flag 写入 '0'(禁用)或 '1'(启用)来启用和禁用调试。

如果磁带似乎挂起,我非常想知道驱动程序在等待什么。使用命令 'ps -l',您可以看到使用磁带的进程的状态。如果状态为 D,则进程正在等待某些内容。字段 WCHAN 指示驱动程序正在等待的位置。如果您在正确的位置(我使用的 procps 为 /boot)具有当前的 System.map 或已更新 /etc/psdatabase(用于 kmem ps),则 ps 会在 WCHAN 字段中写入函数名称。如果没有,您必须从 System.map 中查找函数。

另请注意,与大多数其他驱动程序相比,超时时间非常长。这意味着 Linux 驱动程序可能看起来挂起了,尽管真正的原因是磁带固件混淆了。