target 和 iSCSI 接口指南

简介和概述

待定

Target 核心设备接口

此部分为空,因为没有将 kerneldoc 注释添加到 drivers/target/target_core_device.c。

Target 核心传输接口

void transport_init_session(struct se_session *se_sess)

初始化会话对象

参数

struct se_session *se_sess

会话对象指针。

描述

调用方必须在调用此函数之前将 se_sess 初始化为零。

struct se_session *transport_alloc_session(enum target_prot_op sup_prot_ops)

分配会话对象并初始化它

参数

enum target_prot_op sup_prot_ops

定义支持哪些 T10-PI 模式的位掩码。

int transport_alloc_session_tags(struct se_session *se_sess, unsigned int tag_num, unsigned int tag_size)

分配目标驱动程序私有数据

参数

struct se_session *se_sess

会话指针。

unsigned int tag_num

发起程序和目标之间正在进行的命令的最大数量。

unsigned int tag_size

目标驱动程序与每个命令关联的私有数据的字节大小。

int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, unsigned char *sense, u64 unpacked_lun, u32 data_length, int task_attr, int data_dir, int flags)

初始化 se_cmd

参数

struct se_cmd *se_cmd

要初始化的命令描述符

struct se_session *se_sess

端点的关联 se_sess

unsigned char *sense

指向 SCSI 感知缓冲区的指针

u64 unpacked_lun

用于引用 struct se_lun 的解包 LUN

u32 data_length

结构预期的数据传输长度

int task_attr

SAM 任务属性

int data_dir

DMA 数据方向

int flags

来自 target_sc_flags_tables 的命令提交标志

描述

如果调用方已设置 se_cmd->tag,则支持任务标签。

如果结构驱动程序调用 target_stop_session,则必须检查返回代码并处理失败。对于其他驱动程序,这永远不会失败,并且可以忽略返回代码。

返回

  • 小于零以表示活动 I/O 关闭失败。

  • 成功时返回零。

int target_submit_prep(struct se_cmd *se_cmd, unsigned char *cdb, struct scatterlist *sgl, u32 sgl_count, struct scatterlist *sgl_bidi, u32 sgl_bidi_count, struct scatterlist *sgl_prot, u32 sgl_prot_count, gfp_t gfp)

准备要提交的命令

参数

struct se_cmd *se_cmd

要准备的命令描述符

unsigned char *cdb

指向 SCSI CDB 的指针

struct scatterlist *sgl

用于单向映射的 struct scatterlist 内存

u32 sgl_count

单向映射的 scatterlist 计数

struct scatterlist *sgl_bidi

用于双向读取映射的 struct scatterlist 内存

u32 sgl_bidi_count

双向读取映射的 scatterlist 计数

struct scatterlist *sgl_prot

struct scatterlist 内存保护信息

u32 sgl_prot_count

保护信息的 scatterlist 计数

gfp_t gfp

gfp 分配类型

返回

  • 小于零以表示失败。

  • 成功时返回零。

描述

如果返回失败,则 lio 将调用方的 queue_status 完成命令。

void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, unsigned char *cdb, unsigned char *sense, u64 unpacked_lun, u32 data_length, int task_attr, int data_dir, int flags)

查找解包的 LUN 并提交未初始化的 se_cmd

参数

struct se_cmd *se_cmd

要提交的命令描述符

struct se_session *se_sess

端点的关联 se_sess

unsigned char *cdb

指向 SCSI CDB 的指针

unsigned char *sense

指向 SCSI 感知缓冲区的指针

u64 unpacked_lun

用于引用 struct se_lun 的解包 LUN

u32 data_length

结构预期的数据传输长度

int task_attr

SAM 任务属性

int data_dir

DMA 数据方向

int flags

来自 target_sc_flags_tables 的命令提交标志

描述

如果调用方已设置 se_cmd->tag,则支持任务标签。

此函数只能从进程上下文中调用,并且目前假设目标核心内部分配 fabric 有效负载缓冲区。

它还假设内部目标核心 SGL 内存分配。

此函数只能由在关闭期间执行自己的同步且不使用 target_stop_session 的驱动程序使用。如果发生故障,此函数将调用 fabric 驱动程序的 queue_status,并返回 CHECK_CONDITION。

int target_submit(struct se_cmd *se_cmd)

执行最终初始化并将 cmd 提交到 LIO 核心

参数

struct se_cmd *se_cmd

要提交的命令描述符

描述

必须在 cmd 上调用过 target_submit_prep 或类似函数,并且必须从进程上下文中调用此函数。

int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, unsigned char *sense, u64 unpacked_lun, void *fabric_tmr_ptr, unsigned char tm_type, gfp_t gfp, u64 tag, int flags)

查找解包的 LUN 并提交用于 TMR CDB 的未初始化 se_cmd

参数

struct se_cmd *se_cmd

要提交的命令描述符

struct se_session *se_sess

端点的关联 se_sess

unsigned char *sense

指向 SCSI 感知缓冲区的指针

u64 unpacked_lun

用于引用 struct se_lun 的解包 LUN

void *fabric_tmr_ptr

TMR 请求的 fabric 上下文

unsigned char tm_type

TM 请求的类型

gfp_t gfp

调用者的 gfp 类型

u64 tag

TMR_ABORT_TASK 的引用任务标记

int flags

提交 cmd 标志

描述

可从所有上下文中调用。

int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)

验证会话是否接受 cmd 并获取 ref

参数

struct se_cmd *se_cmd

要添加的命令描述符

bool ack_kref

发出 fabric 将执行 ack target_put_sess_cmd() 的信号

int target_put_sess_cmd(struct se_cmd *se_cmd)

减少命令引用计数

参数

struct se_cmd *se_cmd

要从中删除引用的命令

描述

当且仅当此 target_put_sess_cmd() 调用导致 refcount 降至零时返回 1。否则返回零。

void target_stop_cmd_counter(struct target_cmd_counter *cmd_cnt)

停止向计数器添加新的 IO。

参数

struct target_cmd_counter *cmd_cnt

要停止的计数器

void target_stop_session(struct se_session *se_sess)

停止将新的 IO 排队到会话中。

参数

struct se_session *se_sess

要停止的会话

void target_wait_for_cmds(struct target_cmd_counter *cmd_cnt)

等待未完成的 cmd。

参数

struct target_cmd_counter *cmd_cnt

要等待活动的 I/O 的计数器。

void target_wait_for_sess_cmds(struct se_session *se_sess)

等待未完成的命令

参数

struct se_session *se_sess

要等待活动 I/O 的会话

bool transport_wait_for_tasks(struct se_cmd *cmd)

设置 CMD_T_STOP 并等待 t_transport_stop_comp

参数

struct se_cmd *cmd

要等待的命令

int target_send_busy(struct se_cmd *cmd)

将 SCSI BUSY 状态发送回启动器

参数

struct se_cmd *cmd

要发送 BUSY 回复的 SCSI 命令。

注意

仅当 target_submit_cmd*() 失败时才调用此函数。

目标支持的用户空间 I/O

用户空间 I/O

定义一个共享内存接口,供 LIO 将 SCSI 命令和数据传递给用户空间进行处理。这是为了允许后端过于复杂而无法实现内核支持。

它使用 UIO 框架为我们完成大量设备创建和内省工作。

有关环的布局方式,请参阅 .h 文件。请注意,虽然定义了命令环,但数据区域的具体细节没有定义。命令条目中的偏移值指向 mmap 区域内部的其他位置。在命令环外部有单独的空间用于数据缓冲区。这为移动缓冲区分配(甚至页面翻转或其他分配技术)提供了最大的灵活性,而不会更改命令环布局。

安全性:必须假定用户进程是恶意的。无法阻止它破坏命令环协议(如果它愿意),但为了防止其他问题,我们必须只从共享内存区域读取数据,而不是偏移量或大小。这适用于命令环条目以及邮箱。此额外代码可能带有“UAM”注释。

环设计

mmap 区域分为三个部分:1) 邮箱(下面的 struct tcmu_mailbox);2) 命令环;3) 命令环之外的所有内容(数据)。

邮箱告诉用户空间命令环相对于共享内存区域起点的偏移量以及命令环的大小。

内核通过将 struct tcmu_cmd_entry 放入环中、更新 mailbox->cmd_head,并通过 UIO 的中断机制触发用户空间来将 SCSI 命令传递给用户空间。

tcmu_cmd_entry 包含一个标头。如果标头类型为 PAD,用户空间应跳过 hdr->length 字节(mod cmdr_size)以查找下一个 cmd_entry。

否则,该条目将包含指向 mmap 区域的偏移量,该区域包含 cdb 和数据缓冲区,后者可通过 iov 数组访问。iov 地址也是共享区域的偏移量。

当用户空间完成处理命令时,设置 entry->rsp.scsi_status,如果适用,则填充 rsp.sense_buffer,并将 mailbox->cmd_tail 设置为等于旧的 cmd_tail 加上 hdr->length,mod cmdr_size。如果 cmd_tail 不等于 cmd_head,它应以相同方式处理下一个数据包,依此类推。

iSCSI 辅助函数

void iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t, struct iscsi_data *hdr)

初始化 Data-Out

参数

struct iscsi_task *task

SCSI 命令任务

struct iscsi_r2t_info *r2t

R2T 信息

struct iscsi_data *hdr

iscsi 数据输出 PDU

说明

在此 R2T 序列中初始化 Data-Out,并在此 SCSI 命令中查找正确的数据偏移量。

此函数在获取连接锁时调用。

void __iscsi_put_task(struct iscsi_task *task)

减少任务的引用计数

参数

struct iscsi_task *task

要减少引用计数的 iscsi_task

描述

调用时必须持有 back_lock,以防它释放任务。

void iscsi_complete_scsi_task(struct iscsi_task *task, uint32_t exp_cmdsn, uint32_t max_cmdsn)

正常完成 SCSI 任务

参数

struct iscsi_task *task

SCSI 命令的 iscsi 任务

uint32_t exp_cmdsn

CPU 格式的预期命令序列号

uint32_t max_cmdsn

CPU 格式的最大命令序列号

描述

当驱动程序不需要或无法执行较低级别的 PDU 处理时,会使用此方法。

使用会话 back_lock 调用

struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)

通过 itt 查找任务

参数

struct iscsi_conn *conn

iSCSI 连接

itt_t itt

itt

描述

这应该用于管理任务(如登录和 NOP),或者当 LDD 的 itt 空间不包含会话期限时。

必须持有会话 back_lock。

int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, char *data, int datalen)

完成 PDU

参数

struct iscsi_conn *conn

iSCSI 连接

struct iscsi_hdr *hdr

iSCSI 标头

char *data

数据缓冲区

int datalen

数据缓冲区的长度

描述

通过释放 queuecommand 或 send generic 时分配的任何资源来完成 PDU 处理。必须持有会话 back_lock,并且必须调用 verify itt。

struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt)

通过 itt 查找 ctask

参数

struct iscsi_conn *conn

iSCSI 连接

itt_t itt

itt

描述

这应该用于命令任务。

必须持有会话 back_lock。

void iscsi_requeue_task(struct iscsi_task *task)

将任务重新排队以从会话工作队列运行

参数

struct iscsi_task *task

要重新排队的任务

描述

调用方必须已对要重新排队的任务进行了引用。

void iscsi_suspend_queue(struct iscsi_conn *conn)

挂起 iscsi_queuecommand

参数

struct iscsi_conn *conn

停止在其上排队 IO 的 iscsi 连接

描述

这会获取会话 frwd_lock 以确保没有人在 xmit_task/queuecommand 中,然后设置挂起以防止新命令排队。这只需要由需要同步路径的卸载驱动程序调用,例如 ep 断开连接和 iscsi_queuecommand/xmit_task。要再次启动 IO,当处于 FFP 时,libiscsi 将调用 iscsi_start_tx 和 iscsi_unblock_session。

void iscsi_suspend_tx(struct iscsi_conn *conn)

挂起 iscsi_data_xmit

参数

struct iscsi_conn *conn

停止在其上处理 IO 的 iSCSI 连接。

描述

此函数设置挂起位以防止 iscsi_data_xmit 发送新的 IO,并且如果工作在 xmit 线程上排队,它将等待它完成。

void iscsi_suspend_rx(struct iscsi_conn *conn)

防止 recvwork 再次运行。

参数

struct iscsi_conn *conn

要停止的 iSCSI 连接。

void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active)

防止向连接排队。

参数

struct iscsi_cls_conn *cls_conn

iSCSI 连接 EP 绑定到。

bool is_active

连接是否用于启动,还是用于 EH/终止

描述

必须由实现 ep_disconnect 调用驱动程序调用。它会禁用来自 libiscsi 的连接排队,为 ep_disconnect 调用做准备。

int iscsi_eh_session_reset(struct scsi_cmnd *sc)

删除会话并尝试重新登录

参数

struct scsi_cmnd *sc

SCSI 命令

描述

此函数将等待重新登录、来自用户空间的会话终止或恢复/替换超时。

int iscsi_eh_recover_target(struct scsi_cmnd *sc)

重置目标,并可能重置会话

参数

struct scsi_cmnd *sc

SCSI 命令

描述

这将尝试发送热目标重置。如果失败,我们将升级到 ERL0 会话恢复。

int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev)

将主机添加到系统

参数

struct Scsi_Host *shost

SCSI 主机

struct device *pdev

父设备

描述

部分卸载和软件 iSCSI 驱动程序应调用此函数,以便将主机添加到系统中。

struct Scsi_Host *iscsi_host_alloc(const struct scsi_host_template *sht, int dd_data_size, bool xmit_can_sleep)

分配主机和驱动程序数据

参数

const struct scsi_host_template *sht

SCSI 主机模板

int dd_data_size

驱动程序主机数据大小

bool xmit_can_sleep

一个布尔值,指示 LLD 是否将从工作队列中排队 IO

描述

部分卸载和软件 iSCSI 驱动程序应调用此函数。要访问驱动程序特定的内存,请使用 iscsi_host_priv() 宏。

void iscsi_host_remove(struct Scsi_Host *shost, bool is_shutdown)

移除主机和会话

参数

struct Scsi_Host *shost

SCSI 主机

bool is_shutdown

如果从驱动程序关闭回调中调用,则为 true

描述

如果有任何会话剩余,这将启动移除并等待完成。

struct iscsi_cls_session *iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, uint16_t cmds_max, int dd_size, int cmd_task_size, uint32_t initial_cmdsn, unsigned int id)

创建 iSCSI cls 会话以及主机和会话

参数

struct iscsi_transport *iscsit

iSCSI 传输模板

struct Scsi_Host *shost

SCSI 主机

uint16_t cmds_max

会话可以排队

int dd_size

私有驱动程序数据大小,添加到会话分配大小

int cmd_task_size

LLD 任务私有数据大小

uint32_t initial_cmdsn

初始 CmdSN

unsigned int id

要添加到此会话的目标 ID

描述

这可以被每个 SCSI 主机分配一个会话的软件 iscsi_transports 使用。

调用者应将 cmds_max 设置为它们支持的最大任务总数(mgmt + scsi)。iSCSI 层为 nop 处理和登录/注销请求保留 ISCSI_MGMT_CMDS_MAX 任务。

void iscsi_session_free(struct iscsi_cls_session *cls_session)

释放 iSCSI 会话及其资源

参数

struct iscsi_cls_session *cls_session

iSCSI 会话

void iscsi_session_teardown(struct iscsi_cls_session *cls_session)

销毁会话和 cls_session

参数

struct iscsi_cls_session *cls_session

iSCSI 会话

struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, uint32_t conn_idx)

创建 iscsi_cls_conn 和 iscsi_conn

参数

struct iscsi_cls_session *cls_session

iSCSI_cls_session

int dd_size

私有驱动程序数据大小

uint32_t conn_idx

cid

void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)

拆卸 iSCSI 连接

参数

struct iscsi_cls_conn *cls_conn

iSCSI 类连接

描述

TODO: 我们可能需要将其转换为两步过程,如 scsi-mls remove + put host

iSCSI 启动信息

struct iscsi_boot_kobj *iscsi_boot_create_target(struct iscsi_boot_kset *boot_kset, int index, void *data, ssize_t (*show)(void *data, int type, char *buf), umode_t (*is_visible)(void *data, int type), void (*release)(void *data))

创建启动目标 sysfs 目录

参数

struct iscsi_boot_kset *boot_kset

启动 kset

int index

目标 ID

void *data

目标的驱动程序特定数据

ssize_t (*show) (void *data, int type, char *buf)

属性 show 函数

umode_t (*is_visible) (void *data, int type)

属性可见性函数

void (*release) (void *data)

释放函数

注意

当目标kobject的所有引用都被释放后,引导 sysfs 库将释放为调用者传入的数据。

struct iscsi_boot_kobj *iscsi_boot_create_initiator(struct iscsi_boot_kset *boot_kset, int index, void *data, ssize_t (*show)(void *data, int type, char *buf), umode_t (*is_visible)(void *data, int type), void (*release)(void *data))

创建引导发起程序 sysfs 目录

参数

struct iscsi_boot_kset *boot_kset

启动 kset

int index

发起程序 ID

void *data

驱动程序特定数据

ssize_t (*show) (void *data, int type, char *buf)

属性 show 函数

umode_t (*is_visible) (void *data, int type)

属性可见性函数

void (*release) (void *data)

释放函数

注意

当所有对发起程序 kobject 的引用都被释放后,引导 sysfs 库将释放为调用者传入的数据。

struct iscsi_boot_kobj *iscsi_boot_create_ethernet(struct iscsi_boot_kset *boot_kset, int index, void *data, ssize_t (*show)(void *data, int type, char *buf), umode_t (*is_visible)(void *data, int type), void (*release)(void *data))

创建引导以太网 sysfs 目录

参数

struct iscsi_boot_kset *boot_kset

启动 kset

int index

以太网设备 ID

void *data

驱动程序特定数据

ssize_t (*show) (void *data, int type, char *buf)

属性 show 函数

umode_t (*is_visible) (void *data, int type)

属性可见性函数

void (*release) (void *data)

释放函数

注意

当所有对以太网 kobject 的引用都被释放后,引导 sysfs 库将释放为调用者传入的数据。

struct iscsi_boot_kobj *iscsi_boot_create_acpitbl(struct iscsi_boot_kset *boot_kset, int index, void *data, ssize_t (*show)(void *data, int type, char *buf), umode_t (*is_visible)(void *data, int type), void (*release)(void *data))

创建引导 acpi 表 sysfs 目录

参数

struct iscsi_boot_kset *boot_kset

启动 kset

int index

未使用

void *data

驱动程序特定数据

ssize_t (*show)(void *data, int type, char *buf)

属性 show 函数

umode_t (*is_visible)(void *data, int type)

属性可见性函数

void (*release)(void *data)

释放函数

注意

当所有对 acpitbl kobject 的引用都被释放后,引导 sysfs 库将释放为调用者传入的数据。

struct iscsi_boot_kset *iscsi_boot_create_kset(const char *set_name)

创建根 sysfs 树

参数

const char *set_name

根目录名称

struct iscsi_boot_kset *iscsi_boot_create_host_kset(unsigned int hostno)

为 scsi 主机创建根 sysfs 树

参数

unsigned int hostno

scsi 主机的主机号

void iscsi_boot_destroy_kset(struct iscsi_boot_kset *boot_kset)

销毁 kset 及其下的 kobject

参数

struct iscsi_boot_kset *boot_kset

启动 kset

描述

这将移除 kset 及其下的 kobject 和属性。

iSCSI TCP 接口

int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, unsigned int offset, size_t len)

以 sendfile 方式接收 TCP 数据

参数

read_descriptor_t *rd_desc

读取描述符

struct sk_buff *skb

套接字缓冲区

unsigned int offset

skb 中的偏移量

size_t len

skb->len - 偏移量

int iscsi_sw_sk_state_check(struct sock *sk)

检查套接字状态

参数

struct sock *sk

套接字

描述

如果套接字处于 CLOSE 或 CLOSE_WAIT 状态,如果仍有数据挂起,我们不应关闭连接。

必须使用 sk_callback_lock 调用。

void iscsi_sw_tcp_write_space(struct sock *sk)

当有更多输出缓冲区空间可用时调用

参数

struct sock *sk

套接字空间可用于

int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment)

传输段

参数

struct iscsi_tcp_conn *tcp_conn

iSCSI TCP 连接

struct iscsi_segment *segment

要传输的缓冲区

描述

此函数传输网络层将接受的缓冲区中的尽可能多的数据,并返回传输的字节数。

如果启用了 CRC 哈希,该函数将在传输过程中计算哈希。当整个段传输完毕后,它将检索哈希值并将其发送。

int iscsi_sw_tcp_xmit(struct iscsi_conn *conn)

TCP 传输

参数

struct iscsi_conn *conn

iSCSI 连接

int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn)

返回排队等待 xmit 的字节数

参数

struct iscsi_conn *conn

iSCSI 连接

int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment, int recv, unsigned copied)

检查段是否完成

参数

struct iscsi_tcp_conn *tcp_conn

iscsi tcp 连接

struct iscsi_segment *segment

要检查的 iscsi 段

int recv

如果从接收路径调用此函数,则设置为 1

unsigned copied

复制的字节数

描述

检查我们是否已完成接收此段。如果接收缓冲区已满但我们期望更多数据,请转到散列表中的下一个条目。

如果我们收到的数据量不是 4 的倍数,我们也会透明地接收填充字节。

此函数必须是可重入的。

void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)

准备用于 hdr 接收的段

参数

struct iscsi_tcp_conn *tcp_conn

要准备的 iscsi 连接

描述

此函数始终为哈希参数传递 NULL,因为当调用此函数时,我们还不知道标头的最终大小,并希望延迟摘要处理,直到我们知道为止。

void iscsi_tcp_cleanup_task(struct iscsi_task *task)

释放 tcp_task 资源

参数

struct iscsi_task *task

iscsi 任务

描述

必须使用会话 back_lock 调用

int iscsi_tcp_recv_segment_is_hdr(struct iscsi_tcp_conn *tcp_conn)

测试我们是否正在读取标头

参数

struct iscsi_tcp_conn *tcp_conn

iscsi tcp 连接

描述

如果当前正在处理或设置为处理标头,则返回非零值。

int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb, unsigned int offset, bool offloaded, int *status)

处理 skb

参数

struct iscsi_conn *conn

iSCSI 连接

struct sk_buff *skb

带有标头和/或数据段的网络缓冲区

unsigned int offset

skb 中的偏移量

bool offloaded

指示传输是否被卸载的布尔值

int *status

iscsi TCP 状态结果

描述

将在 status 中返回传输状态。并返回复制的字节数。

int iscsi_tcp_task_init(struct iscsi_task *task)

初始化 iSCSI SCSI_READ 或 SCSI_WRITE 命令

参数

struct iscsi_task *task

SCSI 命令任务

int iscsi_tcp_task_xmit(struct iscsi_task *task)

传输正常 PDU 任务

参数

struct iscsi_task *task

iscsi 命令任务

描述

当一切传输成功时,我们应该返回 0,如果队列中仍然有数据,则返回 -EAGAIN,对于任何其他类型的错误,则返回 != 0。