1*56a248e7SYujie Zhang.. SPDX-License-Identifier: GPL-2.0 2*56a248e7SYujie Zhang.. include:: ../disclaimer-zh_CN.rst 3*56a248e7SYujie Zhang 4*56a248e7SYujie Zhang:Original: Documentation/scsi/libsas.rst 5*56a248e7SYujie Zhang 6*56a248e7SYujie Zhang:翻译: 7*56a248e7SYujie Zhang 8*56a248e7SYujie Zhang 张钰杰 Yujie Zhang <yjzhang@leap-io-kernel.com> 9*56a248e7SYujie Zhang 10*56a248e7SYujie Zhang:校译: 11*56a248e7SYujie Zhang 12*56a248e7SYujie Zhang====== 13*56a248e7SYujie ZhangSAS 层 14*56a248e7SYujie Zhang====== 15*56a248e7SYujie Zhang 16*56a248e7SYujie ZhangSAS 层是一个管理基础架构,用于管理 SAS LLDD。它位于 SCSI Core 17*56a248e7SYujie Zhang与 SAS LLDD 之间。 体系结构如下: SCSI Core 关注的是 SAM/SPC 相 18*56a248e7SYujie Zhang关的问题;SAS LLDD 及其序列控制器负责 PHY 层、OOB 信号以及链路 19*56a248e7SYujie Zhang管理;而 SAS 层则负责以下任务:: 20*56a248e7SYujie Zhang 21*56a248e7SYujie Zhang * SAS Phy、Port 和主机适配器(HA)事件管理(事件由 LLDD 22*56a248e7SYujie Zhang 生成,由 SAS 层处理); 23*56a248e7SYujie Zhang * SAS 端口的管理(创建与销毁); 24*56a248e7SYujie Zhang * SAS 域的发现与重新验证; 25*56a248e7SYujie Zhang * SAS 域内设备的管理; 26*56a248e7SYujie Zhang * SCSI 主机的注册与注销; 27*56a248e7SYujie Zhang * 将设备注册到 SCSI Core(SAS 设备)或 libata(SATA 设备); 28*56a248e7SYujie Zhang * 扩展器的管理,并向用户空间导出扩展器控制接口。 29*56a248e7SYujie Zhang 30*56a248e7SYujie ZhangSAS LLDD 是一种 PCI 设备驱动程序。它负责 PHY 层和 OOB(带外) 31*56a248e7SYujie Zhang信号的管理、厂商特定的任务,并向 SAS 层上报事件。 32*56a248e7SYujie Zhang 33*56a248e7SYujie ZhangSAS 层实现了 SAS 1.1 规范中定义的大部分 SAS 功能。 34*56a248e7SYujie Zhang 35*56a248e7SYujie Zhangsas_ha_struct 结构体用于向 SAS 层描述一个 SAS LLDD。该结构的 36*56a248e7SYujie Zhang大部分字段由 SAS 层使用,但其中少数字段需要由 LLDD 进行初始化。 37*56a248e7SYujie Zhang 38*56a248e7SYujie Zhang在完成硬件初始化之后,应当在驱动的 probe() 函数中调用 39*56a248e7SYujie Zhangsas_register_ha()。该函数会将 LLDD 注册到 SCSI 子系统中,创 40*56a248e7SYujie Zhang建一个对应的 SCSI 主机,并将你的 SAS 驱动程序注册到其在 sysfs 41*56a248e7SYujie Zhang下创建的 SAS 设备树中。随后该函数将返回。接着,你需要使能 PHY, 42*56a248e7SYujie Zhang以启动实际的 OOB(带外)过程;此时驱动将开始调用 notify_* 系 43*56a248e7SYujie Zhang列事件回调函数。 44*56a248e7SYujie Zhang 45*56a248e7SYujie Zhang结构体说明 46*56a248e7SYujie Zhang========== 47*56a248e7SYujie Zhang 48*56a248e7SYujie Zhang``struct sas_phy`` 49*56a248e7SYujie Zhang------------------ 50*56a248e7SYujie Zhang 51*56a248e7SYujie Zhang通常情况下,该结构体会被静态地嵌入到驱动自身定义的 PHY 结构体中, 52*56a248e7SYujie Zhang例如:: 53*56a248e7SYujie Zhang 54*56a248e7SYujie Zhang struct my_phy { 55*56a248e7SYujie Zhang blah; 56*56a248e7SYujie Zhang struct sas_phy sas_phy; 57*56a248e7SYujie Zhang bleh; 58*56a248e7SYujie Zhang } 59*56a248e7SYujie Zhang 60*56a248e7SYujie Zhang随后,在主机适配器(HA)的结构体中,所有的 PHY 通常以 my_phy 61*56a248e7SYujie Zhang数组的形式存在(如下文所示)。 62*56a248e7SYujie Zhang 63*56a248e7SYujie Zhang在初始化各个 PHY 时,除了初始化驱动自定义的 PHY 结构体外,还 64*56a248e7SYujie Zhang需要同时初始化其中的 sas_phy 结构体。 65*56a248e7SYujie Zhang 66*56a248e7SYujie Zhang一般来说,PHY 的管理由 LLDD 负责,而端口(port)的管理由 SAS 67*56a248e7SYujie Zhang层负责。因此,PHY 的初始化与更新由 LLDD 完成,而端口的初始化与 68*56a248e7SYujie Zhang更新则由 SAS 层完成。系统设计中规定,某些字段可由 LLDD 进行读 69*56a248e7SYujie Zhang写,而 SAS 层只能读取这些字段;反之亦然。其设计目的是为了避免不 70*56a248e7SYujie Zhang必要的锁操作。 71*56a248e7SYujie Zhang 72*56a248e7SYujie Zhang在该设计中,某些字段可由 LLDD 进行读写(RW),而 SAS 层仅可读 73*56a248e7SYujie Zhang取这些字段;反之亦然。这样设计的目的在于避免不必要的锁操作。 74*56a248e7SYujie Zhang 75*56a248e7SYujie Zhangenabled 76*56a248e7SYujie Zhang - 必须设置(0/1) 77*56a248e7SYujie Zhang 78*56a248e7SYujie Zhangid 79*56a248e7SYujie Zhang - 必须设置[0,MAX_PHYS)] 80*56a248e7SYujie Zhang 81*56a248e7SYujie Zhangclass, proto, type, role, oob_mode, linkrate 82*56a248e7SYujie Zhang - 必须设置。 83*56a248e7SYujie Zhang 84*56a248e7SYujie Zhangoob_mode 85*56a248e7SYujie Zhang - 当 OOB(带外信号)完成后,设置此字段,然后通知 SAS 层。 86*56a248e7SYujie Zhang 87*56a248e7SYujie Zhangsas_addr 88*56a248e7SYujie Zhang - 通常指向一个保存该 PHY 的 SAS 地址的数组,该数组可能位于 89*56a248e7SYujie Zhang 驱动自定义的 my_phy 结构体中。 90*56a248e7SYujie Zhang 91*56a248e7SYujie Zhangattached_sas_addr 92*56a248e7SYujie Zhang - 当 LLDD 接收到 IDENTIFY 帧或 FIS 帧时,应在通知 SAS 层 93*56a248e7SYujie Zhang 之前设置该字段。其设计意图在于:有时 LLDD 可能需要伪造或 94*56a248e7SYujie Zhang 提供一个与实际不同的 SAS 地址用于该 PHY/端口,而该机制允许 95*56a248e7SYujie Zhang LLDD 这样做。理想情况下,应将 SAS 地址从 IDENTIFY 帧中 96*56a248e7SYujie Zhang 复制过来;对于直接连接的 SATA 设备,也可以由 LLDD 生成一 97*56a248e7SYujie Zhang 个 SAS 地址。后续的发现过程可能会修改此字段。 98*56a248e7SYujie Zhang 99*56a248e7SYujie Zhangframe_rcvd 100*56a248e7SYujie Zhang - 当接收到 IDENTIFY 或 FIS 帧时,将该帧复制到此处。正确的 101*56a248e7SYujie Zhang 操作流程是获取锁 → 复制数据 → 设置 frame_rcvd_size → 释 102*56a248e7SYujie Zhang 放锁 → 调用事件通知。该字段是一个指针,因为驱动无法精确确 103*56a248e7SYujie Zhang 定硬件帧的大小;因此,实际的帧数据数组应定义在驱动自定义的 104*56a248e7SYujie Zhang PHY 结构体中,然后让此指针指向该数组。在持锁状态下,将帧从 105*56a248e7SYujie Zhang DMA 可访问内存区域复制到该数组中。 106*56a248e7SYujie Zhang 107*56a248e7SYujie Zhangsas_prim 108*56a248e7SYujie Zhang - 用于存放接收到的原语(primitive)。参见 sas.h。操作流程同 109*56a248e7SYujie Zhang 样是:获取锁 → 设置 primitive → 释放锁 → 通知事件。 110*56a248e7SYujie Zhang 111*56a248e7SYujie Zhangport 112*56a248e7SYujie Zhang - 如果该 PHY 属于某个端口(port),此字段指向对应的 sas_port 113*56a248e7SYujie Zhang 结构体。LLDD 仅可读取此字段。它由 SAS 层设置,用于指向当前 114*56a248e7SYujie Zhang PHY 所属的 sas_port。 115*56a248e7SYujie Zhang 116*56a248e7SYujie Zhangha 117*56a248e7SYujie Zhang - 可以由 LLDD 设置;但无论是否设置,SAS 层都会再次对其进行赋值。 118*56a248e7SYujie Zhang 119*56a248e7SYujie Zhanglldd_phy 120*56a248e7SYujie Zhang - LLDD 应将此字段设置为指向自身定义的 PHY 结构体,这样当 SAS 121*56a248e7SYujie Zhang 层调用某个回调并传入 sas_phy 时,驱动可以快速定位自身的 PHY 122*56a248e7SYujie Zhang 结构体。如果 sas_phy 是嵌入式成员,也可以使用 container_of() 123*56a248e7SYujie Zhang 宏进行访问——两种方式均可。 124*56a248e7SYujie Zhang 125*56a248e7SYujie Zhang``struct sas_port`` 126*56a248e7SYujie Zhang------------------- 127*56a248e7SYujie Zhang 128*56a248e7SYujie ZhangLLDD 不应修改该结构体中的任何字段——它只能读取这些字段。这些字段的 129*56a248e7SYujie Zhang含义应当是不言自明的。 130*56a248e7SYujie Zhang 131*56a248e7SYujie Zhangphy_mask 为 32 位,目前这一长度已足够使用,因为尚未听说有主机适配 132*56a248e7SYujie Zhang器拥有超过8 个 PHY。 133*56a248e7SYujie Zhang 134*56a248e7SYujie Zhanglldd_port 135*56a248e7SYujie Zhang - 目前尚无明确用途。不过,对于那些希望在 LLDD 内部维护自身端 136*56a248e7SYujie Zhang 口表示的驱动,实现时可以利用该字段。 137*56a248e7SYujie Zhang 138*56a248e7SYujie Zhang``struct sas_ha_struct`` 139*56a248e7SYujie Zhang------------------------ 140*56a248e7SYujie Zhang 141*56a248e7SYujie Zhang它通常静态声明在你自己的 LLDD 结构中,用于描述您的适配器:: 142*56a248e7SYujie Zhang 143*56a248e7SYujie Zhang struct my_sas_ha { 144*56a248e7SYujie Zhang blah; 145*56a248e7SYujie Zhang struct sas_ha_struct sas_ha; 146*56a248e7SYujie Zhang struct my_phy phys[MAX_PHYS]; 147*56a248e7SYujie Zhang struct sas_port sas_ports[MAX_PHYS]; /* (1) */ 148*56a248e7SYujie Zhang bleh; 149*56a248e7SYujie Zhang }; 150*56a248e7SYujie Zhang 151*56a248e7SYujie Zhang (1) 如果你的 LLDD 没有自己的端口表示 152*56a248e7SYujie Zhang 153*56a248e7SYujie Zhang需要初始化(示例函数如下所示)。 154*56a248e7SYujie Zhang 155*56a248e7SYujie Zhangpcidev 156*56a248e7SYujie Zhang^^^^^^ 157*56a248e7SYujie Zhang 158*56a248e7SYujie Zhangsas_addr 159*56a248e7SYujie Zhang - 由于 SAS 层不想弄乱内存分配等, 因此这指向静态分配的数 160*56a248e7SYujie Zhang 组中的某个位置(例如,在您的主机适配器结构中),并保存您或 161*56a248e7SYujie Zhang 制造商等给出的主机适配器的 SAS 地址。 162*56a248e7SYujie Zhang 163*56a248e7SYujie Zhangsas_port 164*56a248e7SYujie Zhang^^^^^^^^ 165*56a248e7SYujie Zhang 166*56a248e7SYujie Zhangsas_phy 167*56a248e7SYujie Zhang - 指向结构体的指针数组(参见上文关于 sas_addr 的说明)。 168*56a248e7SYujie Zhang 这些指针必须设置。更多细节见下文说明。 169*56a248e7SYujie Zhang 170*56a248e7SYujie Zhangnum_phys 171*56a248e7SYujie Zhang - 表示 sas_phy 数组中 PHY 的数量,同时也表示 sas_port 172*56a248e7SYujie Zhang 数组中的端口数量。一个端口最多对应一个 PHY,因此最大端口数 173*56a248e7SYujie Zhang 等于 num_phys。因此,结构中不再单独使用 num_ports 字段, 174*56a248e7SYujie Zhang 而仅使用 num_phys。 175*56a248e7SYujie Zhang 176*56a248e7SYujie Zhang事件接口:: 177*56a248e7SYujie Zhang 178*56a248e7SYujie Zhang /* LLDD 调用以下函数来通知 SAS 类层发生事件 */ 179*56a248e7SYujie Zhang void sas_notify_port_event(struct sas_phy *, enum port_event, gfp_t); 180*56a248e7SYujie Zhang void sas_notify_phy_event(struct sas_phy *, enum phy_event, gfp_t); 181*56a248e7SYujie Zhang 182*56a248e7SYujie Zhang端口事件通知:: 183*56a248e7SYujie Zhang 184*56a248e7SYujie Zhang /* SAS 类层调用以下回调来通知 LLDD 端口事件 */ 185*56a248e7SYujie Zhang void (*lldd_port_formed)(struct sas_phy *); 186*56a248e7SYujie Zhang void (*lldd_port_deformed)(struct sas_phy *); 187*56a248e7SYujie Zhang 188*56a248e7SYujie Zhang如果 LLDD 希望在端口形成或解散时接收通知,则应将上述回调指针设 189*56a248e7SYujie Zhang置为符合函数类型定义的处理函数。 190*56a248e7SYujie Zhang 191*56a248e7SYujie ZhangSAS LLDD 还应至少实现 SCSI 协议中定义的一种任务管理函数(TMFs):: 192*56a248e7SYujie Zhang 193*56a248e7SYujie Zhang /* 任务管理函数. 必须在进程上下文中调用 */ 194*56a248e7SYujie Zhang int (*lldd_abort_task)(struct sas_task *); 195*56a248e7SYujie Zhang int (*lldd_abort_task_set)(struct domain_device *, u8 *lun); 196*56a248e7SYujie Zhang int (*lldd_clear_task_set)(struct domain_device *, u8 *lun); 197*56a248e7SYujie Zhang int (*lldd_I_T_nexus_reset)(struct domain_device *); 198*56a248e7SYujie Zhang int (*lldd_lu_reset)(struct domain_device *, u8 *lun); 199*56a248e7SYujie Zhang int (*lldd_query_task)(struct sas_task *); 200*56a248e7SYujie Zhang 201*56a248e7SYujie Zhang如需更多信息,请参考 T10.org。 202*56a248e7SYujie Zhang 203*56a248e7SYujie Zhang端口与适配器管理:: 204*56a248e7SYujie Zhang 205*56a248e7SYujie Zhang /* 端口与适配器管理 */ 206*56a248e7SYujie Zhang int (*lldd_clear_nexus_port)(struct sas_port *); 207*56a248e7SYujie Zhang int (*lldd_clear_nexus_ha)(struct sas_ha_struct *); 208*56a248e7SYujie Zhang 209*56a248e7SYujie ZhangSAS LLDD 至少应实现上述函数中的一个。 210*56a248e7SYujie Zhang 211*56a248e7SYujie ZhangPHY 管理:: 212*56a248e7SYujie Zhang 213*56a248e7SYujie Zhang /* PHY 管理 */ 214*56a248e7SYujie Zhang int (*lldd_control_phy)(struct sas_phy *, enum phy_func); 215*56a248e7SYujie Zhang 216*56a248e7SYujie Zhanglldd_ha 217*56a248e7SYujie Zhang - 应设置为指向驱动的主机适配器(HA)结构体的指针。如果 sas_ha_struct 218*56a248e7SYujie Zhang 被嵌入到更大的结构体中,也可以通过 container_of() 宏来获取。 219*56a248e7SYujie Zhang 220*56a248e7SYujie Zhang一个示例的初始化与注册函数可以如下所示:(该函数应在 probe() 221*56a248e7SYujie Zhang函数的最后调用)但必须在使能 PHY 执行 OOB 之前调用:: 222*56a248e7SYujie Zhang 223*56a248e7SYujie Zhang static int register_sas_ha(struct my_sas_ha *my_ha) 224*56a248e7SYujie Zhang { 225*56a248e7SYujie Zhang int i; 226*56a248e7SYujie Zhang static struct sas_phy *sas_phys[MAX_PHYS]; 227*56a248e7SYujie Zhang static struct sas_port *sas_ports[MAX_PHYS]; 228*56a248e7SYujie Zhang 229*56a248e7SYujie Zhang my_ha->sas_ha.sas_addr = &my_ha->sas_addr[0]; 230*56a248e7SYujie Zhang 231*56a248e7SYujie Zhang for (i = 0; i < MAX_PHYS; i++) { 232*56a248e7SYujie Zhang sas_phys[i] = &my_ha->phys[i].sas_phy; 233*56a248e7SYujie Zhang sas_ports[i] = &my_ha->sas_ports[i]; 234*56a248e7SYujie Zhang } 235*56a248e7SYujie Zhang 236*56a248e7SYujie Zhang my_ha->sas_ha.sas_phy = sas_phys; 237*56a248e7SYujie Zhang my_ha->sas_ha.sas_port = sas_ports; 238*56a248e7SYujie Zhang my_ha->sas_ha.num_phys = MAX_PHYS; 239*56a248e7SYujie Zhang 240*56a248e7SYujie Zhang my_ha->sas_ha.lldd_port_formed = my_port_formed; 241*56a248e7SYujie Zhang 242*56a248e7SYujie Zhang my_ha->sas_ha.lldd_dev_found = my_dev_found; 243*56a248e7SYujie Zhang my_ha->sas_ha.lldd_dev_gone = my_dev_gone; 244*56a248e7SYujie Zhang 245*56a248e7SYujie Zhang my_ha->sas_ha.lldd_execute_task = my_execute_task; 246*56a248e7SYujie Zhang 247*56a248e7SYujie Zhang my_ha->sas_ha.lldd_abort_task = my_abort_task; 248*56a248e7SYujie Zhang my_ha->sas_ha.lldd_abort_task_set = my_abort_task_set; 249*56a248e7SYujie Zhang my_ha->sas_ha.lldd_clear_task_set = my_clear_task_set; 250*56a248e7SYujie Zhang my_ha->sas_ha.lldd_I_T_nexus_reset= NULL; (2) 251*56a248e7SYujie Zhang my_ha->sas_ha.lldd_lu_reset = my_lu_reset; 252*56a248e7SYujie Zhang my_ha->sas_ha.lldd_query_task = my_query_task; 253*56a248e7SYujie Zhang 254*56a248e7SYujie Zhang my_ha->sas_ha.lldd_clear_nexus_port = my_clear_nexus_port; 255*56a248e7SYujie Zhang my_ha->sas_ha.lldd_clear_nexus_ha = my_clear_nexus_ha; 256*56a248e7SYujie Zhang 257*56a248e7SYujie Zhang my_ha->sas_ha.lldd_control_phy = my_control_phy; 258*56a248e7SYujie Zhang 259*56a248e7SYujie Zhang return sas_register_ha(&my_ha->sas_ha); 260*56a248e7SYujie Zhang } 261*56a248e7SYujie Zhang 262*56a248e7SYujie Zhang(2) SAS 1.1 未定义 I_T Nexus Reset TMF(任务管理功能)。 263*56a248e7SYujie Zhang 264*56a248e7SYujie Zhang事件 265*56a248e7SYujie Zhang==== 266*56a248e7SYujie Zhang 267*56a248e7SYujie Zhang事件是 SAS LLDD 唯一的通知 SAS 层发生任何情况的方式。 268*56a248e7SYujie ZhangLLDD 没有其他方法可以告知 SAS 层其内部或 SAS 域中发生的事件。 269*56a248e7SYujie Zhang 270*56a248e7SYujie ZhangPhy 事件:: 271*56a248e7SYujie Zhang 272*56a248e7SYujie Zhang PHYE_LOSS_OF_SIGNAL, (C) 273*56a248e7SYujie Zhang PHYE_OOB_DONE, 274*56a248e7SYujie Zhang PHYE_OOB_ERROR, (C) 275*56a248e7SYujie Zhang PHYE_SPINUP_HOLD. 276*56a248e7SYujie Zhang 277*56a248e7SYujie Zhang端口事件,通过 _phy_ 传递:: 278*56a248e7SYujie Zhang 279*56a248e7SYujie Zhang PORTE_BYTES_DMAED, (M) 280*56a248e7SYujie Zhang PORTE_BROADCAST_RCVD, (E) 281*56a248e7SYujie Zhang PORTE_LINK_RESET_ERR, (C) 282*56a248e7SYujie Zhang PORTE_TIMER_EVENT, (C) 283*56a248e7SYujie Zhang PORTE_HARD_RESET. 284*56a248e7SYujie Zhang 285*56a248e7SYujie Zhang主机适配器事件: 286*56a248e7SYujie Zhang HAE_RESET 287*56a248e7SYujie Zhang 288*56a248e7SYujie ZhangSAS LLDD 应能够生成以下事件:: 289*56a248e7SYujie Zhang 290*56a248e7SYujie Zhang - 来自 C 组的至少一个事件(可选), 291*56a248e7SYujie Zhang - 标记为 M(必需)的事件为必需事件(至少一种); 292*56a248e7SYujie Zhang - 若希望 SAS 层处理域重新验证(domain revalidation),则 293*56a248e7SYujie Zhang 应生成标记为 E(扩展器)的事件(仅需一种); 294*56a248e7SYujie Zhang - 未标记的事件为可选事件。 295*56a248e7SYujie Zhang 296*56a248e7SYujie Zhang含义 297*56a248e7SYujie Zhang 298*56a248e7SYujie ZhangHAE_RESET 299*56a248e7SYujie Zhang - 当 HA 发生内部错误并被复位时。 300*56a248e7SYujie Zhang 301*56a248e7SYujie ZhangPORTE_BYTES_DMAED 302*56a248e7SYujie Zhang - 在接收到 IDENTIFY/FIS 帧时。 303*56a248e7SYujie Zhang 304*56a248e7SYujie ZhangPORTE_BROADCAST_RCVD 305*56a248e7SYujie Zhang - 在接收到一个原语时。 306*56a248e7SYujie Zhang 307*56a248e7SYujie ZhangPORTE_LINK_RESET_ERR 308*56a248e7SYujie Zhang - 定时器超时、信号丢失、丢失 DWS 等情况。 [1]_ 309*56a248e7SYujie Zhang 310*56a248e7SYujie ZhangPORTE_TIMER_EVENT 311*56a248e7SYujie Zhang - DWS 复位超时定时器到期时。[1]_ 312*56a248e7SYujie Zhang 313*56a248e7SYujie ZhangPORTE_HARD_RESET 314*56a248e7SYujie Zhang - 收到 Hard Reset 原语。 315*56a248e7SYujie Zhang 316*56a248e7SYujie ZhangPHYE_LOSS_OF_SIGNAL 317*56a248e7SYujie Zhang - 设备已断开连接。 [1]_ 318*56a248e7SYujie Zhang 319*56a248e7SYujie ZhangPHYE_OOB_DONE 320*56a248e7SYujie Zhang - OOB 过程成功完成,oob_mode 有效。 321*56a248e7SYujie Zhang 322*56a248e7SYujie ZhangPHYE_OOB_ERROR 323*56a248e7SYujie Zhang - 执行 OOB 过程中出现错误,设备可能已断开。 [1]_ 324*56a248e7SYujie Zhang 325*56a248e7SYujie ZhangPHYE_SPINUP_HOLD 326*56a248e7SYujie Zhang - 检测到 SATA 设备,但未发送 COMWAKE 信号。 327*56a248e7SYujie Zhang 328*56a248e7SYujie Zhang.. [1] 应设置或清除 phy 中相应的字段,或者从 tasklet 中调用 329*56a248e7SYujie Zhang 内联函数 sas_phy_disconnected(),该函数只是一个辅助函数。 330*56a248e7SYujie Zhang 331*56a248e7SYujie Zhang执行命令 SCSI RPC:: 332*56a248e7SYujie Zhang 333*56a248e7SYujie Zhang int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags); 334*56a248e7SYujie Zhang 335*56a248e7SYujie Zhang用于将任务排队提交给 SAS LLDD,@task 为要执行的任务,@gfp_mask 336*56a248e7SYujie Zhang为定义调用者上下文的 gfp 掩码。 337*56a248e7SYujie Zhang 338*56a248e7SYujie Zhang此函数应实现 执行 SCSI RPC 命令。 339*56a248e7SYujie Zhang 340*56a248e7SYujie Zhang也就是说,当调用 lldd_execute_task() 时,命令应当立即在传输 341*56a248e7SYujie Zhang层发出。SAS LLDD 中在任何层级上都不应再进行队列排放。 342*56a248e7SYujie Zhang 343*56a248e7SYujie Zhang返回值:: 344*56a248e7SYujie Zhang 345*56a248e7SYujie Zhang * 返回 -SAS_QUEUE_FULL 或 -ENOMEM 表示未排入队列; 346*56a248e7SYujie Zhang * 返回 0 表示任务已成功排入队列。 347*56a248e7SYujie Zhang 348*56a248e7SYujie Zhang:: 349*56a248e7SYujie Zhang 350*56a248e7SYujie Zhang struct sas_task { 351*56a248e7SYujie Zhang dev —— 此任务目标设备; 352*56a248e7SYujie Zhang task_proto —— 协议类型,为 enum sas_proto 中的一种; 353*56a248e7SYujie Zhang scatter —— 指向散布/聚集(SG)列表数组的指针; 354*56a248e7SYujie Zhang num_scatter —— SG 列表元素数量; 355*56a248e7SYujie Zhang total_xfer_len —— 预计传输的总字节数; 356*56a248e7SYujie Zhang data_dir —— 数据传输方向(PCI_DMA_*); 357*56a248e7SYujie Zhang task_done —— 任务执行完成时的回调函数。 358*56a248e7SYujie Zhang }; 359*56a248e7SYujie Zhang 360*56a248e7SYujie Zhang发现 361*56a248e7SYujie Zhang==== 362*56a248e7SYujie Zhang 363*56a248e7SYujie Zhangsysfs 树有以下用途:: 364*56a248e7SYujie Zhang 365*56a248e7SYujie Zhang a) 它显示当前时刻 SAS 域的物理布局,即展示当前物理世界中 366*56a248e7SYujie Zhang 域的实际结构。 367*56a248e7SYujie Zhang b) 显示某些设备的参数。 _at_discovery_time_. 368*56a248e7SYujie Zhang 369*56a248e7SYujie Zhang下面是一个指向 tree(1) 程序的链接,该工具在查看 SAS 域时非常 370*56a248e7SYujie Zhang有用: 371*56a248e7SYujie Zhangftp://mama.indstate.edu/linux/tree/ 372*56a248e7SYujie Zhang 373*56a248e7SYujie Zhang我期望用户空间的应用程序最终能够为此创建一个图形界面。 374*56a248e7SYujie Zhang 375*56a248e7SYujie Zhang也就是说,sysfs 域树不会显示或保存某些状态变化,例如,如果你更 376*56a248e7SYujie Zhang改了 READY LED 含义的设置,sysfs 树不会反映这种状态变化;但它 377*56a248e7SYujie Zhang确实会显示域设备的当前连接状态。 378*56a248e7SYujie Zhang 379*56a248e7SYujie Zhang维护内部设备状态变化的职责由上层(命令集驱动)和用户空间负责。 380*56a248e7SYujie Zhang 381*56a248e7SYujie Zhang当某个设备或多个设备从域中拔出时,这一变化会立即反映在 sysfs 382*56a248e7SYujie Zhang树中,并且这些设备会从系统中移除。 383*56a248e7SYujie Zhang 384*56a248e7SYujie Zhang结构体 domain_device 描述了 SAS 域中的任意设备。它完全由 SAS 385*56a248e7SYujie Zhang层管理。一个任务会指向某个域设备,SAS LLDD 就是通过这种方式知 386*56a248e7SYujie Zhang道任务应发送到何处。SAS LLDD 只读取 domain_device 结构的内容, 387*56a248e7SYujie Zhang但不会创建或销毁它。 388*56a248e7SYujie Zhang 389*56a248e7SYujie Zhang用户空间中的扩展器管理 390*56a248e7SYujie Zhang====================== 391*56a248e7SYujie Zhang 392*56a248e7SYujie Zhang在 sysfs 中的每个扩展器目录下,都有一个名为 "smp_portal" 的 393*56a248e7SYujie Zhang文件。这是一个二进制的 sysfs 属性文件,它实现了一个 SMP 入口 394*56a248e7SYujie Zhang(注意:这并不是一个 SMP 端口),用户空间程序可以通过它发送 395*56a248e7SYujie ZhangSMP 请求并接收 SMP 响应。 396*56a248e7SYujie Zhang 397*56a248e7SYujie Zhang该功能的实现方式看起来非常简单: 398*56a248e7SYujie Zhang 399*56a248e7SYujie Zhang1. 构建要发送的 SMP 帧。其格式和布局在 SAS 规范中有说明。保持 400*56a248e7SYujie Zhang CRC 字段为 0。 401*56a248e7SYujie Zhang 402*56a248e7SYujie Zhangopen(2) 403*56a248e7SYujie Zhang 404*56a248e7SYujie Zhang2. 以读写模式打开该扩展器的 SMP portal sysfs 文件。 405*56a248e7SYujie Zhang 406*56a248e7SYujie Zhangwrite(2) 407*56a248e7SYujie Zhang 408*56a248e7SYujie Zhang3. 将第 1 步中构建的帧写入文件。 409*56a248e7SYujie Zhang 410*56a248e7SYujie Zhangread(2) 411*56a248e7SYujie Zhang 412*56a248e7SYujie Zhang4. 读取与所构建帧预期返回长度相同的数据量。如果读取的数据量与 413*56a248e7SYujie Zhang 预期不符,则表示发生了某种错误。 414*56a248e7SYujie Zhang 415*56a248e7SYujie Zhangclose(2) 416*56a248e7SYujie Zhang 417*56a248e7SYujie Zhang整个过程在 "expander_conf.c" 文件中的函数 do_smp_func() 418*56a248e7SYujie Zhang及其调用者中有详细展示。 419*56a248e7SYujie Zhang 420*56a248e7SYujie Zhang对应的内核实现位于 "sas_expander.c" 文件中。 421*56a248e7SYujie Zhang 422*56a248e7SYujie Zhang程序 "expander_conf.c" 实现了上述逻辑。它接收一个参数——扩展器 423*56a248e7SYujie ZhangSMP portal 的 sysfs 文件名,并输出扩展器的信息,包括路由表内容。 424*56a248e7SYujie Zhang 425*56a248e7SYujie ZhangSMP portal 赋予了你对扩展器的完全控制权,因此请谨慎操作。 426