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)

分配 target 驱动程序私有数据

参数

struct se_session *se_sess

会话指针。

unsigned int tag_num

发起方和 target 之间的最大未完成命令数。

unsigned int tag_size

target 驱动程序与每个命令关联的私有数据的大小(以字节为单位)。

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 sense 缓冲区的指针

u64 unpacked_lun

用于引用 struct se_lun 的解包 LUN

u32 data_length

fabric 期望的数据传输长度

int task_attr

SAM 任务属性

int data_dir

DMA 数据方向

int flags

来自 target_sc_flags_tables 的命令提交标志

描述

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

如果 fabric 驱动程序调用 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)

准备提交 cmd

参数

struct se_cmd *se_cmd

要准备的命令描述符

unsigned char *cdb

指向 SCSI CDB 的指针

struct scatterlist *sgl

用于单向映射的 struct scatterlist 内存

u32 sgl_count

用于单向映射的 scatterlist 计数

struct scatterlist *sgl_bidi

用于双向 READ 映射的 struct scatterlist 内存

u32 sgl_bidi_count

用于双向 READ 映射的 scatterlist 计数

struct scatterlist *sgl_prot

struct scatterlist 内存保护信息

u32 sgl_prot_count

保护信息的 scatterlist 计数

gfp_t gfp

gfp 分配类型

返回

  • 小于零表示失败。

  • 成功时返回零。

描述

如果返回失败,lio 将调用 queue_status 来完成 cmd。

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 sense 缓冲区的指针

u64 unpacked_lun

用于引用 struct se_lun 的解包 LUN

u32 data_length

fabric 期望的数据传输长度

int task_attr

SAM 任务属性

int data_dir

DMA 数据方向

int flags

来自 target_sc_flags_tables 的命令提交标志

描述

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

这只能从进程上下文中调用,并且当前还假设 target-core 内部分配 fabric 有效负载缓冲区。

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

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

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 sense 缓冲区的指针

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)

验证会话是否接受 cmds 并获取引用

参数

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() 调用导致引用计数降至零时,才返回 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)

等待未完成的 cmds。

参数

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*() 失败时才调用此函数。

Target 支持的用户空间 I/O

用户空间 I/O

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

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

有关环的布局,请参见 .h 文件。 请注意,尽管定义了命令环,但数据区域的细节并未定义。 命令条目中的偏移值指向 mmap 区域内的其他位置。 在命令环之外有单独的空间用于数据缓冲区。 这为移动缓冲区分配,甚至页面翻转或其他分配技术留下了最大的灵活性,而无需更改命令环布局。

安全性:必须假定用户进程是恶意的。 如果它愿意,没有办法阻止它破坏命令环协议,但为了防止其他问题,我们必须始终仅从共享内存区域读取*data*,而不是偏移量或大小。 这适用于命令环条目以及邮箱。 为此需要的额外代码可能带有“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 命令中查找适当的 data_offset。

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

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 cmd 的 iscsi 任务

uint32_t exp_cmdsn

cpu 格式的预期 cmd sn

uint32_t max_cmdsn

cpu 格式的最大 cmd sn

描述

当驱动程序不需要或无法执行较低级别的 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

描述

这应该用于 mgmt 任务,例如登录和 nops,或者如果 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 conn

struct iscsi_hdr *hdr

iscsi 标头

char *data

数据缓冲区

int datalen

数据缓冲区的长度

描述

通过释放 queuecommand 或发送通用命令时分配的任何资源来完成 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

描述

这应该用于 cmd 任务。

必须持有会话 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 conn

描述

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

void iscsi_suspend_tx(struct iscsi_conn *conn)

暂停 iscsi_data_xmit

参数

struct iscsi_conn *conn

iscsi conn 以停止处理 IO。

描述

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

void iscsi_suspend_rx(struct iscsi_conn *conn)

阻止 recvwork 再次运行。

参数

struct iscsi_conn *conn

iscsi conn 以停止。

void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active)

防止排队到 conn。

参数

struct iscsi_cls_conn *cls_conn

iscsi conn ep 绑定到。

bool is_active

conn 是否用于启动,或者这是否用于 EH/终止

描述

实现 ep_disconnect callout 的驱动程序必须调用此选项。 它禁用从 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

如果从驱动程序关闭 callout 调用,则为 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 设置为它们支持的最大总任务数(管理 + 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)

属性显示函数

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)

属性显示函数

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)

属性显示函数

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)

属性显示函数

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 主机的 host number

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 - offset

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

如果从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连接

描述

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

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)

测试我们是否正在读取header

参数

struct iscsi_tcp_conn *tcp_conn

iscsi tcp 连接

描述

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

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

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

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)

xmit正常PDU任务

参数

struct iscsi_task *task

iscsi命令任务

描述

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