1:由于工作的原因,在过去的几个月中,一直从事LSI公司的2*36port的expander方面的开发。其主要是对SAS中SSP,SMP,SES命令协议的解析及数据处理。
其LSI公司的SDK使用的是threadx操作系统,其芯片内核为ARM920t,在920t之提供了对SAS协议处理的功能。个人感觉代码风格不好。
现将个人对expander的理解整理如下:
The LSISAS2X36 is a 36-port, 6.0-Gbit/s Serial Attached SCSI (SAS) expander that enables the connection of up to 36 directly attached SAS or Serial ATA (SATA) devices, and provides table routing to support connections for up to 1024 SAS addresses. Each expander phy is individually configurable and performs SAS and SATA transfers based
on the speed of the host or target at either 6.0-Gbits/s, 3.0-Gbits/s, or 1.5-Gbits/s.expander在系统中角色处于"中间层"即向上连接initiator,向下连接target。上面描述可知:36port的expander可以直接连接36个tSAS或者SATA类型的target。expander 在进行性数据或者命令转发时,使用路由机制。基于每一个target的SAS地址进行数据包的路由。
所支持的协议:
A Serial SCSI Protocol (SSP)
B Serial ATA Tunneled Protocol (STP)
C Serial Management Protocol (SMP)
D SAS protocol, described in the Serial Attached SCSI (SAS) Standard, Revision 15a
E SATA, as defined in the Serial ATA: High Speed Serialized AT Attachment Specification,version 2.5
F SATA II
H SFF-8485 protocol, using the Serial GPIO (SGPIO) interface provided by theexpander
上面涉及到的协议SAS,SSP,STP,SMP可以参考一下章节:
SCSI Architecture Model - 5 (SAM-5)
SAS Protocol Layer (SPL)
SCSI Enclosure Services - 3 (SES-3)
SCSI Primary Commands - 4 (SPC-4)
SCSI Block Commands – 3 (SBC-3)
SFF-8485 protocol 协议主要是控制GPIO的工作方式。
下面使用几张图来说明expander在整个存储系统的作用(不是我画的)
上图中可以看到,expander在系统中处于中间层,向上连接initiator,向下连接target。由于向下直接连接target,故图中的Routing types为D表示Direct。
上图主要使用多个expander来增加target的数量。
上图是路径冗余,主要是确保系统的可靠性,防止单点故障。
2:Linux下面的expander相关代码
而下面的代码是针对Marvell 公司提供的代码,其中SAS88SE9485/9445 6Gb/s SAS/SATA IO Controllers 为SAScontroller,其中expander的代码来自E:\src\linux-2.6.33.20\drivers\scsi\libsas这个目录中的SAS_expander.c。
现在看看linux下面的跟expander相关的代码,在linux之下跟SCSI,SAS相关的代码的目录如下:
E:\src\linux-2.6.33.20\drivers\scsi
E:\src\linux-2.6.33.20\include\scsi
其中
SAS_expander.c 的作用Serial Attached SCSI (SAS) Expander discovery and configuration
sas_init.c的作用 Serial Attached SCSI (SAS) Transport Layer initialization
文件mv_init.c
static int __init mvs_init(void)
{
int rc;
mvs_stt = sas_domain_attach_transport(&mvs_transport_ops);//函数的定义在:sas_init.c
if (!mvs_stt)
return -ENOMEM;
rc = pci_register_driver(&mvs_pci_driver);
if (rc)
goto err_out;
return 0;
err_out:
sas_release_transport(mvs_stt);
return rc;
}
//函数的定义在:sas_init.c
struct scsi_transport_template *
sas_domain_attach_transport(struct sas_domain_function_template *dft)
{
struct scsi_transport_template *stt = sas_attach_transport(&sft);//sft的定义在下面
struct sas_internal *i;
if (!stt)
return stt;
i = to_sas_internal(stt);
i->dft = dft;
stt->create_work_queue = 1;
stt->eh_timed_out = sas_scsi_timed_out;
stt->eh_strategy_handler = sas_scsi_recover_host;
return stt;
}
//函数指针的定义,主要用来获取expander或phy,enclosure的信息。
/* The functions by which the transport class and the driver communicate */
struct sas_function_template {
int (*get_linkerrors)(struct sas_phy *);
int (*get_enclosure_identifier)(struct sas_rphy *, u64 *);
int (*get_bay_identifier)(struct sas_rphy *);
int (*phy_reset)(struct sas_phy *, int);
int (*phy_enable)(struct sas_phy *, int);
int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *);
int (*smp_handler)(struct Scsi_Host *, struct sas_rphy *, struct request *);
};
static struct sas_function_template sft = { //是对上面的结构体的初始化
.phy_enable = sas_phy_enable,
.phy_reset = sas_phy_reset,
.set_phy_speed = sas_set_phy_speed,
.get_linkerrors = sas_get_linkerrors,
.smp_handler = sas_smp_handler,
};
现在我们来看个简单的 .set_phy_speed = sas_set_phy_speed,
sas_set_phy_speed定义如下:
enum sas_linkrate {
/* These Values are defined in the SAS standard */
SAS_LINK_RATE_UNKNOWN = 0,
SAS_PHY_DISABLED = 1,
SAS_PHY_RESET_PROBLEM = 2,
SAS_SATA_SPINUP_HOLD = 3,
SAS_SATA_PORT_SELECTOR = 4,
SAS_PHY_RESET_IN_PROGRESS = 5,
SAS_LINK_RATE_1_5_GBPS = 8,
SAS_LINK_RATE_G1 = SAS_LINK_RATE_1_5_GBPS,
SAS_LINK_RATE_3_0_GBPS = 9,
SAS_LINK_RATE_G2 = SAS_LINK_RATE_3_0_GBPS,
SAS_LINK_RATE_6_0_GBPS = 10,
/* These are virtual to the transport class and may never
* be signalled normally since the standard defined field
* is only 4 bits */
SAS_LINK_RATE_FAILED = 0x10,
SAS_PHY_VIRTUAL = 0x11,
};定义的为linkrate的 enumm。定义了SAS的链路速率。
int sas_set_phy_speed(struct sas_phy *phy,struct sas_phy_linkrates *rates)
{
int ret;
//下面是判断SAS的速率是否正确
if ((rates->minimum_linkrate && rates->minimum_linkrate > phy->maximum_linkrate) ||
(rates->maximum_linkrate && rates->maximum_linkrate < phy->minimum_linkrate))
return -EINVAL;
if (rates->minimum_linkrate && rates->minimum_linkrate < phy->minimum_linkrate_hw)
rates->minimum_linkrate = phy->minimum_linkrate_hw;
if (rates->maximum_linkrate && rates->maximum_linkrate > phy->maximum_linkrate_hw)
rates->maximum_linkrate = phy->maximum_linkrate_hw;
if (scsi_is_sas_phy_local(phy)) { //根据scsi_is_sas_phy_local(phy)的值来判断使用哪个control函数来执行
struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
struct sas_internal *i =
to_sas_internal(sas_ha->core.shost->transportt);
ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE,rates);
} else {
struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
ret = sas_smp_phy_control(ddev, phy->number,
PHY_FUNC_LINK_RESET, rates);
}
return ret;
}
i->dft->lldd_control_phy函数指针的赋值是在下面的结构体中进行的
static struct sas_domain_function_template mvs_transport_ops = {
.lldd_dev_found = mvs_dev_found,
.lldd_dev_gone = mvs_dev_gone,
.lldd_execute_task = mvs_queue_command,
.lldd_control_phy = mvs_phy_control,
.lldd_abort_task = mvs_abort_task,
.lldd_abort_task_set = mvs_abort_task_set,
.lldd_clear_aca = mvs_clear_aca,
.lldd_clear_task_set = mvs_clear_task_set,
.lldd_I_T_nexus_reset = mvs_I_T_nexus_reset,
.lldd_lu_reset = mvs_lu_reset,
.lldd_query_task = mvs_query_task,
.lldd_port_formed = mvs_port_formed,
.lldd_port_deformed = mvs_port_deformed,
};
mvs_phy_control,定义如下:
int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,void *funcdata)
{
int rc = 0, phy_id = sas_phy->id;
u32 tmp, i = 0, hi;
struct sas_ha_struct *sha = sas_phy->ha;
struct mvs_info *mvi = NULL;
while (sha->sas_phy[i]) {
if (sha->sas_phy[i] == sas_phy)
break;
i++;
}
hi = i/((struct mvs_prv_info *)sha->lldd_ha)->n_phy;
mvi = ((struct mvs_prv_info *)sha->lldd_ha)->mvi[hi];
switch (func) {
case PHY_FUNC_SET_LINK_RATE:
MVS_CHIP_DISP->phy_set_link_rate(mvi, phy_id, funcdata);
break;
case PHY_FUNC_HARD_RESET:
tmp = MVS_CHIP_DISP->read_phy_ctl(mvi, phy_id);
if (tmp & PHY_RST_HARD)
break;
MVS_CHIP_DISP->phy_reset(mvi, phy_id, 1);
break;
case PHY_FUNC_LINK_RESET:
MVS_CHIP_DISP->phy_enable(mvi, phy_id);
MVS_CHIP_DISP->phy_reset(mvi, phy_id, 0);
break;
case PHY_FUNC_DISABLE:
MVS_CHIP_DISP->phy_disable(mvi, phy_id);
break;
case PHY_FUNC_RELEASE_SPINUP_HOLD:
default:
rc = -EOPNOTSUPP;
}
msleep(200);
return rc;
}上面的case语句,定义在SPL的9.4.3.28 PHY CONTROL function
Marvell 88SE9485/9445 6Gb/s SAS/SATA IO Controllers
const struct mvs_dispatch mvs_94xx_dispatch = { //定义了上面case中的函数。
"mv94xx",
mvs_94xx_init,
NULL,
mvs_94xx_ioremap,
mvs_94xx_iounmap,
mvs_94xx_isr,
mvs_94xx_isr_status,
mvs_94xx_interrupt_enable,
mvs_94xx_interrupt_disable,
mvs_read_phy_ctl,
mvs_write_phy_ctl,
mvs_read_port_cfg_data,
mvs_write_port_cfg_data,
mvs_write_port_cfg_addr,
mvs_read_port_vsr_data,
mvs_write_port_vsr_data,
mvs_write_port_vsr_addr,
mvs_read_port_irq_stat,
mvs_write_port_irq_stat,
mvs_read_port_irq_mask,
mvs_write_port_irq_mask,
mvs_get_sas_addr,
mvs_94xx_command_active,
mvs_94xx_issue_stop,
mvs_start_delivery,
mvs_rx_update,
mvs_int_full,
mvs_94xx_assign_reg_set,
mvs_94xx_free_reg_set,
mvs_get_prd_size,
mvs_get_prd_count,
mvs_94xx_make_prd,
mvs_94xx_detect_porttype,
mvs_94xx_oob_done,
mvs_94xx_fix_phy_info,
NULL,
mvs_94xx_phy_set_link_rate,
mvs_hw_max_link_rate,
mvs_94xx_phy_disable,
mvs_94xx_phy_enable,
mvs_94xx_phy_reset,
NULL,
mvs_94xx_clear_active_cmds,
mvs_94xx_spi_read_data,
mvs_94xx_spi_write_data,
mvs_94xx_spi_buildcmd,
mvs_94xx_spi_issuecmd,
mvs_94xx_spi_waitdataready,
#ifndef DISABLE_HOTPLUG_DMA_FIX
mvs_94xx_fix_dma,
#endif
};
来个图更形象:
2:sas_init.c文件
在这个文件的最后可以看到有如下来个函数:
static int __init sas_class_init(void)
{
sas_task_cache = kmem_cache_create("sas_task", sizeof(struct sas_task), 0, SLAB_HWCACHE_ALIGN, NULL); //如下有说明
if (!sas_task_cache)
return -ENOMEM;
return 0;
}
static void __exit sas_class_exit(void)
{
kmem_cache_destroy(sas_task_cache);
}
在初始化函数和退出函数中,完成slab高速缓存的申请和释放。
在内核编程中,可能经常会有一些数据结构需要反复使用和释放,按照通常的思路,可能是使用kmalloc和kfree来实现。
但是这种方式效率不高,Linux为我们提供了更加高效的方法——Slab高速缓存管理器
通过先使用kmem_cache_create函数创建一个高速缓存的头指针——在内核中是struct kmem_cache结构,具体用法可以这样:struct kmem_cache * cachep = NULL ;
cachep = kmem_cache_create( "cache_name" , sizeof ( struct yourstruct) , 0, SLAB_HWCACHE_ALIGN, NULL , NULL ) ;
这样我们就获得了一个可用的cachep头指针。