ToB企服应用市场:ToB评测及商务社交产业平台

标题: SCSI/UFS储存架构/协议/电源管理/下令处理流程 [打印本页]

作者: 大号在练葵花宝典    时间: 2024-7-23 21:16
标题: SCSI/UFS储存架构/协议/电源管理/下令处理流程
UFS子体系架构



1.UFS协议



无论是ufs host controller部分照旧ufs device部分,他们都将遵循统一的UFS规范

在 SCSI 架构中,主机上的 SCSI 接口卡称为 Initiator,与其相连接的 SCSI 磁盘等设备称为 Target,在逻辑上,Initiator 和 Target 之间通讯的工作模式,与两个网络设备之间的模式相似,他们之间采用 client-server 的“请求-回应”模式:

SCSI 的 Initiator 与 Target 共同构成了一个典型的 C/S 模型,每个指令都是“请求/应答”这样的模型来实现:
* Initiator重要任务:发出SCSI请求;
* Target重要任务:回答SCSI请求,通过 LU 提供业务,并通过任务管理器提供任务管理功能;
LU(Logical Unit):逻辑单位,是指一个可被操纵体系辨认和访问的逻辑存储单位。一个 UFS 设备可以包罗多个 LU,每个 LU 可以被视为一个独立的存储设备;
LUs : LU 的复数情势;
LUN(Logical Unit Number):逻辑单位号码,是用来标识差别 LU 的唯一编号。每个 LU 都有一个对应的 LUN,它可以用来在体系中唯一地标识和访问该 LU;
2.UFS 重要 Layer


别的,设备管理器可以略过传输层,直接管理与控制互联层:

主机设备管理器可以通过原语(Primitive)直接与UFS互联层(UIC,即MIPI的UniPro和M-PHY)通讯。除了上图中所示的reset原语,UFS还包罗让UIC进入和退出休眠的原语:DME_HIBERNATE_ENTER和DME_HIBERNATE_EXIT。
这是UFS主机和设备之间链路的省电模式,对UFS设备来说,链路只是整个UFS设备的一部分。一个UFS设备是否省电,除了看其链路,还需要考虑UFS控制器、存储介质等是否省电,即看整个UFS设备是否有好的电源管理。
Logical unit

UFS 设备有多少个 LU,LU 接收主机发过来的下令或请求大概来自应用层的 SCSI 模块、设备管理器大概任务管理器:

每个 LU 都是独立的,“独立”体现在下面几个方面:

总结来说,分别差别 LU 有以下作用:

3.UFS 架构

UFS 架构以 HCI(Host Controller Interface,主机控制器接口)为界分别为三部分。如下图,蓝色框上方调用 HCI 的为主机软件部分,蓝色框下方的为 HCI 封装的硬件处理细节部分,即 HC(Host Controller,主机控制器)

HCI 接口架构

数据结构服务于软件,主机的软件通过内存中的一系列主机 register 和数据结构 Transfer Request Descriptors 来与 Host controller 硬件进行交互。UFSHCI 定义了两种接口:IO Memory/Registar Space 以及 Host Memory Space,下图展示了 UFS HCI 的框图:


IO Memory/Registar Space:在这个空间,主机 register 被定义为主机的软件接口,通过 MMIO[4](Memory-Mapped I/O,内存映射 I/O)的方式被实现,重要包罗了下面三种类型的寄存器:

Host Memory Space:该空间中包罗了能够描述将实行下令和下令中数据缓存的数据结构。简单地可以明确为——UTP Transfer Request List 负责通用的 IO 下令,UTP Task Mangement Request List 负责管理下令。
   UTP Transfer Request List(传输请求列表)由一系列数据结构 UTRD (UTP Transfer Request Descriptor,传输请求描述符) 构成。UFS host controller 下令队列中的下令槽(slot)被 MMIO 到 UTRD,32 个 UTRD 对应最多 32 个 slot,UTRD 描述了要实行的下令及相干数据。
UFS 主机软件通过将要实行的下令及相干数据放置在 UTRD,再敲响主机控制器门铃(Offset 58h: UTRLDBR – UTP Transfer Request List Door Bell Register )向主机控制器发出下令。传输请求列表中的下令被 UFSHCI 次序实行,但大概会按照差别的次序完成。所有下令都可以产生下令完成停止大概更新在列表中的 UTRD 下令状态字段。UFS 主机软件可以在运行时向列表中添加下令。主机控制器支持停止聚合,即在预定义的下令完成设定的数量后天生单个下令完成停止。
  


UFS host驱动通太过配和利用UTRD描述符和UTMRD描述符来与host控制器硬件通讯。host控制器体系内存可以同时接受多达32个UTRD描述符和8个UTMRD描述符。每个UTRD描述符指向了一块内存地域,这块内存地域包罗:下令UPIU(UCD),response UPIU,并包罗了一块物理地域描述符表(PRDT),表中每一项指向一个数据buffer,存储需要传输的数据(command upiu)或要接收的数据(response upiu)。UCD的command和response将被分别组装成UPIU并通过UTP层进行传输。






UTP TMR descriptor (UTMRD)
对应的数据结构为struct utp_task_req_desc


4.UFS电源管理

UFS定义了4种根本功耗模式:Active,Idle,Power Down和Sleep(简称AIDS),外加3个过渡功耗模式:Pre-Active, Pre-Sleep和Pre-PowerDown,一共是7种功耗模式。非常4+3!
Active模式:UFS设备在实行下令大概做背景任务(Background Operation)时处于这种状态;
Idle模式:UFS设备空闲时,即既没有来自UFS主机的下令,自身也没有背景任务需要处理,设备就处于该状态;
Sleep模式:闲得打盹了。睡眠模式下,VCC电源大概被堵截(取决UFS设备设计)。VCC一样寻常给闪存供电,即堵截闪存供电。
Power Down模式:掉电模式下,所有电源供电VCC, VCCQ和VCCQ2都大概被掐断(取决UFS设备设计),该模式是最省电的功耗模式了。
这些模式之间的转换如下图:我们只需要重点关注sleep->active->idle状态

5.实战:通过一个ufs缺陷了解ufs

缺陷描述:不绝cat /sys/devices/platform/soc/4804000.ufshc/life_time_est_a 这个节点查看ufs寿命,会概率性出现体系重启。
有效的日志信息:
  1. [  333.848513] ufshcd-qcom 4804000.ufshc: pwr ctrl cmd 0x18 with mode 0x0 completion timeout
  2. [  334.360545] ufshcd-qcom 4804000.ufshc: pwr ctrl cmd 0x18 with mode 0x0 completion timeout
  3. [  334.868572] ufshcd-qcom 4804000.ufshc: pwr ctrl cmd 0x18 with mode 0x0 completion timeout
  4. [  334.972668] ufshcd-qcom 4804000.ufshc: pwr ctrl cmd 0x18 failed, host upmcrs:0x5
  5. [  334.980372] ufshcd-qcom 4804000.ufshc: UFS Host state=2
  6. [  334.985868] ufshcd-qcom 4804000.ufshc: lrb in use=0x0, outstanding reqs=0x0 tasks=0x0
  7. [  334.994068] ufshcd-qcom 4804000.ufshc: saved_err=0x0, saved_uic_err=0x0, saved_ce_err=0x0
  8. [  335.002634] ufshcd-qcom 4804000.ufshc: Device power mode=2, UIC link state=2
  9. [  335.009929] ufshcd-qcom 4804000.ufshc: PM in progress=1, sys. suspended=0
  10. [  335.016949] ufshcd-qcom 4804000.ufshc: Auto BKOPS=0, Host self-block=1
  11. [  335.023709] ufshcd-qcom 4804000.ufshc: Clk gate=1, hibern8 on idle=4
  12. [  335.030709] ufshcd-qcom 4804000.ufshc: error handling flags=0x0, req. abort count=0
  13. [  335.038709] ufshcd-qcom 4804000.ufshc: Host capabilities=0x1587031f, caps=0x8f
  14. [  335.046185] ufshcd-qcom 4804000.ufshc: quirks=0x0, dev. quirks=0xe2
  15. [  335.052757] ufshcd-qcom 4804000.ufshc: pa_err_cnt_total=0, pa_lane_0_err_cnt=0, pa_lane_1_err_cnt=0, pa_line_reset_err_cnt=0
复制代码


通过Device power mode=2,UIC link state=0可以知道,电源进入sleep模式,链路状态进入disabeld状态。
思路1:想办法包管power和link是active状态,直接调ufshcd_resume回复power和link为正常状态。
  1. 设置ufs电源为active状态
  2. #define ufshcd_set_ufs_dev_active(h) \
  3.         ((h)->curr_dev_pwr_mode = UFS_ACTIVE_PWR_MODE)
  4. /**
  5. * ufshcd_runtime_resume - runtime resume routine
  6. * @hba: per adapter instance
  7. *
  8. * This function basically brings the UFS device, UniPro link and controller
  9. * to active state. Following operations are done in this function:
  10. *
  11. * 1. Turn on all the controller related clocks
  12. * 2. Bring the UniPro link out of Hibernate state
  13. * 3. If UFS device is in sleep state, turn ON VCC rail and bring the UFS device
  14. *    to active state.
  15. * 4. If auto-bkops is enabled on the device, disable it.
  16. *
  17. * So following would be the possible power state after this function return
  18. * successfully:
  19. *        S1: UFS device in Active state with VCC rail ON
  20. *            UniPro link in Active state
  21. *            All the UFS/UniPro controller clocks are ON
  22. *
  23. * Returns 0 for success and non-zero for failure
  24. */
  25. int ufshcd_runtime_resume(struct ufs_hba *hba)
  26. {
  27.         int ret = 0;
  28.         ktime_t start = ktime_get();
  29.         if (!hba)
  30.                 return -EINVAL;
  31.         if (!hba->is_powered)
  32.                 goto out;
  33.         else
  34.                 ret = ufshcd_resume(hba, UFS_RUNTIME_PM);
  35. out:
  36.         trace_ufshcd_runtime_resume(dev_name(hba->dev), ret,
  37.                 ktime_to_us(ktime_sub(ktime_get(), start)),
  38.                 hba->curr_dev_pwr_mode, hba->uic_link_state);
  39.         return ret;
  40. }
  41. EXPORT_SYMBOL(ufshcd_runtime_resume);
  42. int ufshcd_resume();
  43.         int ufshcd_vreg_set_hpm()
  44.         int ufshcd_setup_vreg(hba, true);
  45.                         ufshcd_toggle_vreg(dev, info->vcc, on);//给ufs设备供电
  46.                         ufshcd_toggle_vreg(dev, info->vccq, on);
  47.                         ufshcd_toggle_vreg(dev, info->vccq2, on);
  48.                         ufshcd_vops_resume(hba, pm_op);
  49.                                 hba->vops->resume(hba, op);
  50.                         ret = ufshcd_uic_hibern8_exit(hba);//退出链路休眠模式(hibern8是链路休眠模式)
  51.                                 ufshcd_set_link_active(hba);//激活链路
复制代码
补充:三个供电电压,VCC,VCCQ和VCCQ2,分别给UFS设备模块供电。UFS设备重要包罗三部分:前端UFS接口(M-PHY)UFS控制器闪存介质(图中的Memory模块)。VCC 给 闪存介质供电,VCCQ 一样寻常给闪存输入输出接口 和 UFS控制器供电,VCCQ2一样寻常给 M-PHY或其它一些低电压模块供电。

思路2:想办法包管power和link是active状态,直接调用pm_runtime_get_sync进入resume恢复power 为active状态。
pm_runtime_get_sync/pm_runtime_put_sync 增加/减少计数值,并判定是否进入suspend/resume。
  1. pm_runtime_get_sync
  2.     __pm_runtime_resume(dev, RPM_GET_PUT)
  3.         atomic_inc(&dev->power.usage_count); // 若上级arg2&RPM_GET_PUT为真,才调用
  4.         rpm_resume(dev, rpmflags) //关闭本地CPU中断后调用它
  5.             if (dev->power.disable_depth > 0) retval = -EACCES;
  6. //若要使用runtime PM的函数,需要首先pm_runtime_enable。
  7.             if (!dev->power.timer_autosuspends)
  8.                 /*为了防止设备频繁的开关,可以设置timer_autosuspends的值*/
  9.                 pm_runtime_deactivate_timer(dev);
  10.             if (dev->power.runtime_status == RPM_ACTIVE) {  
  11.                 /*如果已经是ACTIVE,就没有必要再次resume*/
  12.             if (dev->power.runtime_status == RPM_RESUMING || dev->power.runtime_status == RPM_SUSPENDING) 如果设备正处于RPM_RESUMING和RPM_SUSPENDING状态,等待其完成
  13.             if (!parent && dev->parent)
  14.                 //增加父级的使用计数器并在必要时恢复它,在resume设备本身之前先resume父设备。
  15.             开始resume设备自己:
  16.                 dev->pm_domain->ops->runtime_resume    //或
  17.                 dev->type->pm->runtime_resume          //或
  18.                 dev->class->pm->runtime_resume         //或
  19.                 dev->bus->pm->runtime_resume           
  20.                     //或 前4个被称为subsystem level的callback,优先调用,第5个是驱动级别的。
  21.                 dev->driver->pm->runtime_resume        //或
  22.             __update_runtime_status(dev, RPM_SUSPENDED);
  23.                 //如果resume失败,重新设置回SUSPENDED状态
  24.             if (parent) atomic_inc(&parent->power.child_count);
  25.                 //如果resume成功时给父亲的child_count加1
  26.             
  27.             wake_up_all(&dev->power.wait_queue); //唤醒其它进程
  28.             
  29. pm_runtime_put_sync
  30.     __pm_runtime_idle(dev, RPM_GET_PUT)
  31.         if (!atomic_dec_and_test(&dev->power.usage_count)) //减少usage_count引用计数
  32.         rpm_idle(dev, rpmflags); //让设备进入idle状态
  33.             rpm_check_suspend_allowed
  34.                 //检查是否允许设备进入suspend状态,看来内核把idle和suspend一样看待了。
  35.                 if (dev->power.disable_depth > 0) retval = -EACCES;
  36.                 //调用时还没有pm_runtime_enable,就失败。
  37.                 if (atomic_read(&dev->power.usage_count) > 0) retval = -EAGAIN;
  38.                 if (!dev->power.ignore_children && atomic_read(&dev->power.child_count)) retval = -EBUSY; //它的孩子不全睡眠它是不能睡眠的
  39.             if (dev->power.runtime_status != RPM_ACTIVE) retval = -EAGAIN;
  40.                 //如果不是出于ACTIVE状态直接返回。
  41.             开始suspend设备自己:
  42.             dev->pm_domain->ops->runtime_suspend    //或
  43.             dev->type->pm->runtime_suspend          //或
  44.             dev->class->pm->runtime_suspend         //或
  45.             dev->bus->pm->runtime_suspend           
  46.                 //或 前4个被称为subsystem level的callback,优先调用,第5个是驱动级别的。
  47.             dev->driver->pm->runtime_suspend        //或
  48.         wake_up_all(&dev->power.wait_queue); //唤醒其它进程
复制代码
debug ufs信息

  1. ls /sys/kernel/debug/4804000.ufshc
  2. crash_on_err   dump_device_desc host_regs    reset_controller
  3. dbg_print_en   err_inj_scenario inject_fault show_hba
  4. dme_local_read err_inj_stats    power_mode   stats
  5. dme_peer_read  err_state        qcom
复制代码
可以通过cat show_hba将ufs设备信息全部打印出来。也可以通过cat host_regs将host的寄存器的值打印出来。
  1. static int ufsdbg_show_hba_show(struct seq_file *file, void *data)
  2. {
  3.         struct ufs_hba *hba = (struct ufs_hba *)file->private;
  4.         seq_printf(file, "hba->outstanding_tasks = 0x%x\n",
  5.                         (u32)hba->outstanding_tasks);
  6.         seq_printf(file, "hba->outstanding_reqs = 0x%x\n",
  7.                         (u32)hba->outstanding_reqs);
  8.         seq_printf(file, "hba->capabilities = 0x%x\n", hba->capabilities);
  9.         seq_printf(file, "hba->nutrs = %d\n", hba->nutrs);
  10.         seq_printf(file, "hba->nutmrs = %d\n", hba->nutmrs);
  11.         seq_printf(file, "hba->ufs_version = 0x%x\n", hba->ufs_version);
  12.         seq_printf(file, "hba->irq = 0x%x\n", hba->irq);
  13.         seq_printf(file, "hba->auto_bkops_enabled = %d\n",
  14.                         hba->auto_bkops_enabled);
  15.         seq_printf(file, "hba->ufshcd_state = 0x%x\n", hba->ufshcd_state);
  16.         seq_printf(file, "hba->clk_gating.state = 0x%x\n",
  17.                         hba->clk_gating.state);
  18.         seq_printf(file, "hba->eh_flags = 0x%x\n", hba->eh_flags);
  19.         seq_printf(file, "hba->intr_mask = 0x%x\n", hba->intr_mask);
  20.         seq_printf(file, "hba->ee_ctrl_mask = 0x%x\n", hba->ee_ctrl_mask);
  21.         /* HBA Errors */
  22.         seq_printf(file, "hba->errors = 0x%x\n", hba->errors);
  23.         seq_printf(file, "hba->uic_error = 0x%x\n", hba->uic_error);
  24.         seq_printf(file, "hba->saved_err = 0x%x\n", hba->saved_err);
  25.         seq_printf(file, "hba->saved_uic_err = 0x%x\n", hba->saved_uic_err);
  26.         seq_printf(file, "power_mode_change_cnt = %d\n",
  27.                         hba->ufs_stats.power_mode_change_cnt);
  28.         seq_printf(file, "hibern8_exit_cnt = %d\n",
  29.                         hba->ufs_stats.hibern8_exit_cnt);
  30.         seq_printf(file, "pa_err_cnt_total = %d\n",
  31.                         hba->ufs_stats.pa_err_cnt_total);
  32.         seq_printf(file, "pa_lane_0_err_cnt = %d\n",
  33.                         hba->ufs_stats.pa_err_cnt[UFS_EC_PA_LANE_0]);
  34.         seq_printf(file, "pa_lane_1_err_cnt = %d\n",
  35.                         hba->ufs_stats.pa_err_cnt[UFS_EC_PA_LANE_1]);
  36.         seq_printf(file, "pa_line_reset_err_cnt = %d\n",
  37.                         hba->ufs_stats.pa_err_cnt[UFS_EC_PA_LINE_RESET]);
  38.         seq_printf(file, "dl_err_cnt_total = %d\n",
  39.                         hba->ufs_stats.dl_err_cnt_total);
  40.         seq_printf(file, "dl_nac_received_err_cnt = %d\n",
  41.                         hba->ufs_stats.dl_err_cnt[UFS_EC_DL_NAC_RECEIVED]);
  42.         seq_printf(file, "dl_tcx_replay_timer_expired_err_cnt = %d\n",
  43.         hba->ufs_stats.dl_err_cnt[UFS_EC_DL_TCx_REPLAY_TIMER_EXPIRED]);
  44.         seq_printf(file, "dl_afcx_request_timer_expired_err_cnt = %d\n",
  45.         hba->ufs_stats.dl_err_cnt[UFS_EC_DL_AFCx_REQUEST_TIMER_EXPIRED]);
  46.         seq_printf(file, "dl_fcx_protection_timer_expired_err_cnt = %d\n",
  47.         hba->ufs_stats.dl_err_cnt[UFS_EC_DL_FCx_PROTECT_TIMER_EXPIRED]);
  48.         seq_printf(file, "dl_crc_err_cnt = %d\n",
  49.                         hba->ufs_stats.dl_err_cnt[UFS_EC_DL_CRC_ERROR]);
  50.         seq_printf(file, "dll_rx_buffer_overflow_err_cnt = %d\n",
  51.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_RX_BUFFER_OVERFLOW]);
  52.         seq_printf(file, "dl_max_frame_length_exceeded_err_cnt = %d\n",
  53.                 hba->ufs_stats.dl_err_cnt[UFS_EC_DL_MAX_FRAME_LENGTH_EXCEEDED]);
  54.         seq_printf(file, "dl_wrong_sequence_number_err_cnt = %d\n",
  55.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_WRONG_SEQUENCE_NUMBER]);
  56.         seq_printf(file, "dl_afc_frame_syntax_err_cnt = %d\n",
  57.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_AFC_FRAME_SYNTAX_ERROR]);
  58.         seq_printf(file, "dl_nac_frame_syntax_err_cnt = %d\n",
  59.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_NAC_FRAME_SYNTAX_ERROR]);
  60.         seq_printf(file, "dl_eof_syntax_err_cnt = %d\n",
  61.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_EOF_SYNTAX_ERROR]);
  62.         seq_printf(file, "dl_frame_syntax_err_cnt = %d\n",
  63.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_FRAME_SYNTAX_ERROR]);
  64.         seq_printf(file, "dl_bad_ctrl_symbol_type_err_cnt = %d\n",
  65.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_BAD_CTRL_SYMBOL_TYPE]);
  66.         seq_printf(file, "dl_pa_init_err_cnt = %d\n",
  67.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_PA_INIT_ERROR]);
  68.         seq_printf(file, "dl_pa_error_ind_received = %d\n",
  69.                    hba->ufs_stats.dl_err_cnt[UFS_EC_DL_PA_ERROR_IND_RECEIVED]);
  70.         seq_printf(file, "dme_err_cnt = %d\n", hba->ufs_stats.dme_err_cnt);
  71.         return 0;
  72. }
复制代码
不对ufs操纵和往ufs写数据对比

  1. UFS访问硬件资源信息
  2. ufshcd_read_desc_param()
  3.     ufshcd_query_descriptor_retry()
  4.             __ufshcd_query_descriptor()
  5.                     ufshcd_hold_all(hba);//退出hibern8模式进入active
  6.                         ufshcd_init_query()//构造请求reqest初始化
  7.             ufshcd_exec_dev_cmd()//执行命令查询ufs寿命
  8.                 ufshcd_compose_dev_cmd()//构造lrbp结构体
  9.                 ufshcd_send_command()//发送命令
  10.                 ufshcd_wait_for_dev_cmd()//等待命令执行结果
复制代码
参考资料:

https://blog.csdn.net/jasonactions/article/details/116295649
https://blog.csdn.net/don_chiang709/article/details/89314552
https://blog.csdn.net/Frank_sample/article/details/119033341
https://blog.csdn.net/jasonactions/article/details/116293944
https://blog.csdn.net/jasonactions/article/details/116932302
https://blog.csdn.net/don_chiang709/category_8805138.html
https://blog.csdn.net/frank_sample/category_11016468.html

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4