SAS层¶
SAS层是一个管理SAS LLDD的管理基础设施。它位于SCSI核心和SAS LLDD之间。布局如下:SCSI核心关注SAM/SPC问题,而SAS LLDD+定序器关注phy/OOB/链路管理,SAS层关注以下内容:
SAS Phy/端口/HA事件管理(LLDD生成,SAS层处理),
SAS端口管理(创建/销毁),
SAS域发现和重新验证,
SAS域设备管理,
SCSI主机注册/注销,
向SCSI核心(SAS)或libata(SATA)注册设备,以及
扩展器管理并将扩展器控制导出到用户空间。
SAS LLDD是一个PCI设备驱动程序。它关注phy/OOB管理以及特定于供应商的任务,并向SAS层生成事件。
SAS层执行SAS 1.1规范中概述的大部分SAS任务。
sas_ha_struct向SAS层描述了SAS LLDD。它的大部分由SAS层使用,但LLDD需要初始化一些字段。
初始化硬件后,从probe()函数中调用sas_register_ha()。它会将LLDD注册到SCSI子系统,创建一个SCSI主机,并将SAS驱动程序注册到它创建的sysfs SAS树。然后它会返回。然后,启用phy以实际启动OOB(此时驱动程序将开始调用notify_*事件回调)。
结构描述¶
struct sas_phy
¶
通常,这是静态嵌入到驱动程序的phy结构中
struct my_phy {
blah;
struct sas_phy sas_phy;
bleh;
};
然后,所有phy都是HA结构中my_phy的数组(如下所示)。
然后,在初始化phy时,您也初始化sas_phy结构以及自己的phy结构。
通常,phy由LLDD管理,端口由SAS层管理。因此,phy由LLDD初始化和更新,而端口由SAS层初始化和更新。
存在一种方案,LLDD可以RW某些字段,而SAS层只能读取此类字段,反之亦然。其目的是避免不必要的锁定。
- enabled
必须设置 (0/1)
- id
必须设置 [0,MAX_PHYS)]
- class, proto, type, role, oob_mode, linkrate
必须设置
- oob_mode
在OOB完成后设置此值,然后通知SAS层。
- sas_addr
这通常指向一个数组,该数组保存phy的sas地址,可能位于my_phy结构中的某个位置。
- attached_sas_addr
当您(LLDD)接收到IDENTIFY帧或FIS帧时,在通知SAS层_之前_设置此值。想法是,有时LLDD可能希望在该phy/端口上伪造或提供不同的SAS地址,这允许它执行此操作。最好从IDENTIFY帧复制sas地址,或者可能为直接连接的SATA设备生成SAS地址。发现过程可能会稍后更改此值。
- frame_rcvd
这是您在获取IDENTIFY/FIS帧时复制帧的位置;您锁定,复制,设置frame_rcvd_size并解锁锁定,然后调用事件。它是一个指针,因为无法_精确_知道硬件帧大小,因此您在phy结构中定义实际数组,并让此指针指向它。您将帧从可DMA的内存复制到持有锁定的区域。
- sas_prim
这是接收到原语时它们所在的位置。请参阅sas.h。获取锁,设置原语,释放锁,通知。
- port
如果phy属于端口,则此项指向sas_port -- LLDD仅读取此项。它指向此phy所属的sas_port。由SAS层设置。
- ha
可以设置;SAS层无论如何都会设置它。
- lldd_phy
您应将其设置为指向您的phy,以便当SAS层调用您的回调之一并将phy传递给您时,您可以更快地找到您要查找的内容。如果嵌入了sas_phy,您也可以使用container_of -- 无论您喜欢什么。
struct sas_port
¶
LLDD不设置此结构的任何字段 -- 它只读取它们。它们应该是不言自明的。
phy_mask是32位的,这应该足够了,因为我还没有听说过HA拥有超过8个phy。
- lldd_port
我没有发现它的用途 -- 也许其他希望具有内部端口表示的LLDD可以利用这一点。
struct sas_ha_struct
¶
它通常在描述适配器的LLDD结构中静态声明
struct my_sas_ha {
blah;
struct sas_ha_struct sas_ha;
struct my_phy phys[MAX_PHYS];
struct sas_port sas_ports[MAX_PHYS]; /* (1) */
bleh;
};
(1) If your LLDD doesn't have its own port representation.
需要初始化哪些内容(下面给出了示例函数)。
pcidev¶
- sas_addr
由于SAS层不想处理内存分配等问题,因此它指向某个位置(例如,在主机适配器结构中)静态分配的数组,并保存您或制造商等提供的主机适配器的SAS地址。
sas_port¶
- sas_phy
指向结构的指针数组。(请参见以上关于sas_addr的说明)。必须设置这些。请参阅以下更多说明。
- num_phys
sas_phy数组中存在的phy数量,以及sas_port数组中存在的端口数量。最多可以有num_phys个端口(每个端口一个),因此我们删除num_ports,仅使用num_phys。
事件接口
/* LLDD calls these to notify the class of an event. */
void sas_notify_port_event(struct sas_phy *, enum port_event, gfp_t);
void sas_notify_phy_event(struct sas_phy *, enum phy_event, gfp_t);
端口通知
/* The class calls these to notify the LLDD of an event. */
void (*lldd_port_formed)(struct sas_phy *);
void (*lldd_port_deformed)(struct sas_phy *);
如果LLDD希望在形成或变形端口时收到通知,则将其设置为满足类型的函数。
SAS LLDD还应至少实现SAM中描述的一个任务管理功能(TMF)
/* Task Management Functions. Must be called from process context. */
int (*lldd_abort_task)(struct sas_task *);
int (*lldd_abort_task_set)(struct domain_device *, u8 *lun);
int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
int (*lldd_I_T_nexus_reset)(struct domain_device *);
int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
int (*lldd_query_task)(struct sas_task *);
有关更多信息,请从T10.org读取SAM。
端口和适配器管理
/* Port and Adapter management */
int (*lldd_clear_nexus_port)(struct sas_port *);
int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
SAS LLDD应至少实现其中一个。
phy管理
/* Phy management */
int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
- lldd_ha
将其设置为指向您的HA结构。如果像上面那样嵌入它,您也可以使用container_of。
示例初始化和注册函数可能如下所示(从probe()调用的最后一件事)但是在您启用phy进行OOB之前
static int register_sas_ha(struct my_sas_ha *my_ha)
{
int i;
static struct sas_phy *sas_phys[MAX_PHYS];
static struct sas_port *sas_ports[MAX_PHYS];
my_ha->sas_ha.sas_addr = &my_ha->sas_addr[0];
for (i = 0; i < MAX_PHYS; i++) {
sas_phys[i] = &my_ha->phys[i].sas_phy;
sas_ports[i] = &my_ha->sas_ports[i];
}
my_ha->sas_ha.sas_phy = sas_phys;
my_ha->sas_ha.sas_port = sas_ports;
my_ha->sas_ha.num_phys = MAX_PHYS;
my_ha->sas_ha.lldd_port_formed = my_port_formed;
my_ha->sas_ha.lldd_dev_found = my_dev_found;
my_ha->sas_ha.lldd_dev_gone = my_dev_gone;
my_ha->sas_ha.lldd_execute_task = my_execute_task;
my_ha->sas_ha.lldd_abort_task = my_abort_task;
my_ha->sas_ha.lldd_abort_task_set = my_abort_task_set;
my_ha->sas_ha.lldd_clear_task_set = my_clear_task_set;
my_ha->sas_ha.lldd_I_T_nexus_reset= NULL; (2)
my_ha->sas_ha.lldd_lu_reset = my_lu_reset;
my_ha->sas_ha.lldd_query_task = my_query_task;
my_ha->sas_ha.lldd_clear_nexus_port = my_clear_nexus_port;
my_ha->sas_ha.lldd_clear_nexus_ha = my_clear_nexus_ha;
my_ha->sas_ha.lldd_control_phy = my_control_phy;
return sas_register_ha(&my_ha->sas_ha);
}
SAS 1.1未定义I_T Nexus Reset TMF。
事件¶
事件是SAS LLDD通知SAS层任何内容的_唯一方法_。没有其他方法或方式可以使LLDD告知SAS层内部或SAS域中发生的任何事情。
phy事件
PHYE_LOSS_OF_SIGNAL, (C)
PHYE_OOB_DONE,
PHYE_OOB_ERROR, (C)
PHYE_SPINUP_HOLD.
端口事件,在_phy_上传递
PORTE_BYTES_DMAED, (M)
PORTE_BROADCAST_RCVD, (E)
PORTE_LINK_RESET_ERR, (C)
PORTE_TIMER_EVENT, (C)
PORTE_HARD_RESET.
- 主机适配器事件
HAE_RESET
SAS LLDD应能够生成
至少一个来自C组(选择)的事件,
标记为M(强制)的事件是强制性的(只有一个),
标记为E(扩展器)的事件,如果它希望SAS层处理域重新验证(只有一个这样的事件)。
未标记的事件是可选的。
含义
- HAE_RESET
当您的HA出现内部错误并被重置时。
- PORTE_BYTES_DMAED
在接收到IDENTIFY/FIS帧时
- PORTE_BROADCAST_RCVD
在接收到原语时
- PORTE_LINK_RESET_ERR
计时器过期,信号丢失,DWS丢失等。[1]
- PORTE_TIMER_EVENT
DWS复位超时计时器过期[1]
- PORTE_HARD_RESET
接收到硬重置原语。
- PHYE_LOSS_OF_SIGNAL
设备已消失[1]
- PHYE_OOB_DONE
OOB正常,并且oob_mode有效
- PHYE_OOB_ERROR
在执行OOB时出错,设备可能已断开连接。[1]
- PHYE_SPINUP_HOLD
SATA存在,未发送COMWAKE。
执行命令SCSI RPC
int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags);
用于将任务排队到SAS LLDD。@task是要执行的任务。@gfp_mask是定义调用者上下文的gfp_mask。
此函数应实现执行命令SCSI RPC,
也就是说,当调用lldd_execute_task()时,命令会_立即_在传输上发出。在SAS LLDD的任何级别都_没有_任何形式的排队。
返回值
-SAS_QUEUE_FULL,-ENOMEM,没有排队任何内容;
0,任务已排队。
struct sas_task {
dev -- the device this task is destined to
task_proto -- _one_ of enum sas_proto
scatter -- pointer to scatter gather list array
num_scatter -- number of elements in scatter
total_xfer_len -- total number of bytes expected to be transferred
data_dir -- PCI_DMA_...
task_done -- callback when the task has finished execution
};
发现¶
sysfs树具有以下目的
它向您显示当前SAS域的物理布局,即,域现在在物理世界中的样子。
显示_在发现时_的一些设备参数。
这是指向tree(1)程序的链接,该程序对于查看SAS域非常有用:ftp://mama.indstate.edu/linux/tree/
我希望用户空间应用程序实际创建此图形界面。
也就是说,如果您例如更改READY LED MEANING设置的含义,则sysfs域树不会显示或保留状态,但它会向您显示域设备的当前连接状态。
保留内部设备状态更改是上层(命令集驱动程序)和用户空间的责任。
当设备或多个设备从域中断开连接时,这会立即反映在sysfs树中,并且设备会从系统中删除。
结构domain_device描述SAS域中的任何设备。它完全由SAS层管理。任务指向域设备,这就是SAS LLDD知道将任务发送到何处的方式。SAS LLDD仅读取domain_device结构的内容,但绝不创建或销毁它。
从用户空间进行扩展器管理¶
在sysfs中的每个扩展器目录中,都有一个名为“smp_portal”的文件。它是一个二进制sysfs属性文件,它实现了一个SMP门户(注意:这不是SMP端口),用户空间应用程序可以通过该门户发送SMP请求并接收SMP响应。
功能非常简单
构建您要发送的SMP帧。SAS规范中描述了格式和布局。将CRC字段保留为等于0。
open(2)
以RW模式打开扩展器的SMP门户sysfs文件。
write(2)
写入您在1中构建的帧。
read(2)
读取你为构建的帧所期望接收的数据量。如果你接收到的数据量与预期不同,那么就存在某种错误。
close(2)
所有这些过程都在函数 do_smp_func() 及其调用者中详细展示,这些代码位于文件 “expander_conf.c” 中。
内核功能在文件 “sas_expander.c” 中实现。
程序 “expander_conf.c” 实现了这些。它接收一个参数,即指向扩展器的 SMP 端口的 sysfs 文件名,并给出扩展器信息,包括路由表。
SMP 端口让你完全控制扩展器,因此请务必小心。