libATA 开发者指南

作者:

Jeff Garzik

简介

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

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

libata 驱动程序 API

为每个低级 libata 硬件驱动程序定义了 struct ata_port_operations,它控制低级驱动程序如何与 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 命令之前调用的钩子。当 libata 构建了可能模式的掩码时,将调用可选的 ->mode_filter() 钩子。这将传递给 ->mode_filter() 函数,该函数应在筛选掉由于硬件限制而不合适的模式后返回有效模式的掩码。使用此接口添加模式是无效的。

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

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

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

任务文件读取/写入

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() 将给定的任务文件加载到硬件寄存器/DMA 缓冲区中。调用 ->tf_read() 读取硬件寄存器/DMA 缓冲区,以获取当前的任务文件寄存器值集。大多数基于任务文件的硬件(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 命令。大多数基于任务文件的硬件的驱动程序都使用 ata_sff_exec_command() 作为此钩子。

每个命令 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);

从硬件读取状态/备用状态 ATA 阴影寄存器。在某些硬件上,读取状态寄存器具有清除中断条件的副作用。大多数基于任务文件的硬件的驱动程序都使用 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 的设备上没有意义。

大多数基于任务文件的硬件的驱动程序都使用 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() 钩子。

高级任务文件钩子

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

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

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

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

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

将调用 “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 一对一地映射到 NCQ 标签 1-1。

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

如何发出命令

内部命令

一旦分配了 qc,就会初始化要执行的命令的任务文件。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->scsidone 以在 qc 完成时通知上层。转换完成后,将使用 ata_qc_issue() 发出 qc。

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

如何处理命令

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

目前使用了 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. ATA_QCFLAG_ACTIVE 从 qc->flags 中清除。

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

  4. __ata_qc_complete() 被调用,它执行以下操作:

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

    2. ap->active_tagqc->tag 被污染。

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

    4. qc 通过清除 ap->qactive 中的相应位来解除分配。

因此,它基本上通知上层并解除分配 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。这将我们引向具有部分完成的 qc 的 ata_scsi_error()

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,但它有效。

一旦控制到达此处,qc 会通过显式调用 __ata_qc_complete() 来解除分配。然后,发出用于 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** 打包成一个无符号整数 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 任务文件寄存器集

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

锁定:无。

返回

设备类型,ATA_DEV_ATAATA_DEV_ATAPIATA_DEV_PMPATA_DEV_ZAC,或失败事件中的 ATA_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

进入标识设备页的偏移量

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

进入标识设备页的偏移量

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:预 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 控制器和预 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. 总线探测信号量。

返回

成功时返回 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

操作的截止时间(节拍数)

int (*check_ready)(struct ata_link *link)

检查链接就绪状态的回调

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

锁定:EH 上下文。

返回

如果 linkdeadline 之前准备就绪,则返回 0;否则返回 -errno。

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

准备重置

参数

struct ata_link *link

要重置的 ATA 链接

unsigned long deadline

操作的截止时间(节拍数)

即将重置 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. 总线探测信号量。

返回

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

int ata_std_qc_defer(struct ata_queued_cmd *qc)

检查是否需要延迟 qc

参数

struct ata_queued_cmd *qc

有问题的 ATA 命令

非 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 完全初始化。此函数启动端口,向 ATA 和 SCSI 层注册 host,并探测已注册的设备。

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

返回

成功时返回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 上下文。

根据 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 任务文件读取块地址

参数

const struct ata_taskfile *tf

感兴趣的 ATA 任务文件

struct ata_device *dev

ATA 设备 tf 所属

锁定:无。

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 任务文件

参数

struct ata_queued_cmd *qc

与要构建的任务文件关联的元数据

u64 block

块地址

u32 n_block

块数

unsigned int tf_flags

RW/FUA 等...

int cdl

命令持续时间限制索引

int class

IO 优先级类

锁定:无。

为命令 qc 构建 ATA 任务文件,用于由 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。此函数还会为 pre-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

操作的截止时间(节拍数)

int (*check_ready)(struct ata_link *link)

检查链接就绪状态的回调

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

对于 ATA_TMOUT_FF_WAIT,允许瞬态 -ENODEV 情况。

锁定:EH 上下文。

返回

如果 linkdeadline 之前准备就绪,则返回 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)

计算给定设备支持的传输掩码

参数

struct ata_device *dev

要计算传输掩码的设备

计算 dev 支持的传输掩码,并将其存储在 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. 总线探测信号量。

返回

成功时返回 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

s/g 表中的元素数。

初始化 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 位字的数目。

如果需要从低位字节顺序转换为本机 CPU 字节顺序,或者反之亦然,则交换 16 位字的一半。

锁定:从调用方继承。

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 继承。只要继承链中没有循环,这种情况可以根据需要进行多次。

当主机启动时,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 端口

如果调用者为 ap 释放 EH 所有权。调用者必须之前使用 ata_eh_acquire() 获取 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

激活快速排空

如果 fastdrain 非零并且之前 EH 未挂起,则设置 ATA_PFLAG_EH_PENDING 并激活快速排空。快速排空确保 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 下直接操作 host 字段。在 libsas 情况下,我们需要在 ha->level 上保持锁以协调这些事件。

锁定: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。必须首先调用冻结操作,因为某些硬件在访问任务文件寄存器之前需要特殊操作。

锁定: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。对于由于不相关故障而重试的命令(qc->err_mask 为零),scmd->allowed 会递增。

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 设备(可以为 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 设备(可以为 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

要对其执行 REQUEST_SENSE_SENSE_DATA_EXT 的 qc

在设备报告 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 链接

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

锁定:无。

unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc)

分析失败 qc 的任务文件

参数

struct ata_queued_cmd *qc

要分析的 qc

分析 qc 的任务文件并进一步确定失败原因。如果可用,此函数还会请求 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任何命令的超时或 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。

  5. 如果在过去 10 分钟内发生超过 3 次 ATA_BUS 或 TOUT_HSM 错误,或者超过 6 次 UNK_DEV 错误,则降低速度。

锁定:从调用方继承。

返回

ATA_EH_SPDN_* 标志的逻辑或运算结果。

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** 失败的原因,并确定需要哪些恢复操作。此函数还会设置更详细的 AC_ERR_* 值,并为 ATAPI CHECK SENSE 填充感知数据。

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

void ata_eh_autopsy(struct ata_port *ap)

分析错误并确定恢复操作

参数

struct ata_port *ap

要执行检查的主机端口

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

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

const char *ata_get_cmd_name(u8 command)

获取 ATA 命令的名称

参数

u8 command

要获取名称的 ATA 命令代码

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

锁定:无

向用户报告错误处理

参数

struct ata_link *link

ATA 链接 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. 总线探测信号量。

返回

成功时返回 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

要配置电源管理的链接

enum ata_lpm_policy policy

链接电源管理策略

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

预重置方法(可以为 NULL)

ata_reset_fn_t softreset

软重置方法(可以为 NULL)

ata_reset_fn_t hardreset

硬重置方法(可以为 NULL)

ata_postreset_fn_t postreset

后重置方法(可以为 NULL)

struct ata_link **r_failed_link

失败链接的输出参数

这是 libata 异常处理的开始和结束、阴和阳、核心和灵魂。在进入时,在链接的 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

预重置方法(可以为 NULL)

ata_reset_fn_t softreset

软重置方法(可以为 NULL)

ata_reset_fn_t hardreset

硬重置方法(可以为 NULL)

ata_postreset_fn_t 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) 映射。如果未使用此映射,则可能会出现磁盘无法启动的情况。

锁定:由 SCSI 层定义。我们并不真正关心。

返回

零。

void ata_scsi_unlock_native_capacity(struct scsi_device *sdev)

解锁原生容量

参数

struct scsi_device *sdev

要调整设备容量的 SCSI 设备

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

锁定:由 SCSI 层定义。可能会休眠。

bool ata_scsi_dma_need_drain(struct request *rq)

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

参数

struct request *rq

要检查的请求

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

锁定:无。

返回

是则为 1,否则为 0。

int ata_scsi_slave_alloc(struct scsi_device *sdev)

SCSI 设备的早期设置

参数

struct scsi_device *sdev

要检查的 SCSI 设备

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

锁定:由 SCSI 层定义。我们并不真正关心。

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

设置 SCSI 设备属性

参数

struct scsi_device *sdev

要检查的 SCSI 设备

struct queue_limits *lim

队列限制

在实际开始读取和写入设备之前,会调用此函数来配置某些 SCSI 中间层行为。

锁定:由 SCSI 层定义。我们并不真正关心。

void ata_scsi_slave_destroy(struct scsi_device *sdev)

SCSI 设备即将被销毁

参数

struct scsi_device *sdev

要销毁的 SCSI 设备

由于热插拔/热拔出,sdev 即将被销毁。如果此拔出是由 libata 发起的(如 NULL dev->sdev 所指示),则此函数不必执行任何操作。否则,SCSI 层启动的正在进行热拔出。清除 dev->sdev,计划设备进行 ATA 分离并调用 EH。

锁定:由 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 设备的总体效果。

锁定: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 直通命令。

使用 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

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

锁定:由 SCSI 层定义。我们并不真正关心。

返回

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

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

HDIO_DRIVE_CMD ioctl 的处理程序

参数

struct scsi_device *scsidev

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

void __user *arg

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

锁定:由 SCSI 层定义。我们并不真正关心。

返回

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

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

HDIO_DRIVE_TASK ioctl 的处理程序

参数

struct scsi_device *scsidev

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

void __user *arg

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

锁定:由 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 代码

u8 *ascq

我们将要填充的附加 sense 代码限定符

将 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 块。描述符格式用于容纳 LBA48 块地址。

锁定:无。

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 读/写命令转换为 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)

转换然后向 ATA 设备发出 SCSI 命令

参数

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 参数(actor)返回 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 输出将发送到此缓冲区。

返回可用的查询 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

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

锁定: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 读/写错误恢复页

参数

u8 *buf

输出缓冲区

bool changeable

是否请求可更改的参数

生成一个通用的 MODE SENSE 读/写错误恢复页。

锁定:无。

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 任务文件

参数

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 总线上,并使用该映射确定与要发送的 SCSI 命令关联的 ata_device。

锁定:spin_lock_irqsave(主机锁)

返回

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

unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)

将 ATA 直通 CDB 转换为任务文件

参数

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 应该按排序顺序排列,且不应重叠。

注意

这与将 LBA(S) 添加到 NV 缓存固定集中的格式相同

返回

复制到 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

错误时指示失败字段的输出参数

准备一个任务文件来修改设备的缓存信息。

锁定:无。

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

错误时指示失败字段的输出参数

准备一个任务文件来修改设备的缓存信息。

锁定:无。

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 任务文件。假设此调用仅用于直接访问设备(例如磁盘)。其他设备类型不应有块描述符。

锁定: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 完成后从单独的工作队列执行。这是必要的,因为 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 设备存在的错误/异常情况,并描述应如何以与实现无关的方式处理它们。

“错误”一词用于描述以下情况:设备报告显式错误条件或命令超时。

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

异常类别

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

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

HSM 违规

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

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

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

  • 命令完成时 DRQ。

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

在这些情况下,HSM 受到违反,并且无法从 STATUS 或 ERROR 寄存器获取有关错误的太多信息。也就是说,此错误可能是任何情况 - 驱动程序错误、设备故障、控制器和/或电缆故障。

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

ATA/ATAPI 设备错误(非 NCQ / 非检查条件)

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

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

对于 ATAPI 命令,

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

  • 在 CDB 的最后一个字节传输后出现 !BSY && ERR(==CHK) && !ABRT,表示检查条件,不属于此类错误。

  • 在 CDB 的最后一个字节传输后出现 !BSY && ERR(==CHK) && ABRT,*可能*表示检查条件,不属于此类错误。

在上面检测到的错误中,以下错误不是 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 不适用

一个关键字,表示字段在本标准中没有定义值,主机或设备不应检查该值。不适用字段应清零。

因此,假设设备将“na”位清零是合理的,因此不需要显式屏蔽。

ATAPI 设备检查条件

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

获取感知数据后,可以像处理其他 SCSI 错误一样处理此类错误。请注意,感知数据可能指示 ATA 总线错误(例如,感知键 04h 硬件错误 && ASC/ASCQ 47h/00h SCSI 奇偶校验错误)。在这种情况下,该错误应被视为 ATA 总线错误,并根据ATA 总线错误进行处理。

ATA 设备错误 (NCQ)

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

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

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

如果 READ LOG EXT 日志页 10h 失败或报告 NQ,我们就彻底完蛋了。这种情况应根据 HSM 违规进行处理。

ATA 总线错误

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

  • ATA/ATAPI 设备错误(非 NCQ / 非检查条件)中所述的 ICRC 或 ABRT 错误。

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

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

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

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

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

PCI 总线错误

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

延迟完成

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

未知错误(超时)

这是在发生超时且命令仍在处理或主机和设备处于未知状态时发生的情况。发生这种情况时,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 位打开至少 5us 来实现的。PATA 和 SATA 都支持它,但在 SATA 的情况下,这可能需要控制器特定的支持,因为在 BSY 位仍然设置的情况下,应传输第二个用于清除 SRST 的 Register FIS。请注意,在 PATA 上,这将复位通道上的主设备和从设备。

执行设备诊断命令

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

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

ATAPI 设备复位命令

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

SATA 物理层复位

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

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

受影响的参数是。

  • 使用初始化设备参数设置的 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 物理层速度。如果速度无法降低,

  • 则降低 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 主机控制器的预复位

参数

struct ata_link *link

目标链路

unsigned long deadline

操作的截止时间(节拍数)

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

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

在主机控制器 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

在主机控制器 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

piix_pci_tbl 中与 pdev 匹配的条目

从内核 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)中的探测/重置代码的广泛研究。