libATA 开发者指南

作者:

Jeff Garzik

简介

libATA 是 Linux 内核中使用的一个库,用于支持 ATA 主机控制器和设备。 libATA 提供了一个 ATA 驱动程序 API、用于 ATA 和 ATAPI 设备的类传输,以及根据 T10 SAT 规范为 ATA 设备提供的 SCSI<->ATA 转换。

本指南记录了 libATA 驱动程序 API、库函数、库内部结构,以及一些示例 ATA 底层驱动程序。

libata 驱动程序 API

struct ata_port_operations 是为每个底层 libata 硬件驱动程序定义的,它控制着底层驱动程序如何与 ATA 和 SCSI 层进行交互。

基于 FIS 的驱动程序将使用 ->qc_prep()->qc_issue() 高级钩子接入系统。 行为类似于 PCI IDE 硬件的硬件可以使用几个通用辅助函数,至少定义 ATA 影子寄存器块的总线 I/O 地址。

struct ata_port_operations

IDENTIFY 设备配置后

void (*dev_config) (struct ata_port *, struct ata_device *);

在向找到的每个设备发出 IDENTIFY [PACKET] DEVICE 后调用。 通常用于在发出 SET FEATURES - XFER MODE 之前和操作之前应用设备特定的修复。

此条目可以在 ata_port_operations 中指定为 NULL。

设置 PIO/DMA 模式

void (*set_piomode) (struct ata_port *, struct ata_device *);
void (*set_dmamode) (struct ata_port *, struct ata_device *);
void (*post_set_mode) (struct ata_port *);
unsigned int (*mode_filter) (struct ata_port *, struct ata_device *, unsigned int);

在发出 SET FEATURES - XFER MODE 命令之前调用的钩子。 可选的 ->mode_filter() 钩子在 libata 构建了可能模式的掩码时被调用。 这被传递给 ->mode_filter() 函数,该函数应在过滤掉因硬件限制而不合适的模式后,返回有效模式的掩码。 使用此接口添加模式是无效的。

当调用 ->set_piomode() 和调用 ->set_dmamode() 时,保证 dev->pio_modedev->dma_mode 有效。 此时,与电缆共享的任何其他驱动器的时序也将有效。 也就是说,库在尝试设置任何驱动器的模式之前,会记录通道上每个驱动器的模式的决策。

在 SET FEATURES - XFER MODE 命令成功完成后,无条件调用 ->post_set_mode()

->set_piomode() 始终被调用(如果存在),但仅在 DMA 可行时才调用 ->set_dma_mode()

Taskfile 读/写

void (*sff_tf_load) (struct ata_port *ap, struct ata_taskfile *tf);
void (*sff_tf_read) (struct ata_port *ap, struct ata_taskfile *tf);

调用 ->tf_load() 将给定的 taskfile 加载到硬件寄存器/DMA 缓冲区中。 调用 ->tf_read() 读取硬件寄存器/DMA 缓冲区,以获取当前的 taskfile 寄存器值集。 大多数基于 taskfile 的硬件(PIO 或 MMIO)的驱动程序都使用 ata_sff_tf_load()ata_sff_tf_read() 作为这些钩子。

PIO 数据读/写

void (*sff_data_xfer) (struct ata_device *, unsigned char *, unsigned int, int);

所有 bmdma 样式的驱动程序都必须实现此钩子。 这是在 PIO 数据传输期间实际复制数据字节的底层操作。 通常,驱动程序将选择 ata_sff_data_xfer()ata_sff_data_xfer32() 之一。

ATA 命令执行

void (*sff_exec_command)(struct ata_port *ap, struct ata_taskfile *tf);

使先前使用 ->tf_load() 加载的 ATA 命令在硬件中启动。 大多数基于 taskfile 的硬件的驱动程序都使用 ata_sff_exec_command() 作为此钩子。

每个 cmd ATAPI DMA 功能过滤器

int (*check_atapi_dma) (struct ata_queued_cmd *qc);

允许底层驱动程序过滤 ATA PACKET 命令,返回一个状态,指示是否可以使用 DMA 来处理提供的 PACKET 命令。

此钩子可以指定为 NULL,在这种情况下,libata 将假定可以支持 atapi dma。

读取特定的 ATA 影子寄存器

u8   (*sff_check_status)(struct ata_port *ap);
u8   (*sff_check_altstatus)(struct ata_port *ap);

从硬件读取 Status/AltStatus ATA 影子寄存器。 在某些硬件上,读取 Status 寄存器具有清除中断条件的副作用。 大多数基于 taskfile 的硬件的驱动程序都使用 ata_sff_check_status() 作为此钩子。

写入特定的 ATA 影子寄存器

void (*sff_set_devctl)(struct ata_port *ap, u8 ctl);

将设备控制 ATA 影子寄存器写入硬件。 大多数驱动程序不需要定义此项。

选择总线上的 ATA 设备

void (*sff_dev_select)(struct ata_port *ap, unsigned int device);

发出底层硬件命令,使 N 个硬件设备之一被视为 ATA 总线上“已选择”(激活且可用)的设备。 这通常对基于 FIS 的设备没有意义。

大多数基于 taskfile 的硬件的驱动程序都使用 ata_sff_dev_select() 作为此钩子。

私有调整方法

void (*set_mode) (struct ata_port *ap);

默认情况下,libata 根据 ATA 时序规则执行驱动器和控制器调整,并且应用黑名单和电缆限制。 有些控制器需要特殊处理,并且具有自定义调整规则,通常是使用 ATA 命令但不实际执行驱动器时序的 raid 控制器。

警告

当控制器存在怪癖时,不应使用此钩子替换标准控制器调整逻辑。 在这种情况下替换默认调整逻辑将绕过对数据可靠性可能很重要的驱动器和桥接怪癖的处理。 如果控制器需要过滤模式选择,则应改用 mode_filter 钩子。

控制 PCI IDE BMDMA 引擎

void (*bmdma_setup) (struct ata_queued_cmd *qc);
void (*bmdma_start) (struct ata_queued_cmd *qc);
void (*bmdma_stop) (struct ata_port *ap);
u8   (*bmdma_status) (struct ata_port *ap);

在设置 IDE BMDMA 事务时,这些钩子会准备 (->bmdma_setup)、启动 (->bmdma_start) 和停止 (->bmdma_stop) 硬件的 DMA 引擎。->bmdma_status 用于读取标准 PCI IDE DMA 状态寄存器。

这些钩子通常在基于 FIS 的驱动程序中要么是空操作,要么根本没有实现。

大多数旧版 IDE 驱动程序使用 ata_bmdma_setup() 作为 bmdma_setup() 钩子。ata_bmdma_setup() 会将 PRD 表的指针写入 IDE PRD 表地址寄存器,在 DMA 命令寄存器中启用 DMA,并调用 exec_command() 以开始传输。

大多数旧版 IDE 驱动程序使用 ata_bmdma_start() 作为 bmdma_start() 钩子。ata_bmdma_start() 会将 ATA_DMA_START 标志写入 DMA 命令寄存器。

许多旧版 IDE 驱动程序使用 ata_bmdma_stop() 作为 bmdma_stop() 钩子。ata_bmdma_stop() 会清除 DMA 命令寄存器中的 ATA_DMA_START 标志。

许多旧版 IDE 驱动程序使用 ata_bmdma_status() 作为 bmdma_status() 钩子。

高级 taskfile 钩子

enum ata_completion_errors (*qc_prep) (struct ata_queued_cmd *qc);
int (*qc_issue) (struct ata_queued_cmd *qc);

更高级别的钩子,这两个钩子可能会取代上面的几个 taskfile/DMA 引擎钩子。 在缓冲区已 DMA 映射后调用 ->qc_prep,通常用于填充硬件的 DMA 散射/收集表。 一些驱动程序使用标准 ata_bmdma_qc_prep()ata_bmdma_dumb_qc_prep() 辅助函数,但更高级的驱动程序会推出自己的函数。

一旦准备好了硬件和 S/G 表,->qc_issue 就用于使命令处于活动状态。 IDE BMDMA 驱动程序使用辅助函数 ata_sff_qc_issue() 进行基于 taskfile 协议的调度。 更高级的驱动程序会实现自己的 ->qc_issue

ata_sff_qc_issue() 根据需要调用 ->sff_tf_load()->bmdma_setup()->bmdma_start() 来启动传输。

异常和探测处理 (EH)

void (*freeze) (struct ata_port *ap);
void (*thaw) (struct ata_port *ap);

当 HSM 违规或其他条件中断端口的正常操作时,会调用 ata_port_freeze()。 在端口解冻之前,不允许冻结的端口执行任何操作,这通常是在成功重置之后。

可选的 ->freeze() 回调可用于以硬件方式冻结端口(例如,屏蔽中断并停止 DMA 引擎)。 如果无法以硬件方式冻结端口,则中断处理程序必须无条件地确认并清除中断,同时端口处于冻结状态。

调用可选的 ->thaw() 回调来执行与 ->freeze() 相反的操作:再次准备端口以进行正常操作。 取消屏蔽中断、启动 DMA 引擎等。

void (*error_handler) (struct ata_port *ap);

->error_handler() 是驱动程序连接到探测、热插拔、恢复和其他异常情况的钩子。 实现的主要责任是以一组 EH 钩子作为参数调用 ata_do_eh()ata_bmdma_drive_eh()

在任何其他操作执行之前,在 EH 重置期间调用“prereset”钩子(可能为 NULL)。

在执行 EH 重置之后调用“postreset”钩子(可能为 NULL)。 基于现有条件、问题的严重性和硬件功能,

将调用“softreset”(可能为 NULL)或“hardreset”(可能为 NULL)来执行底层 EH 重置。

void (*post_internal_cmd) (struct ata_queued_cmd *qc);

通过 ata_exec_internal() 执行探测时间或 EH 时间命令后,执行完成处理所需的任何特定于硬件的操作。

硬件中断处理

irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
void (*irq_clear) (struct ata_port *);

->irq_handler 是由 libata 向系统注册的中断处理例程。 在注册中断处理程序之前,在探测期间调用 ->irq_clear,以确保硬件处于静默状态。

第二个参数 dev_instance 应强制转换为指向 struct ata_host_set 的指针。

大多数旧版 IDE 驱动程序使用 ata_sff_interrupt() 作为 irq_handler 钩子,它会扫描 host_set 中的所有端口,确定哪个排队的命令处于活动状态(如果有),并调用 ata_sff_host_intr(ap,qc)。

大多数旧版 IDE 驱动程序使用 ata_sff_irq_clear() 作为 irq_clear() 钩子,它只是清除 DMA 状态寄存器中的中断和错误标志。

SATA phy 读/写

int (*scr_read) (struct ata_port *ap, unsigned int sc_reg,
         u32 *val);
int (*scr_write) (struct ata_port *ap, unsigned int sc_reg,
                   u32 val);

读取和写入标准 SATA phy 寄存器。 sc_reg 是 SCR_STATUS、SCR_CONTROL、SCR_ERROR 或 SCR_ACTIVE 之一。

初始化和关闭

int (*port_start) (struct ata_port *ap);
void (*port_stop) (struct ata_port *ap);
void (*host_stop) (struct ata_host_set *host_set);

在初始化每个端口的数据结构之后立即调用 ->port_start()。 通常,这用于分配每个端口的 DMA 缓冲区/表/环、启用 DMA 引擎和类似的任务。 一些驱动程序还使用此入口点来为 ap->private_data 分配驱动程序私有内存。

许多驱动程序使用 ata_port_start() 作为此钩子或从他们自己的 port_start() 钩子调用它。ata_port_start() 会为旧版 IDE PRD 表分配空间并返回。

->host_stop() 之后调用 ->port_stop()。 它的唯一功能是释放 DMA/内存资源,因为它们不再被主动使用。 许多驱动程序此时也会释放端口中的驱动程序私有数据。

在所有 ->port_stop() 调用完成后调用 ->host_stop()。 此钩子必须完成硬件关闭,释放 DMA 和其他资源等。此钩子可以指定为 NULL,在这种情况下不会调用它。

错误处理

本章介绍如何在 libata 下处理错误。 建议读者首先阅读 SCSI EH (SCSI EH) 和 ATA 异常文档。

命令的来源

在 libata 中,命令用 struct ata_queued_cmd 或 qc 表示。 qc 在端口初始化期间预先分配,并重复用于命令执行。 目前,每个端口仅分配一个 qc,但尚未合并的 NCQ 分支为每个标签分配一个 qc,并将每个 qc 映射到 NCQ 标签 1 对 1。

libata 命令可以来自两个来源 - libata 本身和 SCSI 中间层。 libata 内部命令用于初始化和错误处理。 所有正常的 blk 请求和 SCSI 仿真的命令都作为 SCSI 命令通过 SCSI 主机模板的 queuecommand 回调传递。

如何发出命令

内部命令

一旦分配,qc 的 taskfile 将被初始化以执行该命令。 qc 目前有两种机制来通知完成。 一种是通过 qc->complete_fn() 回调,另一种是完成 qc->waitingqc->complete_fn() 回调是正常 SCSI 转换命令使用的异步路径,而 qc->waiting 是内部命令使用的同步(发布者在进程上下文中休眠)路径。

一旦初始化完成,将获取 host_set 锁并发放 qc。

SCSI 命令

所有 libata 驱动程序都使用 ata_scsi_queuecmd() 作为 hostt->queuecommand 回调。 scmd 可以模拟或转换。 处理模拟的 scmd 不涉及任何 qc。 结果会立即计算并完成 scmd。

qc->complete_fn() 回调用于完成通知。 ATA 命令使用 ata_scsi_qc_complete(),而 ATAPI 命令使用 atapi_qc_complete()。 当 qc 完成时,这两个函数最终都会调用 qc->scsidone 来通知上层。 完成转换后,将使用 ata_qc_issue() 发出 qc。

请注意,SCSI 中间层在持有 host_set 锁时调用 hostt->queuecommand,因此以上所有操作都在持有 host_set 锁时发生。

如何处理命令

根据使用的协议和控制器,命令的处理方式有所不同。 为了便于讨论,假设使用 taskfile 接口和所有标准回调的控制器。

目前使用了 6 种 ATA 命令协议。 根据它们的处理方式,可以将它们分为以下四个类别。

ATA 无数据或 DMA

ATA_PROT_NODATA 和 ATA_PROT_DMA 属于此类别。 一旦发出,这些类型的命令不需要任何软件干预。 设备将在完成时引发中断。

ATA PIO

ATA_PROT_PIO 属于此类。libata 当前使用轮询来实现 PIO。设置 ATA_NIEN 位以关闭中断,并且 ata_wq 上的 pio_task 执行轮询和 IO。

ATAPI NODATA 或 DMA

ATA_PROT_ATAPI_NODATA 和 ATA_PROT_ATAPI_DMA 属于此类。packet_task 用于在发出 PACKET 命令后轮询 BSY 位。一旦设备关闭 BSY,packet_task 将传输 CDB 并将处理交给中断处理程序。

ATAPI PIO

ATA_PROT_ATAPI 属于此类。设置 ATA_NIEN 位,并且如 ATAPI NODATA 或 DMA 中一样,packet_task 提交 cdb。但是,在提交 cdb 后,进一步的处理(数据传输)将交给 pio_task。

命令如何完成

一旦发出,所有 qc 要么用 ata_qc_complete() 完成,要么超时。对于由中断处理的命令,ata_host_intr() 调用 ata_qc_complete(),而对于 PIO 任务,pio_task 调用 ata_qc_complete()。在错误情况下,packet_task 也可能完成命令。

ata_qc_complete() 执行以下操作。

  1. DMA 内存被取消映射。

  2. 从 qc->flags 中清除 ATA_QCFLAG_ACTIVE。

  3. 调用 qc->complete_fn 回调。如果回调的返回值不为零,则完成被短路并且 ata_qc_complete() 返回。

  4. 调用 __ata_qc_complete(),它执行

    1. qc->flags 被清除为零。

    2. ap->active_tagqc->tag 被 poisoned。

    3. qc->waiting 被清除并完成(按此顺序)。

    4. 通过清除 ap->qactive 中的相应位来释放 qc。

因此,它基本上通知上层并释放 qc。一个例外是 #3 中的短路路径,该路径由 atapi_qc_complete() 使用。

对于所有非 ATAPI 命令,无论它是否失败,几乎都采用相同的代码路径,并且几乎不进行错误处理。如果 qc 成功完成,则以成功状态完成;否则以失败状态完成。

但是,失败的 ATAPI 命令需要更多的处理,因为需要 REQUEST SENSE 来获取 sense 数据。如果 ATAPI 命令失败,则以错误状态调用 ata_qc_complete(),进而通过 qc->complete_fn() 回调调用 atapi_qc_complete()

这使得 atapi_qc_complete()scmd->result 设置为 SAM_STAT_CHECK_CONDITION,完成 scmd 并返回 1。由于 sense 数据为空但 scmd->result 为 CHECK CONDITION,因此 SCSI 中间层将为 scmd 调用 EH,并且返回 1 会使 ata_qc_complete() 返回而不释放 qc。这会将我们带到 ata_scsi_error(),并带有部分完成的 qc。

ata_scsi_error()

ata_scsi_error() 是 libata 当前的 transportt->eh_strategy_handler()。如上所述,这将在两种情况下输入 - 超时和 ATAPI 错误完成。此函数将检查 qc 是否处于活动状态且尚未失败。这样的 qc 将被标记为 AC_ERR_TIMEOUT,以便 EH 知道稍后处理它。然后它调用底层 libata 驱动程序的 error_handler() 回调。

调用 error_handler() 回调时,它会停止 BMDMA 并完成 qc。请注意,由于我们当前在 EH 中,因此无法调用 scsi_done。如 SCSI EH 文档中所述,恢复的 scmd 应使用 scsi_queue_insert() 重试或使用 scsi_finish_command() 完成。在这里,我们用 scsi_finish_command() 覆盖 qc->scsidone 并调用 ata_qc_complete()

如果 EH 是由于失败的 ATAPI qc 而调用的,则此处的 qc 将被完成但不会被释放。这种半完成的目的是使用 qc 作为占位符,以使 EH 代码到达此位置。这有点 hackish,但它有效。

一旦控制到达此处,就会通过显式调用 __ata_qc_complete() 来释放 qc。然后,发出 REQUEST SENSE 的内部 qc。一旦获取了 sense 数据,scmd 就会通过直接在 scmd 上调用 scsi_finish_command() 来完成。请注意,由于我们已经完成并释放了与 scmd 关联的 qc,因此我们不需要/无法再次调用 ata_qc_complete()

当前 EH 的问题

  • 错误表示过于粗糙。目前,任何和所有错误情况都用 ATA STATUS 和 ERROR 寄存器表示。非 ATA 设备错误通过设置 ATA_ERR 位被视为 ATA 设备错误。需要更好的错误描述符,可以正确表示 ATA 和其他错误/异常。

  • 处理超时时,没有采取任何措施使设备忘记超时的命令并为新命令做好准备。

  • 通过 ata_scsi_error() 进行 EH 处理没有受到通常命令处理的适当保护。在 EH 进入时,设备未处于静止状态。超时的命令可能随时成功或失败。pio_task 和 atapi_task 可能仍在运行。

  • 错误恢复太弱。导致 HSM 不匹配错误和其他错误的设备/控制器通常需要重置才能返回到已知状态。此外,需要高级错误处理来支持 NCQ 和热插拔等功能。

  • ATA 错误直接在中断处理程序中处理,PIO 错误在 pio_task 中处理。由于以下原因,这对于高级错误处理是有问题的。

    首先,高级错误处理通常需要上下文和内部 qc 执行。

    其次,即使是一个简单的故障(例如,CRC 错误)也需要信息收集,并且可能触发复杂的错误处理(例如,重置和重新配置)。拥有多个代码路径来收集信息、进入 EH 和触发操作会使生活变得痛苦。

    第三,分散的 EH 代码使得实现底层驱动程序变得困难。底层驱动程序覆盖 libata 回调。如果 EH 分散在多个位置,则每个受影响的回调都应执行其部分的错误处理。这可能容易出错且痛苦。

libata 库

链接迭代助手

参数

struct ata_link *link

上一个链接,NULL 表示开始

struct ata_port *ap

包含要迭代的链接的 ATA 端口

enum ata_link_iter_mode mode

迭代模式,ATA_LITER_* 之一

锁定:主机锁或 EH 上下文。

返回

指向下一个链接的指针。

struct ata_device *ata_dev_next(struct ata_device *dev, struct ata_link *link, enum ata_dev_iter_mode mode)

设备迭代助手

参数

struct ata_device *dev

上一个设备,NULL 表示开始

struct ata_link *link

包含要迭代的设备的 ATA 链接

enum ata_dev_iter_mode mode

迭代模式,ATA_DITER_* 之一

锁定:主机锁或 EH 上下文。

返回

指向下一个设备的指针。

int atapi_cmd_type(u8 opcode)

从 SCSI 操作码确定 ATAPI 命令类型

参数

u8 opcode

SCSI 操作码

从 **opcode** 确定 ATAPI 命令类型。

锁定:无。

返回

ATAPI_{READ|WRITE|READ_CD|PASS_THRU|MISC}

unsigned int ata_pack_xfermask(unsigned int pio_mask, unsigned int mwdma_mask, unsigned int udma_mask)

将 pio、mwdma 和 udma 掩码打包到 xfer_mask 中

参数

unsigned int pio_mask

pio_mask

unsigned int mwdma_mask

mwdma_mask

unsigned int udma_mask

udma_mask

将 **pio_mask**、**mwdma_mask** 和 **udma_mask** 打包到单个无符号 int xfer_mask 中。

锁定:无。

返回

打包的 xfer_mask。

u8 ata_xfer_mask2mode(unsigned int xfer_mask)

为给定的 xfer_mask 查找匹配的 XFER_*

参数

unsigned int xfer_mask

感兴趣的 xfer_mask

返回与 **xfer_mask** 匹配的 XFER_* 值。仅考虑 **xfer_mask** 的最高位。

锁定:无。

返回

匹配的 XFER_* 值,如果未找到匹配项,则为 0xff。

unsigned int ata_xfer_mode2mask(u8 xfer_mode)

为 XFER_* 查找匹配的 xfer_mask

参数

u8 xfer_mode

感兴趣的 XFER_*

返回与 **xfer_mode** 匹配的 xfer_mask。

锁定:无。

返回

匹配的 xfer_mask,如果未找到匹配项,则为 0。

int ata_xfer_mode2shift(u8 xfer_mode)

为 XFER_* 查找匹配的 xfer_shift

参数

u8 xfer_mode

感兴趣的 XFER_*

返回与 **xfer_mode** 匹配的 xfer_shift。

锁定:无。

返回

匹配的 xfer_shift,如果未找到匹配项,则为 -1。

const char *ata_mode_string(unsigned int xfer_mask)

将 xfer_mask 转换为字符串

参数

unsigned int xfer_mask

支持的位掩码;仅最高位计数。

确定表示最高速度(**modemask** 中的最高位)的字符串。

锁定:无。

返回

表示 **mode_mask** 中列出的最高速度的常量 C 字符串,或常量 C 字符串“<n/a>”。

unsigned int ata_dev_classify(const struct ata_taskfile *tf)

根据 ATA 规范签名确定设备类型

参数

const struct ata_taskfile *tf

要标识的设备的 ATA taskfile 寄存器集

根据 taskfile 寄存器内容确定设备是 ATA 还是 ATAPI,根据 ATA/PI 规范的“签名和持久性”部分(第 1 卷,第 5.14 节)。

锁定:无。

返回

设备类型,ATA_DEV_ATAATA_DEV_ATAPIATA_DEV_PMPATA_DEV_ZACATA_DEV_UNKNOWN(如果失败)。

void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len)

将 IDENTIFY DEVICE 页面转换为字符串

参数

const u16 *id

我们将检查的 IDENTIFY DEVICE 结果

unsigned char *s

数据输出到的字符串

unsigned int ofs

identify device 页面中的偏移量

unsigned int len

要返回的字符串的长度。必须是偶数。

IDENTIFY DEVICE 页面中的字符串被分解为 16 位块。遍历字符串,并线性输出每个 8 位块,而不管平台如何。

锁定:调用者。

void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len)

将 IDENTIFY DEVICE 页面转换为 C 字符串

参数

const u16 *id

我们将检查的 IDENTIFY DEVICE 结果

unsigned char *s

数据输出到的字符串

unsigned int ofs

identify device 页面中的偏移量

unsigned int len

要返回的字符串的长度。必须是奇数。

此函数与 ata_id_string 相同,只是它会修剪尾随空格并以 null 终止生成的字符串。**len** 必须是实际最大长度(偶数)+ 1。

锁定:调用者。

unsigned int ata_id_xfermask(const u16 *id)

从给定的 IDENTIFY 数据计算 xfermask

参数

const u16 *id

用于计算 xfer 掩码的 IDENTIFY 数据

计算此设备的 xfermask。如果必须正确考虑早期设备,这并不像看起来那么简单。

FIXME: pre IDE 驱动器计时(我们在乎吗?)。

锁定:无。

返回

计算的 xfermask

unsigned int ata_pio_need_iordy(const struct ata_device *adev)

检查是否需要 IORDY

参数

const struct ata_device *adev

ATA 设备

检查设备的当前速度是否需要 IORDY。由各种控制器用于芯片配置。

unsigned int ata_do_dev_read_id(struct ata_device *dev, struct ata_taskfile *tf, __le16 *id)

默认 ID 读取方法

参数

struct ata_device *dev

设备

struct ata_taskfile *tf

建议的任务文件

__le16 *id

数据缓冲区

发出识别任务文件并返回包含识别数据的缓冲区。对于某些 RAID 控制器和 pre ATA 设备,此函数由驱动程序包装或替换

int ata_cable_40wire(struct ata_port *ap)

返回 40 线电缆类型

参数

struct ata_port *ap

端口

用于想要硬连接 40 线电缆检测的驱动程序的辅助方法。

int ata_cable_80wire(struct ata_port *ap)

返回 80 线电缆类型

参数

struct ata_port *ap

端口

用于想要硬连接 80 线电缆检测的驱动程序的辅助方法。

int ata_cable_unknown(struct ata_port *ap)

返回未知的 PATA 电缆。

参数

struct ata_port *ap

端口

用于没有 PATA 电缆检测的驱动程序的辅助方法。

int ata_cable_ignore(struct ata_port *ap)

返回忽略的 PATA 电缆。

参数

struct ata_port *ap

端口

用于不使用电缆类型来限制传输模式的驱动程序的辅助方法。

int ata_cable_sata(struct ata_port *ap)

返回 SATA 电缆类型

参数

struct ata_port *ap

端口

用于具有 SATA 电缆的驱动程序的辅助方法

struct ata_device *ata_dev_pair(struct ata_device *adev)

返回电缆上的另一个设备

参数

struct ata_device *adev

设备

获取同一电缆上的另一个设备,如果没有,则返回 NULL

int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)

编程定时并发出 SET FEATURES - XFER

参数

struct ata_link *link

将在其上编程定时的链路

struct ata_device **r_failed_dev

失败设备的输出参数

用于调整和设置 ATA 设备磁盘传输模式(PIO3、UDMA6 等)的函数的标准实现。如果 ata_dev_set_mode() 失败,则指向失败设备的指针将返回到 **r_failed_dev** 中。

锁定:PCI/etc. 总线探测 sem。

返回

成功时为 0,否则为负 errno

int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link))

复位后等待链路变为就绪

参数

struct ata_link *link

要等待的链接

unsigned long deadline

操作的截止时间 jiffies

int (*check_ready)(struct ata_link *link)

用于检查链路就绪情况的回调

等待 **link** 在重置后变为就绪状态。

锁定:EH 上下文。

返回

如果 **link** 在 **deadline** 之前准备就绪,则为 0;否则,为 -errno。

int ata_std_prereset(struct ata_link *link, unsigned long deadline)

准备重置

参数

struct ata_link *link

要重置的 ATA 链接

unsigned long deadline

操作的截止时间 jiffies

**link** 即将被重置。初始化它。来自 prereset 的失败使 libata 中止整个重置序列并放弃该端口,因此 prereset 应该是尽力而为的。它尽最大努力为重置序列做准备,但如果出现问题,它应该只是抱怨,而不是失败。

锁定:内核线程上下文(可能会休眠)

返回

始终为 0。

void ata_std_postreset(struct ata_link *link, unsigned int *classes)

标准 postreset 回调

参数

struct ata_link *link

目标 ata_link

unsigned int *classes

连接设备的类

在成功重置后调用此函数。请注意,在使用不同的重置方法调用 postreset 之前,设备可能已被重置多次。

锁定:内核线程上下文(可能会休眠)

unsigned int ata_dev_set_feature(struct ata_device *dev, u8 subcmd, u8 action)

发出 SET FEATURES

参数

struct ata_device *dev

将向其发送命令的设备

u8 subcmd

要发送的 SET FEATURES 子命令

u8 action

扇区计数表示子命令特定操作

使用扇区计数向端口 **ap** 上的设备 **dev** 发出 SET FEATURES 命令

锁定:PCI/etc. 总线探测 sem。

返回

成功时为 0,否则为 AC_ERR_* 掩码。

int ata_std_qc_defer(struct ata_queued_cmd *qc)

检查是否需要延迟 qc

参数

struct ata_queued_cmd *qc

有问题的 ATA 命令

非 NCQ 命令不能与任何其他命令(NCQ 或非 NCQ)一起运行。由于上层只知道队列深度,因此我们负责维护排除。此函数检查是否可以发出新命令 **qc**。

锁定:spin_lock_irqsave(主机锁)

返回

如果需要延迟,则为 ATA_DEFER_*,否则为 0。

void ata_qc_complete(struct ata_queued_cmd *qc)

完成活动的 ATA 命令

参数

struct ata_queued_cmd *qc

要完成的命令

向中间层和上层表明 ATA 命令已完成,状态为 ok 或 not-ok。

成功完成多个 NCQ 命令时,请避免多次调用此函数。应改用 ata_qc_complete_multiple(),它将正确更新 IRQ 期望状态。

锁定:spin_lock_irqsave(主机锁)

u64 ata_qc_get_active(struct ata_port *ap)

获取活动 qcs 的位掩码

参数

struct ata_port *ap

有问题的端口

锁定:spin_lock_irqsave(主机锁)

返回

活动 qcs 的位掩码

测试给定的链路是否在线

参数

struct ata_link *link

要测试的 ATA 链路

测试 **link** 是否在线。当没有从属链路时,这与 ata_phys_link_online() 相同。当存在从属链路时,此函数应仅在主链路上调用,并且如果任何 M/S 链路在线,则将返回 true。

锁定:无。

返回

如果端口在线状态可用且在线,则为 True。

测试给定的链路是否脱机

参数

struct ata_link *link

要测试的 ATA 链路

测试 **link** 是否脱机。当没有从属链路时,这与 ata_phys_link_offline() 相同。当存在从属链路时,此函数应仅在主链路上调用,并且如果 M/S 链路都脱机,则将返回 true。

锁定:无。

返回

如果端口脱机状态可用且脱机,则为 True。

void ata_host_suspend(struct ata_host *host, pm_message_t mesg)

挂起主机

参数

struct ata_host *host

要挂起的主机

pm_message_t mesg

PM 消息

挂起 **host**。实际操作由端口挂起执行。

void ata_host_resume(struct ata_host *host)

恢复主机

参数

struct ata_host *host

要恢复的主机

恢复 **host**。实际操作由端口恢复执行。

struct ata_port *ata_port_alloc(struct ata_host *host)

分配和初始化基本 ATA 端口资源

参数

struct ata_host *host

此已分配端口所属的 ATA 主机

分配和初始化基本 ATA 端口资源。

返回

成功时分配 ATA 端口,失败时分配 NULL。

锁定:从调用层继承(可能会休眠)。

struct ata_host *ata_host_alloc(struct device *dev, int n_ports)

分配和初始化基本 ATA 主机资源

参数

struct device *dev

此主机与之关联的通用设备

int n_ports

与此主机关联的 ATA 端口数

分配和初始化基本 ATA 主机资源。LLD 调用此函数来分配主机,完全初始化它,并使用 ata_host_register() 连接它。

返回

成功时分配 ATA 主机,失败时分配 NULL。

锁定:从调用层继承(可能会休眠)。

struct ata_host *ata_host_alloc_pinfo(struct device *dev, const struct ata_port_info *const *ppi, int n_ports)

分配主机并使用 port_info 数组初始化

参数

struct device *dev

此主机与之关联的通用设备

const struct ata_port_info * const * ppi

用于初始化主机的 ATA port_info 数组

int n_ports

连接到此主机的 ATA 端口数

分配 ATA 主机并使用来自 **ppi** 的信息进行初始化。如果以 NULL 结尾,则 **ppi** 可能包含比 **n_ports** 更少的条目。最后一个条目将用于剩余的端口。

返回

成功时分配 ATA 主机,失败时分配 NULL。

锁定:从调用层继承(可能会休眠)。

int ata_host_start(struct ata_host *host)

启动并冻结 ATA 主机的端口

参数

struct ata_host *host

要为其启动端口的 ATA 主机

启动并冻结 host 的端口。 启动状态记录在 host->flags 中,因此可以多次调用此函数。 端口保证只启动一次。 如果 host->ops 尚未初始化,则会将其设置为第一个非虚拟端口操作。

锁定:从调用层继承(可能会休眠)。

返回

如果所有端口都成功启动,则返回 0;否则返回 -errno。

void ata_host_init(struct ata_host *host, struct device *dev, struct ata_port_operations *ops)

为 sas 初始化主机结构(ipr、libsas)

参数

struct ata_host *host

要初始化的主机

struct device *dev

主机连接到的设备

struct ata_port_operations *ops

port_ops

int ata_host_register(struct ata_host *host, const struct scsi_host_template *sht)

注册已初始化的 ATA 主机

参数

struct ata_host *host

要注册的 ATA 主机

const struct scsi_host_template *sht

SCSI 主机的模板

注册已初始化的 ATA 主机。 host 是使用 ata_host_alloc() 分配的,并由 LLD 完全初始化。 此函数启动端口,将 host 注册到 ATA 和 SCSI 层,并探测注册的设备。

锁定:从调用层继承(可能会休眠)。

返回

成功时返回 0,否则返回 -errno。

int ata_host_activate(struct ata_host *host, int irq, irq_handler_t irq_handler, unsigned long irq_flags, const struct scsi_host_template *sht)

启动主机,请求 IRQ 并注册它

参数

struct ata_host *host

目标 ATA 主机

int irq

要请求的 IRQ

irq_handler_t irq_handler

请求 IRQ 时使用的 irq_handler

unsigned long irq_flags

请求 IRQ 时使用的 irq_flags

const struct scsi_host_template *sht

注册主机时要使用的 scsi_host_template

在分配 ATA 主机并初始化它之后,大多数 libata LLD 执行三个步骤来激活主机 - 启动主机、请求 IRQ 并注册它。 此助手获取必要的参数并一次性执行这三个步骤。

无效的 IRQ 会跳过 IRQ 注册,并期望主机在端口上设置轮询模式。 在这种情况下,irq_handler 应该为 NULL。

锁定:从调用层继承(可能会休眠)。

返回

成功时返回 0,否则返回 -errno。

void ata_host_detach(struct ata_host *host)

分离 ATA 主机的所有端口

参数

struct ata_host *host

要分离的主机

分离 host 的所有端口。

锁定:内核线程上下文(可能会休眠)。

void ata_pci_remove_one(struct pci_dev *pdev)

用于设备移除的 PCI 层回调

参数

struct pci_dev *pdev

已移除的 PCI 设备

PCI 层通过此挂钩向 libata 指示已发生热插拔或模块卸载事件。 分离所有端口。 资源释放通过 devres 处理。

锁定:继承自 PCI 层(可能会休眠)。

void ata_platform_remove_one(struct platform_device *pdev)

用于设备移除的平台层回调

参数

struct platform_device *pdev

已移除的平台设备

平台层通过此挂钩向 libata 指示已发生热插拔或模块卸载事件。 分离所有端口。 资源释放通过 devres 处理。

锁定:继承自平台层(可能会休眠)。

void ata_msleep(struct ata_port *ap, unsigned int msecs)

ATA EH 所有者感知型 msleep

参数

struct ata_port *ap

要将休眠归属到的 ATA 端口

unsigned int msecs

以毫秒为单位的休眠持续时间

休眠 msecs。 如果当前任务是 ap 的 EH 的所有者,则在进入休眠状态之前释放所有权,并在休眠完成后重新获取所有权。 换句话说,允许共享 ap->host 的其他端口在此任务休眠时拥有 EH。

锁定:可能会休眠。

u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val, unsigned int interval, unsigned int timeout)

等待寄存器值更改

参数

struct ata_port *ap

要等待寄存器的 ATA 端口,可以为 NULL

void __iomem *reg

IO 映射的寄存器

u32 mask

应用于读取寄存器值的掩码

u32 val

等待条件

unsigned int interval

以毫秒为单位的轮询间隔

unsigned int timeout

以毫秒为单位的超时时间

等待寄存器的某些位更改是 ATA 控制器的常见操作。 此函数读取 32 位 LE IO 映射的寄存器 reg,并测试以下条件。

(*reg & mask) != val

如果满足条件,则返回;否则,在 interval_msec 后重复该过程,直到超时。

锁定:内核线程上下文(可能会休眠)

返回

最终寄存器值。

libata 核心内部

查找设备的物理链路

参数

struct ata_device *dev

要查找物理链路的 ATA 设备

查找 dev 连接到的物理链路。 请注意,这仅在 dev 位于从属链路上时才与 dev->link 不同。 对于所有其他情况,它与 dev->link 相同。

锁定:无所谓。

返回

指向找到的物理链路的指针。

void ata_force_cbl(struct ata_port *ap)

根据 libata.force 强制电缆类型

参数

struct ata_port *ap

感兴趣的 ATA 端口

根据 libata.force 强制电缆类型并对此发出警告。 使用具有匹配端口号的最后一个条目,因此可以将其指定为设备强制参数的一部分。 例如,“a:40c,1.00:udma4”和“1.00:40c,udma4”具有相同的效果。

锁定:EH 上下文。

void ata_force_pflags(struct ata_port *ap)

根据 libata.force 强制端口标志

参数

struct ata_port *ap

感兴趣的 ATA 端口

根据 libata.force 强制端口标志并对此发出警告。

锁定:EH 上下文。

根据 libata.force 强制链路限制

参数

struct ata_link *link

感兴趣的 ATA 链路

根据 libata.force 强制链路标志和 SATA spd 限制并对此发出警告。 仅指定端口部分(例如 1:)时,该限制适用于连接到主机链路和所有通过 PMP 连接的扇出端口的所有链路。 如果设备部分指定为 0(例如 1.00:),则它指定第一个扇出链路,而不是主机链路。 设备编号 15 始终指向主机链路,无论是否连接 PMP。 如果控制器有从属链路,则设备编号 16 指向它。

锁定:EH 上下文。

void ata_force_xfermask(struct ata_device *dev)

根据 libata.force 强制 xfermask

参数

struct ata_device *dev

感兴趣的 ATA 设备

根据 libata.force 强制 xfer_mask 并对此发出警告。 为了与链路选择保持一致,设备编号 15 选择连接到主机链路的第一个设备。

锁定:EH 上下文。

void ata_force_quirks(struct ata_device *dev)

根据 libata.force 强制 quirks

参数

struct ata_device *dev

感兴趣的 ATA 设备

根据 libata.force 强制 quirks 并对此发出警告。 为了与链路选择保持一致,设备编号 15 选择连接到主机链路的第一个设备。

锁定:EH 上下文。

bool ata_set_rwcmd_protocol(struct ata_device *dev, struct ata_taskfile *tf)

设置 taskfile r/w 命令和协议

参数

struct ata_device *dev

taskfile 的目标设备

struct ata_taskfile *tf

要检查和配置的 taskfile

检查设备配置和 tf->flags 以确定用于 tf 的正确读取/写入命令和协议。

锁定:调用者。

u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)

从 ATA taskfile 读取块地址

参数

const struct ata_taskfile *tf

感兴趣的 ATA taskfile

struct ata_device *dev

tf 所属的 ATA 设备

锁定:无。

tf 读取块地址。 此函数可以处理所有三种地址格式 - LBA、LBA48 和 CHS。 tf->protocol 和 flags 选择要使用的地址格式。

返回

tf 读取的块地址。

int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block, unsigned int tf_flags, int cdl, int class)

为给定的读/写请求构建 ATA taskfile

参数

struct ata_queued_cmd *qc

与要构建的 taskfile 关联的元数据

u64 block

块地址

u32 n_block

块数

unsigned int tf_flags

RW/FUA 等...

int cdl

命令持续时间限制索引

int class

IO 优先级类

锁定:无。

为命令 qc 构建 ATA taskfile,用于 blockn_blocktf_flagsclass 描述的读取/写入请求。

返回

成功时返回 0,如果请求对于 dev 来说太大,则返回 -ERANGE,如果请求无效,则返回 -EINVAL。

void ata_unpack_xfermask(unsigned int xfer_mask, unsigned int *pio_mask, unsigned int *mwdma_mask, unsigned int *udma_mask)

将 xfer_mask 解包为 pio、mwdma 和 udma 掩码

参数

unsigned int xfer_mask

要解包的 xfer_mask

unsigned int *pio_mask

生成的 pio_mask

unsigned int *mwdma_mask

生成的 mwdma_mask

unsigned int *udma_mask

生成的 udma_mask

xfer_mask 解包为 pio_maskmwdma_maskudma_mask。 将忽略任何 NULL 目标掩码。

int ata_read_native_max_address(struct ata_device *dev, u64 *max_sectors)

读取本机最大地址

参数

struct ata_device *dev

目标设备

u64 *max_sectors

用于返回原生最大扇区地址的输出参数

对目标设备执行 LBA48 或 LBA28 原生大小查询。

返回

成功时返回 0,如果命令被驱动器中止则返回 -EACCES,其他错误返回 -EIO。

int ata_set_max_sectors(struct ata_device *dev, u64 new_sectors)

设置最大扇区数

参数

struct ata_device *dev

目标设备

u64 new_sectors

要为设备设置的新的最大扇区数值

dev 的最大扇区数设置为 new_sectors

返回

成功时返回 0,如果命令被驱动器中止或拒绝(由于先前非易失性的 SET_MAX 命令)则返回 -EACCES,其他错误返回 -EIO。

int ata_hpa_resize(struct ata_device *dev)

调整设置了 HPA 的设备大小

参数

struct ata_device *dev

要调整大小的设备

读取具有 HPA 功能的 LBA28 或 LBA48 磁盘的大小,并在需要时将其调整为介质的完整大小。 调用者必须检查驱动器是否已启用 HPA 功能集。

返回

成功时返回 0,失败时返回 -errno。

void ata_dump_id(struct ata_device *dev, const u16 *id)

IDENTIFY DEVICE 信息调试输出

参数

struct ata_device *dev

从中获取信息的设备

const u16 *id

要转储的 IDENTIFY DEVICE 页面

从给定的 IDENTIFY DEVICE 页面转储选定的 16 位字。

锁定:调用者。

unsigned int ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, enum dma_data_direction dma_dir, void *buf, unsigned int buflen, unsigned int timeout)

执行 libata 内部命令

参数

struct ata_device *dev

将命令发送到的设备

struct ata_taskfile *tf

命令的任务文件寄存器和结果

const u8 *cdb

数据包命令的 CDB

enum dma_data_direction dma_dir

命令的数据传输方向

void *buf

命令的数据缓冲区

unsigned int buflen

数据缓冲区的长度

unsigned int timeout

超时时间,以毫秒为单位(0 表示默认值)

使用超时时间执行 libata 内部命令。tf 包含输入时的命令和返回时的结果。 超时和错误情况通过返回值报告。 超时后不会采取恢复措施。 调用者有责任在超时后进行清理。

锁定:无。 应该在内核上下文中调用,可能会睡眠。

返回

成功时返回 0,失败时返回 AC_ERR_* 掩码

u32 ata_pio_mask_no_iordy(const struct ata_device *adev)

返回非 IORDY 掩码

参数

const struct ata_device *adev

ATA 设备

如果未使用 iordy,则计算可能的最高模式。 如果没有可用的 iordy 模式,则返回 -1。

int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, unsigned int flags, u16 *id)

从指定设备读取 ID 数据

参数

struct ata_device *dev

目标设备

unsigned int *p_class

指向目标设备类别的指针(可能会更改)

unsigned int flags

ATA_READID_* 标志

u16 *id

用于读取 IDENTIFY 数据的缓冲区

从指定设备读取 ID 数据。 在 ATA 设备上执行 ATA_CMD_ID_ATA,在 ATAPI 设备上执行 ATA_CMD_ID_ATAPI。 此函数还会为 ATA4 之前的驱动器发出 ATA_CMD_INIT_DEV_PARAMS。

FIXME:ATA_CMD_ID_ATA 对于早期驱动器是可选的,如果遇到这种情况,我们现在会中止。

锁定:内核线程上下文(可能会休眠)

返回

成功时返回 0,否则返回 -errno。

void ata_dev_power_set_standby(struct ata_device *dev)

将设备电源模式设置为待机模式

参数

struct ata_device *dev

目标设备

发出 STANDBY IMMEDIATE 命令以将设备电源模式设置为待机模式。 对于 HDD 设备,这将停止磁盘旋转。

锁定:内核线程上下文(可能会休眠)。

void ata_dev_power_set_active(struct ata_device *dev)

将设备电源模式设置为活动模式

参数

struct ata_device *dev

目标设备

发出 VERIFY 命令以进入确保设备处于活动电源模式。 对于停止旋转的 HDD(待机或空闲电源模式),VERIFY 命令将在磁盘启动后完成。

锁定:内核线程上下文(可能会休眠)。

unsigned int ata_read_log_page(struct ata_device *dev, u8 log, u8 page, void *buf, unsigned int sectors)

读取特定的日志页面

参数

struct ata_device *dev

目标设备

u8 log

要读取的日志

u8 page

要读取的页面

void *buf

用于存储读取页面的缓冲区

unsigned int sectors

要读取的扇区数

使用 READ_LOG_EXT 命令读取日志页面。

锁定:内核线程上下文(可能会休眠)。

返回

成功时为 0,否则为 AC_ERR_* 掩码。

int ata_dev_configure(struct ata_device *dev)

配置指定的 ATA/ATAPI 设备

参数

struct ata_device *dev

要配置的目标设备

根据 dev->id 配置 dev。 还会应用通用和低级别驱动程序特定的修复。

锁定:内核线程上下文(可能会休眠)

返回

成功时返回 0,否则返回 -errno

打印 SATA 链路状态

参数

struct ata_link *link

要打印链路状态的 SATA 链路

此函数打印 SATA 链路的链路速度和状态。

锁定:无。

u8 ata_timing_cycle2mode(unsigned int xfer_shift, int cycle)

查找指定周期持续时间的 xfer 模式

参数

unsigned int xfer_shift

要检查的传输类型的 ATA_SHIFT_* 值。

int cycle

周期持续时间,以纳秒为单位

返回 cycle 的匹配 xfer 模式。 返回的模式是由 xfer_shift 指定的传输类型。 如果 cycle 对于 xfer_shift 而言太慢,则返回 0xff。 如果 cycle 比已知的最快模式更快,则返回最快模式。

锁定:无。

返回

匹配的 xfer_mode,如果未找到匹配项,则返回 0xff。

int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel)

向下调整 dev xfer 掩码

参数

struct ata_device *dev

要调整 xfer 掩码的设备

unsigned int sel

ATA_DNXFER_* 选择器

向下调整 dev 的 xfer 掩码。 请注意,此函数不会应用更改。 之后调用 ata_set_mode() 将应用限制。

锁定:从调用方继承。

返回

成功时返回 0,失败时返回负 errno

int ata_wait_ready(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link))

等待链路变为就绪

参数

struct ata_link *link

要等待的链接

unsigned long deadline

操作的截止时间 jiffies

int (*check_ready)(struct ata_link *link)

用于检查链路就绪情况的回调

等待 link 变为就绪。 如果 link 就绪,check_ready 应返回正数,如果未就绪,则返回 0,如果链路似乎未被占用,则返回 -ENODEV,其他错误情况返回其他 errno。

ATA_TMOUT_FF_WAIT 允许瞬态 -ENODEV 情况。

锁定:EH 上下文。

返回

如果 **link** 在 **deadline** 之前准备就绪,则为 0;否则,为 -errno。

int ata_dev_same_device(struct ata_device *dev, unsigned int new_class, const u16 *new_id)

确定新 ID 是否与已配置的设备匹配

参数

struct ata_device *dev

要比较的设备

unsigned int new_class

新设备的类别

const u16 *new_id

新设备的 IDENTIFY 页面

new_classnew_iddev 进行比较,并确定 dev 是否是由 new_classnew_id 指示的设备。

锁定:无。

返回

如果 devnew_classnew_id 匹配,则返回 1,否则返回 0。

int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags)

重新读取 IDENTIFY 数据

参数

struct ata_device *dev

目标 ATA 设备

unsigned int readid_flags

读取 ID 标志

重新读取 IDENTIFY 页面并确保 dev 仍然连接到端口。

锁定:内核线程上下文(可能会休眠)

返回

成功时为 0,否则为负 errno

int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, unsigned int readid_flags)

重新验证 ATA 设备

参数

struct ata_device *dev

要重新验证的设备

unsigned int new_class

新的类别代码

unsigned int readid_flags

读取 ID 标志

重新读取 IDENTIFY 页面,确保 dev 仍然连接到端口,并根据新的 IDENTIFY 页面重新配置它。

锁定:内核线程上下文(可能会休眠)

返回

成功时为 0,否则为负 errno

int ata_is_40wire(struct ata_device *dev)

检查驱动器侧检测

参数

struct ata_device *dev

设备

执行驱动器侧检测解码,允许无法遵循文档的设备供应商这样做。

int cable_is_40wire(struct ata_port *ap)

40/80/SATA 决定器

参数

struct ata_port *ap

要考虑的端口

此函数将速度管理策略封装在一个位置。 目前,我们不缓存结果,但是当我们使用未知电缆调用时,有一个很好的理由将 ap->cbl 设置为结果(并弄清楚它是否会影响热插拔)。

如果电缆似乎是 40 线,则返回 1。

void ata_dev_xfermask(struct ata_device *dev)

计算给定设备支持的 xfermask

参数

struct ata_device *dev

要计算 xfermask 的设备

计算 dev 支持的 xfermask 并将其存储在 dev->*_mask 中。 此函数负责应用所有已知的限制,包括主机控制器限制、设备特性等...

锁定:无。

unsigned int ata_dev_set_xfermode(struct ata_device *dev)

发出 SET FEATURES - XFER MODE 命令

参数

struct ata_device *dev

将向其发送命令的设备

向端口 ap 上的设备 dev 发出 SET FEATURES - XFER MODE 命令。

锁定:PCI/etc. 总线探测 sem。

返回

成功时为 0,否则为 AC_ERR_* 掩码。

unsigned int ata_dev_init_params(struct ata_device *dev, u16 heads, u16 sectors)

发出 INIT DEV PARAMS 命令

参数

struct ata_device *dev

将向其发送命令的设备

u16 heads

磁头数量(任务文件参数)

u16 sectors

扇区数量(任务文件参数)

锁定:内核线程上下文(可能会休眠)

返回

成功时为 0,否则为 AC_ERR_* 掩码。

int atapi_check_dma(struct ata_queued_cmd *qc)

检查是否可以支持 ATAPI DMA

参数

struct ata_queued_cmd *qc

要检查的与任务文件相关的元数据

允许底层驱动程序过滤 ATA PACKET 命令,返回一个状态,指示是否可以使用 DMA 来处理提供的 PACKET 命令。

锁定:spin_lock_irqsave(主机锁)

返回

当可以使用 ATAPI DMA 时为 0

否则为非零值

void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, unsigned int n_elem)

将命令与散列表关联。

参数

struct ata_queued_cmd *qc

要关联的命令

struct scatterlist *sg

散列表。

unsigned int n_elem

散列表中元素的数量。

初始化 queued_cmd qc 的数据相关元素,使其指向包含 n_elem 个元素的散列表 sg

锁定:spin_lock_irqsave(主机锁)

void ata_sg_clean(struct ata_queued_cmd *qc)

取消映射与命令关联的 DMA 内存

参数

struct ata_queued_cmd *qc

包含要释放的 DMA 内存的命令

取消映射与此命令关联的所有已映射的 DMA 内存。

锁定:spin_lock_irqsave(主机锁)

int ata_sg_setup(struct ata_queued_cmd *qc)

DMA 映射与命令关联的散列表。

参数

struct ata_queued_cmd *qc

带有要映射的散列表的命令。

DMA 映射与 queued_cmd qc 关联的散列表。

锁定:spin_lock_irqsave(主机锁)

返回

成功时为零,错误时为负数。

void swap_buf_le16(u16 *buf, unsigned int buf_words)

就地交换 16 位字的半部分

参数

u16 *buf

要交换的缓冲区

unsigned int buf_words

缓冲区中 16 位字的数目。

如果需要,交换 16 位字的半部分,以将小端字节顺序转换为本机 CPU 字节顺序,反之亦然。

锁定:从调用方继承。

void ata_qc_free(struct ata_queued_cmd *qc)

释放未使用的 ata_queued_cmd

参数

struct ata_queued_cmd *qc

要完成的命令

旨在释放未使用的 ata_queued_cmd 对象,以防某些情况阻止使用它。

锁定:spin_lock_irqsave(主机锁)

void ata_qc_issue(struct ata_queued_cmd *qc)

向设备发出任务文件

参数

struct ata_queued_cmd *qc

要向设备发出的命令

准备一个 ATA 命令以提交给设备。这包括将数据映射到 DMA 可寻址区域,填充 S/G 表,最后将任务文件写入硬件,启动命令。

锁定:spin_lock_irqsave(主机锁)

测试给定的链路是否在线

参数

struct ata_link *link

要测试的 ATA 链路

测试 link 是否在线。请注意,如果无法获得 link 的在线状态,则此函数返回 0,因此 ata_link_online(link) != !ata_link_offline(link)。

锁定:无。

返回

如果端口在线状态可用且在线,则为 True。

测试给定的链路是否脱机

参数

struct ata_link *link

要测试的 ATA 链路

测试 link 是否离线。请注意,如果无法获得 link 的离线状态,则此函数返回 0,因此 ata_link_online(link) != !ata_link_offline(link)。

锁定:无。

返回

如果端口脱机状态可用且脱机,则为 True。

void ata_dev_init(struct ata_device *dev)

初始化 ata_device 结构

参数

struct ata_device *dev

要初始化的设备结构

初始化 dev 以准备探测。

锁定:从调用方继承。

初始化 ata_link 结构

参数

struct ata_port *ap

链接所连接的 ATA 端口

struct ata_link *link

要初始化的链接结构

int pmp

端口倍增器端口号

初始化 link

锁定:内核线程上下文(可能会休眠)

初始化 link->sata_spd_limit

参数

struct ata_link *link

要为其配置 sata_spd_limit 的链接

link->[hw_]sata_spd_limit 初始化为当前配置的值。

锁定:内核线程上下文(可能会休眠)。

返回

成功时返回 0,失败时返回 -errno。

void ata_finalize_port_ops(struct ata_port_operations *ops)

完成 ata_port_operations

参数

struct ata_port_operations *ops

要完成的 ata_port_operations

ata_port_operations 可以从另一个 ops 继承,而该 ops 又可以从另一个继承。只要继承链中没有循环,这可以根据需要进行多次。

Ops 表在主机启动时完成。 NULL 或未指定的条目从拥有该方法的最接近的祖先继承,并且该条目使用它填充。完成之后,ops 表直接指向所有方法,并且不再需要 ->inherits 并将其清除。

使用 ATA_OP_NULL,继承 ops 可以强制方法为 NULL。

锁定:无。

void ata_dev_free_resources(struct ata_device *dev)

释放设备资源

参数

struct ata_device *dev

目标 ATA 设备

释放分配用于支持设备功能的资源。

锁定:内核线程上下文(可能会休眠)。

void ata_port_detach(struct ata_port *ap)

分离 ATA 端口以准备移除设备

参数

struct ata_port *ap

要分离的 ATA 端口

分离 ap 的所有 ATA 设备和关联的 SCSI 设备;然后,删除关联的 SCSI 主机。保证从该函数返回后,ap 处于静止状态。

锁定:内核线程上下文(可能会休眠)。

void __ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...)

推送错误描述,无需添加分隔符

参数

struct ata_eh_info *ehi

目标 EHI

const char *fmt

printf 格式字符串

根据 fmt 格式化字符串并将其附加到 ehi->desc

锁定:spin_lock_irqsave(主机锁)

...

可变参数

void ata_ehi_push_desc(struct ata_eh_info *ehi, const char *fmt, ...)

推送带分隔符的错误描述

参数

struct ata_eh_info *ehi

目标 EHI

const char *fmt

printf 格式字符串

根据 fmt 格式化字符串并将其附加到 ehi->desc。如果 ehi->desc 不为空,则在中间添加“,”。

锁定:spin_lock_irqsave(主机锁)

...

可变参数

void ata_ehi_clear_desc(struct ata_eh_info *ehi)

清除错误描述

参数

struct ata_eh_info *ehi

目标 EHI

清除 ehi->desc

锁定:spin_lock_irqsave(主机锁)

void ata_port_desc(struct ata_port *ap, const char *fmt, ...)

附加端口描述

参数

struct ata_port *ap

目标 ATA 端口

const char *fmt

printf 格式字符串

根据 fmt 格式化字符串并将其附加到端口描述。如果端口描述不为空,则在中间添加“ ”。此函数在初始化 ata_host 时使用。描述在主机注册时打印。

锁定:无。

...

可变参数

void ata_port_pbar_desc(struct ata_port *ap, int bar, ssize_t offset, const char *name)

附加 PCI BAR 描述

参数

struct ata_port *ap

目标 ATA 端口

int bar

目标 PCI BAR

ssize_t offset

PCI BAR 中的偏移量

const char *name

区域的名称

如果 offset 为负数,则此函数格式化一个字符串,其中包含 BAR 的名称、地址、大小和类型,并将其附加到端口描述。如果 offset 为零或正数,则仅附加名称和偏移地址。

锁定:无。

unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd)

确定内部命令的超时时间

参数

struct ata_device *dev

目标设备

u8 cmd

要发出的内部命令

确定 dev 的内部命令 cmd 的超时时间。

锁定:EH 上下文。

返回

确定的超时时间。

void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd)

内部命令超时的通知

参数

struct ata_device *dev

目标设备

u8 cmd

超时的内部命令

通知 EH,dev 的内部命令 cmd 超时。此函数应仅针对使用 ata_internal_cmd_timeout() 确定超时的命令调用。

锁定:EH 上下文。

void ata_eh_acquire(struct ata_port *ap)

获取 EH 所有权

参数

struct ata_port *ap

要为其获取 EH 所有权的 ATA 端口

获取 ap 的 EH 所有权。这是共享主机端口的基本互斥机制。同一主机上的只有一个端口可以声明 EH 的所有权。

锁定:EH 上下文。

void ata_eh_release(struct ata_port *ap)

释放 EH 所有权

参数

struct ata_port *ap

要释放 EH 所有权的 ATA 端口

如果调用者之前使用过 ata_eh_acquire() 获取了 EH 所有权,则释放 ap 的 EH 所有权。

锁定:EH 上下文。

void ata_scsi_error(struct Scsi_Host *host)

SCSI 层错误处理回调

参数

struct Scsi_Host *host

发生错误的 SCSI 主机

处理 SCSI 层抛出的错误事件。

锁定:从 SCSI 层继承(无,可以睡眠)

返回

零。

void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, struct list_head *eh_work_q)

用于命令列表的错误回调

参数

struct Scsi_Host *host

包含端口的 scsi 主机

struct ata_port *ap

主机内的 ATA 端口

struct list_head *eh_work_q

要处理的命令列表

描述

处理给定的命令列表,并将已完成的命令返回到 ap->eh_done_q。此函数是 libata 错误处理程序的第一部分,它处理给定的失败命令列表。

void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap)

在命令之后恢复端口

参数

struct Scsi_Host *host

包含端口的 SCSI 主机

struct ata_port *ap

ATA 端口

描述

在所有命令都已恢复后,处理端口 ap 的恢复。

void ata_port_wait_eh(struct ata_port *ap)

等待当前挂起的 EH 完成

参数

struct ata_port *ap

要等待 EH 的端口

等待直到当前挂起的 EH 完成。

锁定:内核线程上下文(可能会休眠)。

void ata_eh_set_pending(struct ata_port *ap, int fastdrain)

设置 ATA_PFLAG_EH_PENDING 并激活快速排空

参数

struct ata_port *ap

目标 ATA 端口

int fastdrain

激活快速排空

设置 ATA_PFLAG_EH_PENDING,如果 fastdrain 非零且 EH 之前未挂起,则激活快速排空。快速排空确保 EH 及时启动。

锁定:spin_lock_irqsave(主机锁)

void ata_qc_schedule_eh(struct ata_queued_cmd *qc)

安排 qc 进行错误处理

参数

struct ata_queued_cmd *qc

要安排错误处理的命令

安排 qc 的错误处理。 一旦其他命令被排空,EH 将启动。

锁定:spin_lock_irqsave(主机锁)

void ata_std_sched_eh(struct ata_port *ap)

非 libsas ata_ports 使用此通用例程发出 eh

参数

struct ata_port *ap

要安排 EH 的 ATA 端口

锁定:从 ata_port_schedule_eh 继承 spin_lock_irqsave(主机锁)

void ata_std_end_eh(struct ata_port *ap)

非 libsas ata_ports 使用此通用例程完成 eh

参数

struct ata_port *ap

要结束 EH 的 ATA 端口

描述

在 libata 对象模型中,ata_port 到 shost 存在 1:1 的映射,因此可以在 ap->lock 下直接操作主机字段。在 libsas 情况下,我们需要在 ha->级别保持一个锁来协调这些事件。

锁定:spin_lock_irqsave(主机锁)

void ata_port_schedule_eh(struct ata_port *ap)

安排错误处理,无需 qc

参数

struct ata_port *ap

要安排 EH 的 ATA 端口

安排 ap 的错误处理。 一旦所有命令都被排空,EH 将启动。

锁定:spin_lock_irqsave(主机锁)

中止链接上的所有 qc

参数

struct ata_link *link

要中止 qc 的 ATA 链接

中止在 link 上激活的所有活动 qc,并安排 EH。

锁定:spin_lock_irqsave(主机锁)

返回

已中止 qc 的数量。

int ata_port_abort(struct ata_port *ap)

中止端口上的所有 qc

参数

struct ata_port *ap

要中止 qc 的 ATA 端口

中止 ap 的所有活动 qc,并安排 EH。

锁定:spin_lock_irqsave(host_set 锁)

返回

已中止 qc 的数量。

void __ata_port_freeze(struct ata_port *ap)

冻结端口

参数

struct ata_port *ap

要冻结的 ATA 端口

当 HSM 违规或其他一些情况中断端口的正常操作时,会调用此函数。 在端口解冻之前,不允许冻结端口执行任何操作,通常在成功重置后进行解冻。

ap->ops->freeze() 回调可用于硬件方式冻结端口(例如,屏蔽中断并停止 DMA 引擎)。如果无法以硬件方式冻结端口,则中断处理程序必须无条件地确认并清除中断,同时端口被冻结。

锁定:spin_lock_irqsave(主机锁)

int ata_port_freeze(struct ata_port *ap)

中止并冻结端口

参数

struct ata_port *ap

要冻结的 ATA 端口

中止并冻结 ap。 必须首先调用冻结操作,因为某些硬件在可以访问 taskfile 寄存器之前需要特殊操作。

锁定:spin_lock_irqsave(主机锁)

返回

已中止命令的数量。

void ata_eh_freeze_port(struct ata_port *ap)

冻结端口的 EH 辅助函数

参数

struct ata_port *ap

要冻结的 ATA 端口

冻结 ap

锁定:无。

void ata_eh_thaw_port(struct ata_port *ap)

解冻端口的 EH 辅助函数

参数

struct ata_port *ap

要解冻的 ATA 端口

解冻冻结的端口 ap

锁定:无。

void ata_eh_qc_complete(struct ata_queued_cmd *qc)

从 EH 完成活动 ATA 命令

参数

struct ata_queued_cmd *qc

要完成的命令

向中间层和上层表明 ATA 命令已完成。 从 EH 使用。

void ata_eh_qc_retry(struct ata_queued_cmd *qc)

告诉中间层在 EH 之后重试 ATA 命令

参数

struct ata_queued_cmd *qc

要重试的命令

向中间层和上层表明应该重试 ATA 命令。 从 EH 使用。

SCSI 中间层将重试次数限制为 scmd->allowed。 对于由于不相关的故障而重试的命令,scmd->allowed 会递增 (qc->err_mask 为零)。

void ata_dev_disable(struct ata_device *dev)

禁用 ATA 设备

参数

struct ata_device *dev

要禁用的 ATA 设备

禁用 dev

锁定:EH 上下文。

void ata_eh_detach_dev(struct ata_device *dev)

分离 ATA 设备

参数

struct ata_device *dev

要分离的 ATA 设备

分离 dev

锁定:无。

void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev, unsigned int action)

即将执行 eh_action

参数

struct ata_link *link

目标 ATA 链接

struct ata_device *dev

每个设备操作的目标 ATA dev(可以为 NULL)

unsigned int action

即将执行的操作

在执行 EH 操作之前调用,以清除 link->eh_info 中的相关位,以便不会不必要地重复 EH 操作。

锁定:无。

void ata_eh_done(struct ata_link *link, struct ata_device *dev, unsigned int action)

EH 操作完成

参数

struct ata_link *link

已完成 EH 操作的 ATA 链接

struct ata_device *dev

每个设备操作的目标 ATA dev(可以为 NULL)

unsigned int action

刚完成的操作

在执行 EH 操作后立即调用,以清除 link->eh_context 中的相关位。

锁定:无。

const char *ata_err_string(unsigned int err_mask)

将 err_mask 转换为描述性字符串

参数

unsigned int err_mask

要转换为字符串的错误掩码

err_mask 转换为描述性字符串。 错误根据严重性进行优先级排序,并且仅报告最严重的错误。

锁定:无。

返回

err_mask 的描述性字符串

unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key)

执行 ATAPI TEST_UNIT_READY

参数

struct ata_device *dev

目标 ATAPI 设备

u8 *r_sense_key

sense_key 的输出参数

执行 ATAPI TEST_UNIT_READY。

锁定:EH 上下文(可能睡眠)。

返回

成功时为 0,失败时为 AC_ERR_* 掩码。

enum scsi_disposition ata_eh_decide_disposition(struct ata_queued_cmd *qc)

根据 sense 数据处置 qc

参数

struct ata_queued_cmd *qc

要检查的 qc

对于常规 SCSI 命令,SCSI 完成回调 (scsi_done()) 将调用 scsi_complete(),后者将调用 scsi_decide_disposition(),后者将调用 scsi_check_sense()。 scsi_complete() 最终调用 scsi_finish_command()。 这对于 SCSI 来说是可以的,因为任何最终的 sense 数据通常都在完成本身中返回(而不调用 SCSI EH)。 但是,对于 QC,我们总是需要使用 SCSI EH 显式获取 sense 数据。

通过 SCSI EH 完成的命令将改为使用 scsi_eh_flush_done_q() 完成,后者将直接调用 scsi_finish_command()(而从不调用 scsi_check_sense())。

对于通过 SCSI EH 进行的命令,SCSI EH 策略处理程序有责任调用 scsi_decide_disposition(),例如,请参阅 scsi_eh_get_sense() 如何为未将 sense 数据作为完成的一部分的 SCSI LLDD 调用 scsi_decide_disposition()。

因此,对于通过 SCSI EH 进行的 QC 命令,我们需要自己调用 scsi_check_sense(),类似于 scsi_eh_get_sense() 如何调用 scsi_decide_disposition(),后者调用 scsi_check_sense(),以便设置正确的 SCSI ML 字节(如果有)。

锁定:EH 上下文。

返回

SUCCESS 或 FAILED 或 NEEDS_RETRY 或 ADD_TO_MLQUEUE

bool ata_eh_request_sense(struct ata_queued_cmd *qc)

执行 REQUEST_SENSE_DATA_EXT

参数

struct ata_queued_cmd *qc

qc执行 REQUEST_SENSE_SENSE_DATA_EXT 到

在设备报告 CHECK SENSE 后执行 REQUEST_SENSE_DATA_EXT。此函数是一个 EH 助手。

锁定:内核线程上下文(可能会休眠)。

返回

如果可以获取到 sense 数据,则为 true;否则为 false。

unsigned int atapi_eh_request_sense(struct ata_device *dev, u8 *sense_buf, u8 dfl_sense_key)

执行 ATAPI REQUEST_SENSE

参数

struct ata_device *dev

设备执行 REQUEST_SENSE 到

u8 *sense_buf

结果 sense 数据缓冲区(SCSI_SENSE_BUFFERSIZE 字节长)

u8 dfl_sense_key

要使用的默认 sense key

在设备报告 CHECK SENSE 后执行 ATAPI REQUEST_SENSE。此函数是 EH 助手。

锁定:内核线程上下文(可能会休眠)。

返回

成功时为 0,失败时为 AC_ERR_* 掩码

void ata_eh_analyze_serror(struct ata_link *link)

分析失败端口的 SError

参数

struct ata_link *link

用于分析 SError 的 ATA link

如果可用,则分析 SError 并进一步确定失败原因。

锁定:无。

unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc)

分析失败的 qc 的 taskfile

参数

struct ata_queued_cmd *qc

要分析的 qc

分析 qc 的 taskfile 并进一步确定失败原因。如果可用,此函数还会请求 ATAPI sense 数据。

锁定:内核线程上下文(可能会休眠)。

返回

确定的恢复操作

unsigned int ata_eh_speed_down_verdict(struct ata_device *dev)

确定降速结论

参数

struct ata_device *dev

感兴趣的设备

此函数检查 dev 的错误环,并确定是否需要关闭 NCQ,是否应降低传输速度,或者是否需要回退到 PIO。

ECAT_ATA_BUS:任何命令的 ATA_BUS 错误

ECAT_TOUT_HSM任何命令的 TIMEOUT 或 HSM 违规

IO 命令

ECAT_UNK_DEV:IO 命令的未知 DEV 错误

ECAT_DUBIOUS_*与上述三个相同,但发生在

数据传输尚未验证时。

结论是

NCQ_OFF:关闭 NCQ。

SPEED_DOWN降低传输速度,但不回退

到 PIO。

FALLBACK_TO_PIO:回退到 PIO。

即使返回多个结论,每次错误也只会采取一个操作。 由非 DUBIOUS 错误触发的操作会清除 ering,而由 DUBIOUS_* 错误触发的操作不会。 这是为了在最初配置设备后立即加速降速决策。

以下是降速规则。 #1 和 #2 处理 DUBIOUS 错误。

  1. 如果在过去 5 分钟内发生超过一个 DUBIOUS_ATA_BUS 或 DUBIOUS_TOUT_HSM 错误,则 SPEED_DOWN 和 FALLBACK_TO_PIO。

  2. 如果在过去 5 分钟内发生超过一个 DUBIOUS_TOUT_HSM 或 DUBIOUS_UNK_DEV 错误,则 NCQ_OFF。

  3. 如果在过去 5 分钟内发生超过 8 个 ATA_BUS、TOUT_HSM 或 UNK_DEV 错误,则 FALLBACK_TO_PIO

  4. 如果在过去 10 分钟内发生超过 3 个 TOUT_HSM 或 UNK_DEV 错误,则 NCQ_OFF。

  5. 如果在过去 10 分钟内发生超过 3 个 ATA_BUS 或 TOUT_HSM 错误,或者超过 6 个 UNK_DEV 错误,则 SPEED_DOWN。

锁定:从调用方继承。

返回

ATA_EH_SPDN_* 标志的 OR。

unsigned int ata_eh_speed_down(struct ata_device *dev, unsigned int eflags, unsigned int err_mask)

记录错误并在必要时降速

参数

struct ata_device *dev

失败的设备

unsigned int eflags

ATA_EFLAG_* 标志的掩码

unsigned int err_mask

错误的 err_mask

记录错误并检查错误历史记录,以确定是否需要调整传输速度。 如果需要进行此类调整,它还会适当地设置传输限制。

锁定:内核线程上下文(可能会休眠)。

返回

确定的恢复操作。

int ata_eh_worth_retry(struct ata_queued_cmd *qc)

分析错误并决定是否重试

参数

struct ata_queued_cmd *qc

可能要重试的 qc

查看错误原因,并确定重试是否有用。 我们不想重试媒体错误,因为驱动器本身可能已经花费了 10-30 秒进行其内部重试,然后才报告失败。

bool ata_eh_quiet(struct ata_queued_cmd *qc)

检查我们是否需要对命令错误保持沉默

参数

struct ata_queued_cmd *qc

要检查的 qc

查看 qc 标志及其 scsi 命令请求标志,以确定我们是否需要对命令失败保持沉默。

分析错误并确定恢复操作

参数

struct ata_link *link

要执行检查的主机 link

分析 link 失败的原因并确定需要哪些恢复操作。 此函数还会设置更详细的 AC_ERR_* 值,并填充 ATAPI CHECK SENSE 的 sense 数据。

锁定:内核线程上下文(可能会休眠)。

void ata_eh_autopsy(struct ata_port *ap)

分析错误并确定恢复操作

参数

struct ata_port *ap

要执行检查的主机端口

分析 ap 的所有 link,并确定它们失败的原因以及需要哪些恢复操作。

锁定:内核线程上下文(可能会休眠)。

const char *ata_get_cmd_name(u8 command)

获取 ATA 命令的名称

参数

u8 command

要获取名称的 ATA 命令代码

返回给定命令的文本名称或“unknown”

锁定:无

向用户报告错误处理

参数

struct ata_link *link

正在进行的 ATA link EH

向用户报告 EH。

锁定:无。

void ata_eh_report(struct ata_port *ap)

向用户报告错误处理

参数

struct ata_port *ap

要报告 EH 的 ATA 端口

向用户报告 EH。

锁定:无。

int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)

编程定时并发出 SET FEATURES - XFER

参数

struct ata_link *link

将在其上编程定时的链路

struct ata_device **r_failed_dev

失败设备的输出参数

设置 ATA 设备磁盘传输模式(PIO3、UDMA6 等)。 如果 ata_set_mode() 失败,则指向失败设备的指针将在 r_failed_dev 中返回。

锁定:PCI/etc. 总线探测 sem。

返回

成功时为 0,否则为负 errno

int atapi_eh_clear_ua(struct ata_device *dev)

在重置后清除 ATAPI UNIT ATTENTION

参数

struct ata_device *dev

要清除 UA 的 ATAPI 设备

重置和其他操作可能会使 ATAPI 设备引发 UNIT ATTENTION,这会导致下一个操作失败。 此函数清除 UA。

锁定:EH 上下文(可能睡眠)。

返回

成功时返回 0,失败时返回 -errno。

int ata_eh_maybe_retry_flush(struct ata_device *dev)

必要时重试 FLUSH

参数

struct ata_device *dev

可能需要 FLUSH 重试的 ATA 设备

如果 dev FLUSH 失败,则需要立即向上传递报告,因为它意味着 dev 无法重新映射,并且至少已经丢失了一个扇区,并且进一步的 FLUSH 重试对丢失的扇区没有任何影响。 但是,如果 FLUSH 由于其他原因(例如传输错误)失败,则需要重试 FLUSH。

此函数确定是否需要 FLUSH 失败重试,并在必要时执行它。

返回

如果 EH 可以继续,则为 0;如果 EH 需要重复,则为 -errno。

int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, struct ata_device **r_failed_dev)

配置 SATA 接口电源管理

参数

struct ata_link *link

用于配置电源管理的 link

enum ata_lpm_policy policy

link 电源管理策略

struct ata_device **r_failed_dev

失败设备的输出参数

启用 SATA 接口电源管理。 这将为 min_power 和 medium_power_with_dipm 策略启用设备接口电源管理 (DIPM),然后调用驱动程序特定的回调以启用主机发起的电源管理。

锁定:EH 上下文。

返回

成功时返回 0,失败时返回 -errno。

int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset, struct ata_link **r_failed_link)

在错误后恢复主机端口

参数

struct ata_port *ap

要恢复的主机端口

ata_prereset_fn_t prereset

prereset 方法(可以为 NULL)

ata_reset_fn_t softreset

softreset 方法(可以为 NULL)

ata_reset_fn_t hardreset

hardreset 方法(可以为 NULL)

ata_postreset_fn_t postreset

postreset 方法(可以为 NULL)

struct ata_link **r_failed_link

失败 link 的输出参数

这是 libata 异常处理的 alpha 和 omega、eum 和 yang、核心和灵魂。 在进入时,恢复每个 link 所需的操作和热插拔请求记录在 link 的 eh_context 中。 此函数执行所有操作,并进行适当的重试和回退,以恢复失败的设备、分离损坏的设备并迎接新设备。

锁定:内核线程上下文(可能会休眠)。

返回

成功时返回 0,失败时返回 -errno。

void ata_eh_finish(struct ata_port *ap)

完成 EH

参数

struct ata_port *ap

要完成 EH 的主机端口

恢复已完成。 清理 EH 状态并重试或完成失败的 qcs。

锁定:无。

void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)

执行标准错误处理

参数

struct ata_port *ap

要处理错误的主机端口

ata_prereset_fn_t prereset

prereset 方法(可以为 NULL)

ata_reset_fn_t softreset

softreset 方法(可以为 NULL)

ata_reset_fn_t hardreset

hardreset 方法(可以为 NULL)

ata_postreset_fn_t postreset

postreset 方法(可以为 NULL)

执行标准错误处理序列。

锁定:内核线程上下文(可能会休眠)。

void ata_std_error_handler(struct ata_port *ap)

标准错误处理程序

参数

struct ata_port *ap

要处理错误的主机端口

标准错误处理程序

锁定:内核线程上下文(可能会休眠)。

void ata_eh_handle_port_suspend(struct ata_port *ap)

执行端口挂起操作

参数

struct ata_port *ap

要挂起的端口

挂起 ap

锁定:内核线程上下文(可能会休眠)。

void ata_eh_handle_port_resume(struct ata_port *ap)

执行端口恢复操作

参数

struct ata_port *ap

要恢复的端口

恢复 ap

锁定:内核线程上下文(可能会休眠)。

libata SCSI 转换/仿真

int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])

sd 使用的通用 bios 磁头/扇区/柱面计算器。

参数

struct scsi_device *sdev

要确定 BIOS 几何结构的 SCSI 设备

struct block_device *bdev

sdev 关联的块设备

sector_t capacity

SCSI 设备的容量

int geom[]

几何结构将要输出到的位置

sd 使用的通用 bios 磁头/扇区/柱面计算器。现在大多数 BIOS 都期望 XXX/255/16 (CHS) 映射。如果未使用此映射,则某些情况下磁盘可能无法启动。

LOCKING: 由 SCSI 层定义。我们并不真正在意。

返回

零。

void ata_scsi_unlock_native_capacity(struct scsi_device *sdev)

解锁原生容量

参数

struct scsi_device *sdev

要调整设备容量的 SCSI 设备

如果 sdev 上的分区超出设备末尾,则调用此函数。它请求 EH 解锁 HPA。

LOCKING: 由 SCSI 层定义。可能会睡眠。

bool ata_scsi_dma_need_drain(struct request *rq)

检查数据传输是否可能溢出

参数

struct request *rq

要检查的请求

由于应用程序错误或硬件错误,将可变长度数据传输到主机的 ATAPI 命令可能会溢出。此函数检查是否应为 request 耗尽并忽略溢出。

锁定:无。

返回

如果是,则为 1;否则为 0。

int ata_scsi_sdev_init(struct scsi_device *sdev)

SCSI 设备的早期设置

参数

struct scsi_device *sdev

要检查的 SCSI 设备

当与 ATA 设备关联的 SCSI 设备在端口上扫描时,从 scsi_alloc_sdev() 调用此函数。

LOCKING: 由 SCSI 层定义。我们并不真正在意。

int ata_scsi_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)

设置 SCSI 设备属性

参数

struct scsi_device *sdev

要检查的 SCSI 设备

struct queue_limits *lim

队列限制

这在我们实际开始读取和写入设备之前调用,以配置某些 SCSI 中间层行为。

LOCKING: 由 SCSI 层定义。我们并不真正在意。

void ata_scsi_sdev_destroy(struct scsi_device *sdev)

SCSI 设备即将被销毁

参数

struct scsi_device *sdev

要销毁的 SCSI 设备

sdev 即将被销毁以进行热插拔/热拔插。如果此拔插是由 libata 发起的,如 NULL dev->sdev 所示,则此函数不必执行任何操作。否则,SCSI 层发起的暖拔插正在进行中。清除 dev->sdev,安排设备进行 ATA 分离并调用 EH。

LOCKING: 由 SCSI 层定义。我们并不真正在意。

int ata_scsi_queuecmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd)

向 libata 管理的设备发出 SCSI cdb

参数

struct Scsi_Host *shost

要发送的命令的 SCSI 主机

struct scsi_cmnd *cmd

要发送的 SCSI 命令

在某些情况下,此函数将 SCSI 命令转换为 ATA 任务文件,并将任务文件排队以发送到硬件。在其他情况下,此函数通过评估和响应某些 SCSI 命令来模拟 SCSI 设备。这创建了 ATA 和 ATAPI 设备显示为 SCSI 设备的整体效果。

LOCKING: ATA 主机锁

返回

如果可以对 cmd 进行排队,则从 __ata_scsi_queuecmd() 返回值,否则为 0。

void ata_scsi_set_passthru_sense_fields(struct ata_queued_cmd *qc)

在 sense 缓冲区中设置 ATA 字段

参数

struct ata_queued_cmd *qc

ATA PASS-THROUGH 命令。

使用 ATA 任务文件字段填充“ATA 状态返回 sense 数据描述符”/“固定格式 sense 数据”。

锁定:无。

int ata_get_identity(struct ata_port *ap, struct scsi_device *sdev, void __user *arg)

HDIO_GET_IDENTITY ioctl 的处理程序

参数

struct ata_port *ap

目标端口

struct scsi_device *sdev

要获取标识数据的 SCSI 设备

void __user *arg

标识数据的用户缓冲区区域

LOCKING: 由 SCSI 层定义。我们并不真正在意。

返回

成功时为零,出错时为负 errno。

int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)

HDIO_DRIVE_CMD ioctl 的处理程序

参数

struct scsi_device *scsidev

我们要向其发出命令的设备

void __user *arg

用户提供的用于发出命令的数据

LOCKING: 由 SCSI 层定义。我们并不真正在意。

返回

成功时为零,出错时为负 errno。

int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)

HDIO_DRIVE_TASK ioctl 的处理程序

参数

struct scsi_device *scsidev

我们要向其发出命令的设备

void __user *arg

用户提供的用于发出命令的数据

LOCKING: 由 SCSI 层定义。我们并不真正在意。

返回

成功时为零,出错时为负 errno。

struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev, struct scsi_cmnd *cmd)

获取新的 ata_queued_cmd 引用

参数

struct ata_device *dev

新命令所附加到的 ATA 设备

struct scsi_cmnd *cmd

发起此 ATA 命令的 SCSI 命令

获取对未使用 ata_queued_cmd 结构的引用,该结构是表示发送到硬件的单个 ATA 命令的基本 libata 结构。

如果命令可用,请使用当前命令的信息填充结构的 SCSI 特定部分。

锁定:spin_lock_irqsave(主机锁)

返回

已分配命令,如果无可用命令,则为 NULL

void ata_to_sense_error(u8 drv_stat, u8 drv_err, u8 *sk, u8 *asc, u8 *ascq)

将 ATA 错误转换为 SCSI 错误

参数

u8 drv_stat

ATA 状态寄存器中包含的值

u8 drv_err

ATA 错误寄存器中包含的值

u8 *sk

我们将要填充的 sense key

u8 *asc

我们将要填充的附加 sense code

u8 *ascq

我们将要填充的附加 sense code qualifier

将 ATA 错误转换为 SCSI 错误。填充 SK、ASC 和 ASCQ 字节的指针,以便稍后在固定或描述符格式 sense 块中使用。

锁定:spin_lock_irqsave(主机锁)

void ata_gen_ata_sense(struct ata_queued_cmd *qc)

生成 SCSI 固定 sense 块

参数

struct ata_queued_cmd *qc

我们出错的命令

为失败的 ATA 命令 qc 生成 sense 块。

锁定:无。

unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)

转换 SCSI START STOP UNIT 命令

参数

struct ata_queued_cmd *qc

已转换 ATA 任务文件的存储

设置 ATA 任务文件以发出 STANDBY(停止)或 READ VERIFY(启动)。也许这些命令应该先执行 CHECK POWER MODE 来查看设备当前处于哪种电源模式。[请参阅 www.t10.org 上的 SAT 修订版 5]

锁定:spin_lock_irqsave(主机锁)

返回

成功时为零,出错时为非零。

unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc)

转换 SCSI SYNCHRONIZE CACHE 命令

参数

struct ata_queued_cmd *qc

已转换 ATA 任务文件的存储

设置 ATA 任务文件以发出 FLUSH CACHE 或 FLUSH CACHE EXT。

锁定:spin_lock_irqsave(主机锁)

返回

成功时为零,出错时为非零。

void scsi_6_lba_len(const u8 *cdb, u64 *plba, u32 *plen)

获取 LBA 和传输长度

参数

const u8 *cdb

要转换的 SCSI 命令

计算 6 字节命令的 LBA 和传输长度。

u64 *plba

LBA

u32 *plen

传输长度

void scsi_10_lba_len(const u8 *cdb, u64 *plba, u32 *plen)

获取 LBA 和传输长度

参数

const u8 *cdb

要转换的 SCSI 命令

计算 10 字节命令的 LBA 和传输长度。

u64 *plba

LBA

u32 *plen

传输长度

void scsi_16_lba_len(const u8 *cdb, u64 *plba, u32 *plen)

获取 LBA 和传输长度

参数

const u8 *cdb

要转换的 SCSI 命令

计算 16 字节命令的 LBA 和传输长度。

u64 *plba

LBA

u32 *plen

传输长度

int scsi_dld(const u8 *cdb)

获取持续时间限制描述符索引

参数

const u8 *cdb

要转换的 SCSI 命令

返回指示命令持续时间限制描述符索引的 dld 位。

unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc)

将 SCSI VERIFY 命令转换为 ATA 命令

参数

struct ata_queued_cmd *qc

已转换 ATA 任务文件的存储

将 SCSI VERIFY 命令转换为 ATA READ VERIFY 命令。

锁定:spin_lock_irqsave(主机锁)

返回

成功时为零,出错时为非零。

unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)

将 SCSI r/w 命令转换为 ATA 命令

参数

struct ata_queued_cmd *qc

已转换 ATA 任务文件的存储

将六个 SCSI 读/写命令中的任何一个转换为 ATA 对应命令,包括起始扇区 (LBA)、扇区计数,并考虑设备的 LBA48 支持。

当前支持命令 READ_6READ_10READ_16WRITE_6WRITE_10WRITE_16

锁定:spin_lock_irqsave(主机锁)

返回

成功时为零,出错时为非零。

int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd, ata_xlat_func_t xlat_func)

转换 SCSI 命令,然后发送到 ATA 设备

参数

struct ata_device *dev

命令所寻址的 ATA 设备

struct scsi_cmnd *cmd

要执行的 SCSI 命令

ata_xlat_func_t xlat_func

cmd 转换为 ATA 任务文件的执行者

我们的 ->queuecommand() 函数已经确定,发出的 SCSI 命令可以直接转换为 ATA 命令,而不是在内部处理。

此函数为 SCSI 命令设置 ata_queued_cmd 结构,并将该 ata_queued_cmd 发送到硬件。

如果准备好执行 ATA 命令,则 xlat_func 参数(执行者)返回 0,否则返回 1 以完成转换。如果返回 1,则假定 cmd->result(可能还有 cmd->sense_buffer)被设置为反映错误情况或干净的(早期)终止。

锁定:spin_lock_irqsave(主机锁)

返回

成功时返回 0,如果需要延迟命令,则返回 SCSI_ML_QUEUE_DEVICE_BUSY。

void ata_scsi_rbuf_fill(struct ata_device *dev, struct scsi_cmnd *cmd, unsigned int (*actor)(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf))

SCSI 命令模拟器的包装器

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

unsigned int (*actor)(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

所需 SCSI 命令模拟器的回调钩子

负责模拟 SCSI 命令的繁重工作...映射响应缓冲区,调用命令的处理程序,并处理处理程序的返回值。此返回值指示处理程序是否希望 SCSI 命令成功完成 (0),否则不成功(在这种情况下,假定 cmd->result 和 sense 缓冲区已设置)。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_std(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟标准 INQUIRY 命令

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回与非 VPD INQUIRY 命令输出关联的标准设备标识数据。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_00(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 0,页面列表

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回可用的 inquiry VPD 页面列表。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_80(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 80,设备序列号

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回 ATA 设备序列号。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_83(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 83,设备标识

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

产生两个逻辑单元设备标识符
  • 包含 ATA 序列号的供应商特定 ASCII

  • SAT 定义的“基于 t10 供应商 id”包含 ASCII 供应商名称 (“ATA “)、型号和序列号。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_89(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 89,ATA 信息

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

产生 SAT 指定的 ATA VPD 页面。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_b0(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 B0,块限制

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回 VPD 页面 B0h(块限制)的数据。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_b1(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 B1,块设备特性

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回 VPD 页面 B1h(块设备特性)的数据。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_b2(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 B2,逻辑块配置

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回 VPD 页面 B2h(逻辑块配置)的数据。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_b6(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 B6,分区块设备特性

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回 VPD 页面 B2h(分区块设备特性)的数据。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inq_b9(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY VPD 页面 B9,并发定位范围

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回 VPD 页面 B9h(并发定位范围)的数据。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_inquiry(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 INQUIRY 命令

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

返回与 INQUIRY 命令输出关联的数据。

锁定:spin_lock_irqsave(主机锁)

void modecpy(u8 *dest, const u8 *src, int n, bool changeable)

准备 MODE SENSE 的响应

参数

u8 *dest

输出缓冲区

const u8 *src

正在复制的数据

int n

模式页面的长度

bool changeable

是否请求可更改的参数

为当前或可更改的参数生成一个通用的 MODE SENSE 页面。

锁定:无。

unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)

模拟 MODE SENSE 缓存信息页面

参数

u16 *id

设备 IDENTIFY 数据

u8 *buf

输出缓冲区

bool changeable

是否请求可更改的参数

生成一个缓存信息页面,该页面有条件地向 SCSI 层指示写缓存,具体取决于设备功能。

锁定:无。

unsigned int ata_msense_control(struct ata_device *dev, u8 *buf, u8 spg, bool changeable)

模拟 MODE SENSE 控制模式页面

参数

struct ata_device *dev

感兴趣的 ATA 设备

u8 *buf

输出缓冲区

u8 spg

子页面代码

bool changeable

是否请求可更改的参数

生成一个通用的 MODE SENSE 控制模式页面。

锁定:无。

unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable)

模拟 MODE SENSE r/w 错误恢复页面

参数

u8 *buf

输出缓冲区

bool changeable

是否请求可更改的参数

生成一个通用的 MODE SENSE r/w 错误恢复页面。

锁定:无。

unsigned int ata_scsiop_mode_sense(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 MODE SENSE 6, 10 命令

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

模拟 MODE SENSE 命令。假定这是为直接访问设备(例如磁盘)调用的。其他设备类型不应有块描述符。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsiop_read_cap(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 READ CAPACITY[ 16] 命令

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

模拟 READ CAPACITY 命令。

锁定:无。

unsigned int ata_scsiop_report_luns(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 REPORT LUNS 命令

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

模拟 REPORT LUNS 命令。

锁定:spin_lock_irqsave(主机锁)

unsigned int atapi_xlat(struct ata_queued_cmd *qc)

初始化 PACKET taskfile

参数

struct ata_queued_cmd *qc

要初始化的命令结构

锁定:spin_lock_irqsave(主机锁)

返回

成功返回零,失败返回非零。

struct ata_device *ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev)

从 scsi_cmnd 查找 ata_device

参数

struct ata_port *ap

设备连接到的 ATA 端口

const struct scsi_device *scsidev

从中派生 ATA 设备的 SCSI 设备

给定 struct scsi_cmnd 中提供的各种信息,将其映射到 ATA 总线上,并使用该映射确定哪个 ata_device 与要发送的 SCSI 命令相关联。

锁定:spin_lock_irqsave(主机锁)

返回

关联的 ATA 设备,如果未找到,则为 NULL

unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)

将 ATA 直通 CDB 转换为 taskfile

参数

struct ata_queued_cmd *qc

要初始化的命令结构

处理 CDB 的 12 字节、16 字节或 32 字节版本。

返回

成功返回零,失败返回非零。

size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax, u64 sector, u32 count)

SATL Write Same 到 DSM Trim

参数

struct scsi_cmnd *cmd

正在转换的 SCSI 命令

u32 trmax

可以容纳在 sector_size 字节中的最大条目数。

u64 sector

起始扇区

u32 count

请求的总范围(以逻辑扇区为单位)

描述

重写 WRITE SAME 描述符,使其成为 DSM TRIM 小端格式描述符。

最多 64 个格式的条目

63:48 范围长度 47:0 LBA

范围长度为 0 时将被忽略。LBA 应按排序顺序排列,并且不应重叠。

注意

这与 ADD LBA(S) TO NV CACHE PINNED SET 的格式相同

返回

复制到 sglist 中的字节数。

unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)

SATL Write Same 到 ATA SCT Write Same

参数

struct ata_queued_cmd *qc

要转换的命令

描述

将 SCSI WRITE SAME 命令转换为 DSM TRIM 命令或 SCT Write Same 命令。基于 WRITE SAME 具有 UNMAP 标志

  • 设置后转换为 DSM TRIM

  • 清除后转换为 SCT Write Same

unsigned int ata_scsiop_maint_in(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf)

模拟 MAINTENANCE_IN 的子集

参数

struct ata_device *dev

目标设备。

struct scsi_cmnd *cmd

感兴趣的 SCSI 命令。

u8 *rbuf

响应缓冲区,模拟的 SCSI cmd 输出将发送到该缓冲区。

产生一个子集以满足 scsi_report_opcode()

锁定:spin_lock_irqsave(主机锁)

void ata_scsi_report_zones_complete(struct ata_queued_cmd *qc)

转换 ATA 输出

参数

struct ata_queued_cmd *qc

返回数据的命令结构

将 T-13 小端字段表示形式转换为 T-10 大端字段表示形式。真是一团糟。

int ata_mselect_caching(struct ata_queued_cmd *qc, const u8 *buf, int len, u16 *fp)

模拟用于缓存信息页的 MODE SELECT

参数

struct ata_queued_cmd *qc

已转换 ATA 任务文件的存储

const u8 *buf

输入缓冲区

int len

输入缓冲区中的有效字节数

u16 *fp

发生错误时,用于指示失败字段的输出参数

准备一个 taskfile 以修改设备的缓存信息。

锁定:无。

int ata_mselect_control(struct ata_queued_cmd *qc, u8 spg, const u8 *buf, int len, u16 *fp)

模拟用于控制页的 MODE SELECT

参数

struct ata_queued_cmd *qc

已转换 ATA 任务文件的存储

u8 spg

控制页的目标子页

const u8 *buf

输入缓冲区

int len

输入缓冲区中的有效字节数

u16 *fp

发生错误时,用于指示失败字段的输出参数

准备一个 taskfile 以修改设备的缓存信息。

锁定:无。

unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)

模拟 MODE SELECT 6、10 命令

参数

struct ata_queued_cmd *qc

已转换 ATA 任务文件的存储

将 MODE SELECT 命令转换为 ATA SET FEATURES taskfile。假定仅为直接访问设备(例如磁盘)调用此命令。对于其他设备类型,应该没有块描述符。

锁定:spin_lock_irqsave(主机锁)

unsigned int ata_scsi_var_len_cdb_xlat(struct ata_queued_cmd *qc)

SATL 可变长度 CDB 到处理程序

参数

struct ata_queued_cmd *qc

要转换的命令

将 SCSI 可变长度 CDB 转换为指定命令。它检查 CDB 中的服务操作值以调用相应的处理程序。

返回

成功返回零,失败返回非零

ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)

检查是否可以进行 SCSI 到 ATA 的转换

参数

struct ata_device *dev

ATA 设备

u8 cmd

要考虑的 SCSI 命令操作码

查找给定的 SCSI 命令,并确定是要翻译还是模拟 SCSI 命令。

返回

如果可以,则指向转换函数的指针,否则为 NULL

void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)

在 ATA 设备上模拟 SCSI 命令

参数

struct ata_device *dev

目标设备

struct scsi_cmnd *cmd

发送到设备的 SCSI 命令。

解释并直接执行可以选择性处理的 SCSI 命令列表。

锁定:spin_lock_irqsave(主机锁)

int ata_scsi_offline_dev(struct ata_device *dev)

离线连接的 SCSI 设备

参数

struct ata_device *dev

要离线连接 SCSI 设备的 ATA 设备

此函数从 ata_eh_hotplug() 调用,负责使连接到 dev 的 SCSI 设备脱机。调用此函数时会使用主机锁,该锁保护 dev->sdev 免于清除。

锁定:spin_lock_irqsave(主机锁)

返回

如果存在连接的 SCSI 设备,则为 1,否则为 0。

void ata_scsi_remove_dev(struct ata_device *dev)

移除连接的 SCSI 设备

参数

struct ata_device *dev

要移除连接的 SCSI 设备的 ATA 设备

此函数从 ata_eh_scsi_hotplug() 调用,负责移除连接到 dev 的 SCSI 设备。

锁定:内核线程上下文(可能会休眠)。

void ata_scsi_media_change_notify(struct ata_device *dev)

发送介质更改事件

参数

struct ata_device *dev

指向具有介质更改事件的磁盘设备的指针

告诉块层发送介质更改通知事件。

锁定:spin_lock_irqsave(主机锁)

void ata_scsi_hotplug(struct work_struct *work)

热插拔的 SCSI 部分

参数

struct work_struct *work

指向要在其上执行 SCSI 热插拔的 ATA 端口的指针

执行热插拔的 SCSI 部分。它在 EH 完成后从单独的 workqueue 执行。这是必需的,因为 SCSI 热插拔需要正常工作的 EH,并且热拔与使用互斥锁的热插拔同步。

锁定:内核线程上下文(可能会休眠)。

int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, unsigned int id, u64 lun)

用户启动的总线扫描的指示

参数

struct Scsi_Host *shost

要扫描的 SCSI 主机

unsigned int channel

要扫描的通道

unsigned int id

要扫描的 ID

u64 lun

要扫描的 LUN

当用户显式请求总线扫描时,将调用此函数。设置探测挂起标志并调用 EH。

锁定:SCSI 层(我们不在乎)

返回

零。

void ata_scsi_dev_rescan(struct work_struct *work)

启动 scsi_rescan_device()

参数

struct work_struct *work

指向要执行 scsi_rescan_device() 的 ATA 端口的指针

成功执行 ATA 直通 (SAT) 命令后,libata 需要将更改传播到 SCSI 层。

锁定:内核线程上下文(可能会休眠)。

ATA 错误和异常

本章尝试识别 ATA/ATAPI 设备的错误/异常条件,并描述应如何以与实现无关的方式处理它们。

术语“错误”用于描述从设备报告显式错误条件或命令超时的条件。

术语“异常”用于描述不是错误的异常情况(例如,电源或热插拔事件),或描述错误和非错误异常情况。在需要明确区分错误和异常的情况下,使用术语“非错误异常”。

异常类别

异常主要根据传统 taskfile + 总线主控 IDE 接口进行描述。如果控制器为错误报告提供了其他更好的机制,则将这些映射到下面描述的类别应该不困难。

在以下各节中,提到了两个恢复操作 - 重置和重新配置传输。这些将在 EH 恢复操作 中进一步描述。

HSM 违规

当 STATUS 值在发出或执行任何 ATA/ATAPI 命令期间与 HSM 要求不匹配时,会指示此错误。

  • 尝试发出命令时,ATA_STATUS 不包含 !BSY && DRDY && !DRQ。

  • PIO 数据传输期间的 !BSY && !DRQ。

  • 命令完成时的 DRQ。

  • CDB 传输开始后但在 CDB 的最后一个字节传输之前,出现 !BSY && ERR。ATA/ATAPI 标准规定,“在写入命令包的最后一个字节之前,设备不得终止 PACKET 命令并出现错误”,并且状态图不包括此类转换。

在这些情况下,HSM 遭到破坏,并且无法从 STATUS 或 ERROR 寄存器获取有关该错误的更多信息。换句话说,此错误可能是任何原因引起的 - 驱动程序错误、设备故障、控制器和/或电缆故障。

由于 HSM 遭到破坏,因此必须重置以恢复已知状态。降低传输速度的重新配置传输也可能有所帮助,因为传输错误有时会导致此类错误。

ATA/ATAPI 设备错误(非 NCQ / 非 CHECK CONDITION)

这些错误由 ATA/ATAPI 设备检测和报告,指示设备存在问题。对于此类错误,STATUS 和 ERROR 寄存器值有效,并描述了错误情况。请注意,某些 ATA 总线错误由 ATA/ATAPI 设备检测到,并使用与设备错误相同的机制进行报告。这些情况将在本节后面描述。

对于 ATA 命令,此类错误通过在命令执行期间和完成时出现 !BSY && ERR 来指示。

对于 ATAPI 命令,

  • 发出 PACKET 后立即出现 !BSY && ERR && ABRT 表示不支持 PACKET 命令,并且属于此类别。

  • CDB 的最后一个字节传输后出现 !BSY && ERR(==CHK) && !ABRT 表示 CHECK CONDITION,并且不属于此类别。

  • CDB 的最后一个字节传输后出现 !BSY && ERR(==CHK) && ABRT *可能* 表示 CHECK CONDITION,并且不属于此类别。

对于如上检测到的错误,以下错误不是ATA/ATAPI设备错误,而是ATA总线错误,应根据ATA总线错误进行处理。

数据传输期间发生CRC错误

这由ERROR寄存器中的ICRC位指示,表示数据传输期间发生了损坏。在ATA/ATAPI-7之前,标准规定此位仅适用于UDMA传输,但ATA/ATAPI-8草案修订版1f表示该位可能适用于多字DMA和PIO。

数据传输期间或完成时发生ABRT错误

在ATA/ATAPI-7之前,标准规定可以在ICRC错误和设备无法完成命令的情况下设置ABRT。结合MWDMA和PIO传输错误在ATA/ATAPI-7之前不允许使用ICRC位的事实,这似乎暗示ABRT位本身可以指示传输错误。

但是,ATA/ATAPI-8草案修订版1f删除了ICRC错误可以开启ABRT的部分。所以,这是一个灰色地带。这里需要一些启发式方法。

ATA/ATAPI设备错误可以进一步分类如下。

介质错误

这由ERROR寄存器中的UNC位指示。ATA设备仅在一定数量的重试无法恢复数据后才报告UNC错误,因此除了通知上层之外,没有其他太多可以做的事情。

READ和WRITE命令报告第一个失败扇区的CHS或LBA,但ATA/ATAPI标准规定错误完成时传输的数据量是不确定的,因此我们不能假定失败扇区之前的扇区已被传输,因此无法像SCSI那样成功完成这些扇区。

介质已更改/请求介质更改错误

<<TODO: 在这里填写>>

地址错误

这由ERROR寄存器中的IDNF位指示。向上层报告。

其他错误

这可能是无效命令或参数(由ABRT ERROR位指示)或一些其他错误条件。请注意,ABRT位可以指示很多内容,包括ICRC和地址错误。需要启发式方法。

并非所有STATUS/ERROR位都适用于某些命令。这些不适用的位在输出描述中标记为“na”,但在ATA/ATAPI-7之前,找不到“na”的定义。但是,ATA/ATAPI-8草案修订版1f将“N/A”描述如下。

3.2.3.3a N/A

一个关键字,表示该字段在本标准中没有定义的值,不应由主机或设备检查。N/A字段应清除为零。

因此,假设设备将“na”位清除为零,因此无需显式屏蔽,这似乎是合理的。

ATAPI 设备 CHECK CONDITION

ATAPI 设备 CHECK CONDITION 错误由 PACKET 命令传输 CDB 的最后一个字节后,STATUS 寄存器中的 CHK 位(ERR 位)设置指示。对于此类错误,应获取感知数据以收集有关错误的信息。应使用 REQUEST SENSE 数据包命令来获取感知数据。

一旦获取了感知数据,此类错误的处理方式可以与其他 SCSI 错误类似。请注意,感知数据可能指示 ATA 总线错误(例如,感知键 04h HARDWARE ERROR && ASC/ASCQ 47h/00h SCSI PARITY ERROR)。在这种情况下,该错误应被视为 ATA 总线错误,并根据ATA 总线错误进行处理。

ATA 设备错误 (NCQ)

NCQ 命令错误由 NCQ 命令阶段(一个或多个未完成的 NCQ 命令)期间清除 BSY 并设置 ERR 位指示。虽然 STATUS 和 ERROR 寄存器将包含描述错误的有效值,但需要 READ LOG EXT 来清除错误条件,确定哪个命令失败并获取更多信息。

READ LOG EXT 日志页面 10h 报告哪个标签失败以及描述错误的任务文件寄存器值。使用此信息,失败的命令可以作为正常的 ATA 命令错误进行处理,如ATA/ATAPI 设备错误(非 NCQ/非 CHECK CONDITION)中所述,所有其他正在运行的命令必须重试。请注意,此重试不应计算在内 - 如果没有失败的命令,以这种方式重试的命令很可能已经正常完成。

请注意,ATA 总线错误可以报告为 ATA 设备 NCQ 错误。应按照ATA 总线错误中的描述进行处理。

如果 READ LOG EXT 日志页面 10h 失败或报告 NQ,我们就彻底完蛋了。应根据HSM 违规处理此条件。

ATA 总线错误

ATA 总线错误表示数据在通过 ATA 总线(SATA 或 PATA)传输期间发生损坏。此类型的错误可以通过以下方式指示:

  • ATA/ATAPI 设备错误(非 NCQ/非 CHECK CONDITION)中所述的 ICRC 或 ABRT 错误。

  • 具有指示传输错误的错误信息的特定于控制器的错误完成。

  • 在某些控制器上,命令超时。在这种情况下,可能存在一种机制来确定超时是由于传输错误造成的。

  • 未知/随机错误、超时和各种怪异现象。

如上所述,传输错误可能导致各种症状,从设备 ICRC 错误到随机设备锁定,并且在许多情况下,无法判断错误条件是否由于传输错误造成的;因此,在处理错误和超时时,有必要采用某种启发式方法。例如,已知支持的命令重复出现 ABRT 错误很可能表明 ATA 总线错误。

一旦确定可能发生了 ATA 总线错误,降低 ATA 总线传输速度是可以缓解问题的措施之一。有关更多信息,请参见重新配置传输

PCI 总线错误

在通过 PCI(或其他系统总线)传输期间发生的数据损坏或其他故障。对于标准 BMDMA,这由 BMDMA 状态寄存器中的 Error 位指示。必须记录此类型的错误,因为它表明系统存在非常严重的问题。建议重置主机控制器。

延迟完成

当发生超时并且超时处理程序发现超时的命令已成功完成或出现错误时,会发生这种情况。这通常是由中断丢失引起的。必须记录此类型的错误。建议重置主机控制器。

未知错误(超时)

这是当发生超时并且命令仍在处理或主机和设备处于未知状态时发生的情况。发生这种情况时,HSM 可能处于任何有效或无效状态。为了使设备进入已知状态并使其忘记超时的命令,必须进行重置。可以重试超时的命令。

超时也可能是由传输错误引起的。有关更多详细信息,请参阅ATA 总线错误

热插拔和电源管理异常

<<TODO: 在这里填写>>

EH 恢复操作

本节讨论了几种重要的恢复操作。

清除错误条件

许多控制器要求错误处理程序清除其错误寄存器。不同的控制器可能有不同的要求。

对于 SATA,强烈建议在错误处理期间至少清除 SError 寄存器。

重置

在 EH 期间,在以下情况下必须进行重置。

  • HSM 处于未知或无效状态

  • HBA 处于未知或无效状态

  • EH 需要使 HBA/设备忘记正在运行的命令

  • HBA/设备的表现很奇怪

无论错误条件如何,在 EH 期间重置可能是一个好主意,以提高 EH 的鲁棒性。是否重置 HBA 和设备中的一个或两个取决于具体情况,但建议采用以下方案。

  • 当已知 HBA 处于就绪状态但 ATA/ATAPI 设备处于未知状态时,仅重置设备。

  • 如果 HBA 处于未知状态,则重置 HBA 和设备。

HBA 重置是特定于实现的。对于符合 taskfile/BMDMA PCI IDE 的控制器,如果 BMDMA 状态是唯一的 HBA 上下文,则停止活动 DMA 事务可能就足够了。但是,即使主要是 taskfile/BMDMA PCI IDE 的控制器也可能具有特定于实现的重置自身的要求和机制。这必须由特定的驱动程序来解决。

另一方面,ATA/ATAPI 标准详细描述了重置 ATA/ATAPI 设备的方法。

PATA 硬件重置

这是硬件发起的设备重置,通过断言 PATA RESET- 信号来指示。虽然某些硬件提供了允许驱动程序直接调整 RESET- 信号的寄存器,但没有从软件启动硬件重置的标准方法。

软件重置

这是通过将 CONTROL SRST 位打开至少 5 微秒来实现的。PATA 和 SATA 都支持它,但在 SATA 的情况下,这可能需要特定于控制器的支持,因为在 BSY 位仍然设置的情况下,应该传输第二个 Register FIS 以清除 SRST。请注意,在 PATA 上,这将重置通道上的主设备和从设备。

EXECUTE DEVICE DIAGNOSTIC 命令

虽然 ATA/ATAPI 标准没有明确描述,但 EDD 意味着某种程度的重置,可能与软件重置的级别相似。主机端 EDD 协议可以使用正常的命令处理来处理,并且大多数 SATA 控制器应该能够像处理其他命令一样处理 EDD。与软件重置一样,EDD 会影响 PATA 总线上的两个设备。

虽然 EDD 会重置设备,但这不适合错误处理,因为在设置 BSY 时无法发出 EDD,并且不清楚当设备处于未知/怪异状态时它将如何运行。

ATAPI DEVICE RESET 命令

这与软件重置非常相似,只是重置可以限制为所选设备,而不会影响共享电缆的其他设备。

SATA PHY 重置

这是重置 SATA 设备的首选方法。实际上,它与 PATA 硬件重置相同。请注意,这可以使用标准 SCR 控制寄存器来完成。因此,它通常比软件重置更容易实现。

重置设备时要考虑的另一件事是,重置会清除某些配置参数,并且需要在重置后将它们设置为其先前或新调整的值。

受影响的参数是。

  • 使用 INITIALIZE DEVICE PARAMETERS 设置的 CHS(很少使用)

  • 使用 SET FEATURES 设置的参数,包括传输模式设置

  • 使用 SET MULTIPLE MODE 设置的块计数

  • 其他参数 (SET MAX, MEDIA LOCK...)

ATA/ATAPI 标准规定某些参数必须在硬件或软件重置期间保持不变,但没有严格规定所有参数。始终在重置后重新配置所需的参数对于鲁棒性是必需的。请注意,这也适用于从深度睡眠(断电)恢复时。

此外,ATA/ATAPI 标准要求在更新任何配置参数或进行硬件重置后发出 IDENTIFY DEVICE/IDENTIFY PACKET DEVICE,并且结果用于进一步操作。OS 驱动程序需要实现重新验证机制以支持此功能。

重新配置传输

对于 PATA 和 SATA,许多角落都为了廉价的连接器、电缆或控制器而被削减,并且看到高传输错误率是很常见的。可以通过降低传输速度来缓解这种情况。

以下是 Jeff Garzik 建议的一种可能的方案。

如果在 15 分钟内发生超过 $N (3?) 个传输错误,

  • 如果是 SATA,则降低 SATA PHY 速度。如果无法降低速度,

  • 则降低 UDMA 传输速度。如果在 UDMA0,则切换到 PIO4,

  • 降低 PIO 传输速度。如果在 PIO3,则抱怨,但继续

ata_piix 内部

int ich_pata_cable_detect(struct ata_port *ap)

探测主机控制器电缆检测信息

参数

struct ata_port *ap

需要电缆检测信息的端口

从 ATA PCI 设备的 PCI 配置寄存器中读取 80c 电缆指示器。此寄存器通常由固件 (BIOS) 设置。

锁定:无(从调用者继承)。

int piix_pata_prereset(struct ata_link *link, unsigned long deadline)

PATA 主机控制器的 prereset

参数

struct ata_link *link

目标链接

unsigned long deadline

操作的截止时间 jiffies

锁定:无(从调用者继承)。

void piix_set_piomode(struct ata_port *ap, struct ata_device *adev)

初始化主机控制器 PATA PIO 时序

参数

struct ata_port *ap

我们要配置时序的端口

struct ata_device *adev

有问题的驱动器

在主机控制器 PCI 配置空间中,为设备设置 PIO 模式。

锁定:无(从调用者继承)。

void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, int isich)

初始化主机控制器 PATA PIO 时序

参数

struct ata_port *ap

我们要配置时序的端口

struct ata_device *adev

有问题的驱动器

int isich

如果芯片是 ICH 设备,则设置

在主机控制器 PCI 配置空间中,为设备设置 UDMA 模式。

锁定:无(从调用者继承)。

void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev)

初始化主机控制器 PATA DMA 时序

参数

struct ata_port *ap

我们要配置时序的端口

struct ata_device *adev

um

在主机控制器 PCI 配置空间中,为设备设置 MW/UDMA 模式。

锁定:无(从调用者继承)。

void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev)

初始化主机控制器 PATA DMA 时序

参数

struct ata_port *ap

我们要配置时序的端口

struct ata_device *adev

um

在主机控制器 PCI 配置空间中,为设备设置 MW/UDMA 模式。

锁定:无(从调用者继承)。

int piix_check_450nx_errata(struct pci_dev *ata_dev)

检查问题 450NX 设置

参数

struct pci_dev *ata_dev

要检查的 PCI 设备

检查是否存在 450NX 勘误 #19 和勘误 #25。如果找到它们,则返回一个错误代码,以便我们可以关闭 DMA

int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)

向内核服务注册 PIIX ATA PCI 设备

参数

struct pci_dev *pdev

要注册的 PCI 设备

const struct pci_device_id *ent

pdev 匹配的 piix_pci_tbl 中的条目

从内核 PCI 层调用。我们探测组合模式(叹息),然后将控制权交给 libata,让它完成其余的工作。

锁定:继承自 PCI 层(可能会休眠)。

返回

成功时为零,或 -ERRNO 值。

sata_sil 内部

int sil_set_mode(struct ata_link *link, struct ata_device **r_failed)

包装 set_mode 函数

参数

struct ata_link *link

要设置的链接

struct ata_device **r_failed

我们失败时返回的设备

包装 libata 设备设置方法,因为在设置后,我们需要检查结果并进行一些配置工作

void sil_dev_config(struct ata_device *dev)

应用设备/主机特定的勘误修复

参数

struct ata_device *dev

要检查的设备

在 IDENTIFY [PACKET] DEVICE 步骤完成后,并且已知设备存在后,将调用此函数。我们应用两个特定于 Silicon Image 的勘误修复、Seagate 和 Maxtor 修复。

对于某些 Seagate 设备,我们必须将最大扇区数限制在 8K 以下。

对于某些 Maxtor 设备,我们不得将驱动器编程为超过 udma5。

这两个修复都非常悲观。一旦我获得有关这些勘误的更多信息,我将创建一个更详尽的列表,并将修复仅应用于需要它的特定设备/主机/固件。

20040111 - 受 Mod15Write 错误影响的 Seagate 驱动器是怪异的。Maxtor 怪异现象位于 sil_quirks 中,但我保留了原始的悲观修复,原因如下... - 似乎关于它的信息较少,仅从 Windows 驱动程序中收集到一个设备,可能仅影响一个设备。如果能提供更多信息,将不胜感激。 - 但话说回来,UDMA5 也没什么好抱怨的

谢谢

大部分 ATA 知识都来自与 Andre Hedrick (www.linux-ide.org) 的长时间对话,以及长时间思考 ATA 和 SCSI 规范。

感谢 Alan Cox 指出 SATA 和 SCSI 之间的相似之处,以及通常激励我们破解 libata。

libata 的设备检测方法 ata_pio_devchk,以及通常所有早期探测都基于对 Hale Landis 的 ATADRVR 驱动程序 (www.ata-atapi.com) 中探测/重置代码的广泛研究。