SAS Expander及Linux下面的代碼實現

來源:互聯網
上載者:User

 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頭指標。

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.