Linux系统块存储子系统分析记录

news2024/11/26 10:17:15

1 Linux存储栈

通过网址Linux Storage Stack Diagram - Thomas-Krenn-Wiki-en,可以获取多个linux内核版本下的存储栈概略图,下面是kernel-4.0的存储栈概略图:

2 存储接口、传输速度 和 协议

2.1 硬盘

                《深入浅出SSD:固态存储核心技术、原理与实战》第2版,1.4 SSD基本工作原理,表1-5

                《深入浅出SSD:固态存储核心技术、原理与实战》第2版,9.2 NVMe综述,图9-4

2.2 闪存(Flash)

2.2.1 ONFI 接口

ONFI 2.0: 133MB/s
ONFI 2.1:  166BM/s 和 200MB/s (工作模式不同则速度不同)
ONFI 3.0: 400MB/s
ONFI 4.0: 800MB/s 

                《固态存储:原理、架构与数据安全》5.5 闪存接口,1. ONFI

2.2.2 Toggle接口


Toggle DDR 2.0: 400MB/s
                《固态存储:原理、架构与数据安全》5.5 闪存接口,2. Toggle

2.3 SDIO

3 Linux块设备⼦系统

3.1 简介

本文涉及的内核源码版本是kernel-5.4

3.1.1 功能框图

                《存储技术原理分析》5.1 概述,图5-1

                《Linux设备驱动开发详解:基于最新的Linux4.0内核》13.1 块设备的I/O操作特点,图13.2

3.2 通用块层 / bio layer

3.2.1 简介

In summary, the bio layer is a thin layer that takes I/O requests in the form of bio structures and passes them directly to the appropriate make_request_fn() function.
                A block layer introduction part 1: the bio layer [LWN.net]

Linux通⽤块层提供给上层的接⼝函数是submit_bio。上层在构造好bio请求之后,调⽤submit_bio提交给Linux通⽤块层处理。
                《存储技术原理分析》5.4 请求处理过程

当内核⽂件⼦系统需要与块设备进⾏数据传输或者对块设备发送控制命令时,内核需 要向对应块设备所属的请求队列发送请求对象。这个任务由函数submit_bio来完成。
                《深⼊Linux设备驱动程序内核机制》 11.13 向队列提交请求

3.2.2 数据结构

3.3 request layer 和 I/O调度层

3.3.1 简介

接收通⽤块层发出的I/O请求,缓存请求并试图合并相邻的请求,并根据设置好的调度算法,回调驱动层提供的请求处理函数,以处理具体的I/O请求。
                《存储技术原理分析》5.1 概述

3.3.2 single queue 和 Multiple queue(blk-mq)

Traditionally, most storage devices were made up of a set of spinning circular platters with magnetic coating and a single head (or set of heads, one per platter) that moved along the radius of the spinning disk to read or change the magnetic polarization at any location on any platter. Such a device can only process a single request at a time, and has a substantial cost in moving from one location on the platters to another. The single-queue implementation started out aimed at driving this sort of device and, while it has broadened in scope over the years, its structure still reflects the needs of rotating storage devices.
                Block layer introduction part 2: the request layer [LWN.net]

blk-mq (Multi-Queue Block IO Queueing Mechanism) is a new framework for the Linux block layer that was introduced with Linux Kernel 3.13, and which has become feature-complete with Kernel 3.16.[1] Blk-mq allows for over 15 million IOPS with high-performance flash devices (e.g. PCIe SSDs) on 8-socket servers, though even single and dual socket servers also benefit considerably from blk-mq.[2] To use a device with blk-mq, the device must support the respective driver.
                Linux Multi-Queue Block IO Queueing Mechanism (blk-mq) Details - Thomas-Krenn-Wiki-en

3.3.3 数据结构: request_queue 和 request(请求描述符)

3.3.4 Request affinity

On large, multiprocessor systems, there can be a performance benefit to ensuring that all processing of a block I/O request happens on the same CPU. In particular, data associated with a given request is most likely to be found in the cache of the CPU which originated that request, so it makes sense to perform the request postprocessing on that same CPU.

设置方式
        /sys/class/block//queue/rq_affinity

If it is set to a non-zero value, CPU affinity will be turned on for that device.

                Block layer: solid-state storage, timeouts, affinity, and more [LWN.net]

3.3.5 I/O调度

3.3.6 请求处理的代码流程

3.4 块设备驱动层

3.4.1 数据结构:struct blk_mq_ops;

来自上层的request最终会通过具体存储设备驱动的queue_rq()下发到存储设备上,然后存储设备会进行处理,处理完成后,存储设备会产生一个中断通知CPU,CPU在中断处理程序中进行request的完成操作。

常见存储设备驱动的queue_rq()函数:

3.4.2 request处理超时

每个request下发给存储设备后,留给存储设备的处理时间是有限的,默认是30秒,可以通过/sys/class/block/<disk>/queue/io_timeout修改。

在queue_rq()实例函数(如scsi_queue_rq())中都会调用blk_mq_start_request(),blk_mq_start_request()内核会设置定时器

blk_mq_start_request();
    -> trace_block_rq_issue(q, rq);
    -> rq->io_start_time_ns = ktime_get_ns();
    -> blk_add_timer(rq);
        -> req->timeout = q->rq_timeout;
        -> expiry = jiffies + req->timeout;
        -> mod_timer(&q->timeout, expiry);

超时处理函数为blk_mq_timeout_work,超时时间默认为30秒,超时工作项处理函数为blk_mq_timeout_work

blk_mq_init_queue();   //申请request_queue
    -> blk_alloc_queue_node();
        -> timer_setup(&q->timeout, blk_rq_timed_out_timer, 0);
    -> blk_mq_init_allocated_queue();
        -> INIT_WORK(&q->timeout_work, blk_mq_timeout_work);
        -> blk_queue_rq_timeout(q, set->timeout ? set->timeout : 30 * HZ);
            -> q->rq_timeout = timeout;

 超时处理流程

 具体的超时处理工作留给存储设备驱动来完成。

4 不同存储设备的request处理过程

4.1 SATA、SCSI 和 SAS类存储设备

4.1.1 请求下发的流程

scsi_queue_rq();
    -> blk_mq_start_request();
    -> scsi_dispatch_cmd();
        -> scsi_log_send(cmd);
            -> scmd_printk(..., "Send: scmd 0x%p\n", cmd);
            -> scsi_print_command();
        -> host->hostt->queuecommand();

4.1.2 存储设备处理完成,产生中断,CPU处理中断的流程

在硬件中断被引发时,中断回调函数将会被调⽤,如果是对SCSI命令的响应,则将找到对应的 scsi_cmnd描述符,低层设备驱动处理完这个请求后,调⽤保存在它⾥⾯的scsi_done函数。
                《存储技术原理分析》5.6.1

在scsi_queue_rq()中,scsi_done被赋值为scsi_mq_done。

scsi_queue_rq();
    -> cmd->scsi_done = scsi_mq_done;

所以中断处理流程如下: 

scsi_mq_done();
    -> trace_scsi_dispatch_cmd_done(cmd);
    -> blk_mq_complete_request();
        -> __blk_mq_complete_request(rq);
            -> WRITE_ONCE(rq->state, MQ_RQ_COMPLETE);
            -> __blk_complete_request();
                -> raise_softirq_irqoff(BLOCK_SOFTIRQ);   //发出软中断

软中断BLOCK_SOFTIRQ的处理函数是blk_done_softirq 

blk_done_softirq();
    -> rq->q->mq_ops->complete(rq);
        -> scsi_softirq_done();
            -> scsi_decide_disposition(cmd);
            -> scsi_log_completion();
                -> scsi_print_result(cmd, "Done", disposition);
                -> scsi_print_command();
                -> scmd_printk(..., "scsi host busy %d failed %d\n", ...);
            -> case SUCCESS:  scsi_finish_command(cmd);
                -> SCSI_LOG_MLCOMPLETE( ... "Notifying upper driver of completion " ...);

4.1.3 实际的dmesg信息

打开SCSI的日志开关:echo 0xffff > /sys/module/scsi_mod/parameters/scsi_logging_level

当系统下有硬盘操作时,就会在dmesg信息里看到如下信息: 

向硬盘发送命令时的dmesg信息:
sd 2:0:0:0: [sda] tag#25 Send: scmd 0x0000000049a58ebd
sd 2:0:0:0: [sda] tag#25 CDB: Write(10) 2a 00 0a 42 60 00 00 00 40 00
......
硬盘收到命令后,对命令进行处理,处理完成后产生中断通知CPU,下面是CPU处理中断时的dmesg信息:
sd 2:0:0:0: [sda] tag#25 Done: SUCCESS Result: hostbyte=DID_OK driverbyte=DRIVER_OK
sd 2:0:0:0: [sda] tag#25 CDB: Write(10) 2a 00 0a 42 60 00 00 00 40 00
sd 2:0:0:0: [sda] tag#25 scsi host busy 1 failed 0
sd 2:0:0:0: Notifying upper driver of completion (result 0)

dmesg信息简单说明 

CDB: Command Descriptor Block 

Write(10)是SCSI的命令,含义如下:
(更多SCSI命令,请看《SCSI Commands Reference Manual》,下载链接:https://www.seagate.com/staticfiles/support/disc/manuals/scsi/100293068a.pdf)

4.2 NVMe

4.2.1 简介

当前有很多种NVMe的实现方式,例如:

  •         NVMe over PCIe
  •         NVMe over RDMA
  •         NVMe over TCP
  •         NVMe over FC

                《深⼊浅出SSD:固态存储核⼼技术、原理与实战》第2版,9.9 NVMe over Fabrics

下面以NVMe over PCIe为例,介绍request的处理流程

4.2.2 NVMe处理命令 的⼋个步骤

第⼀步,主机写命令到内存中的SQ;
第⼆步,主机写SQ的DB,通知SSD取指;
第三步,SSD收到通知后,到SQ中取指;
第四步,SSD执⾏指令;
第五步,指令执⾏完成,SSD往CQ中写指令执⾏结果;
第六步,SSD发送中断通知主机指令完成;
第七步,收到中断,主机处理CQ,查看指令完成状态;
第⼋步,主机处理完CQ中指令执⾏结果,通过DB恢复SSD。

                《深⼊浅出SSD:固态存储核⼼技术、原理与实 战》第2版,9.2 NVMe综述

4.2.3 请求(命令)下发

nvme_queue_rq();
    -> nvme_setup_cmd();
        -> trace_nvme_setup_cmd();
    -> blk_mq_start_request();
    -> nvme_submit_cmd();
        -> memcpy(nvmeq->sq_cmds + (nvmeq->sq_tail << nvmeq->sqes), cmd, sizeof(*cmd));    //第⼀步,主机写命令到内存中的SQ;
        -> nvme_write_sq_db();    //第⼆步,主机写SQ的DB,通知SSD取指

4.2.4 存储设备处理完成,产生中断,CPU处理中断的流程

nvme_irq();             //第七步,收到中断,主机处理CQ,查看指令完成状态;
    -> nvme_process_cq();
        -> nvme_ring_cq_doorbell();
    -> nvme_complete_cqes();
        -> nvme_handle_cqe();
            -> trace_nvme_sq();
            -> nvme_end_request();
                -> blk_mq_complete_request(req);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2228303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python爬虫入门篇!

毕设是做爬虫相关的&#xff0c;本来想的是用java写&#xff0c;也写了几个爬虫&#xff0c;其中一个是爬网易云音乐的用户信息&#xff0c;爬了大概100多万&#xff0c;效果不是太满意。之前听说Python这方面比较强&#xff0c;就想用Python试试&#xff0c;之前也没用过Pytho…

从0开始搭建一个生产级SpringBoot2.0.X项目(三)SpringBoot接口统一返回和全局异常处理

前言 最近有个想法想整理一个内容比较完整springboot项目初始化Demo。 SpringBoot接口统一返回和全局异常处理&#xff0c;使用ControllerAdvice ExceptionHandler 的组合来实现。 一、pom文件新增依赖 <dependency><groupId>com.alibaba</groupId><ar…

【MySQL】实战篇—项目需求分析:ER图的绘制与关系模型设计

在软件开发中&#xff0c;数据库是信息系统的核心部分&#xff0c;合理的数据库设计能够显著提高系统的性能和可维护性。 ER图&#xff08;实体-关系图&#xff09;是数据库设计的重要工具&#xff0c;它通过图形化的方式描述了数据实体及其相互关系&#xff0c;帮助开发者和设…

输入整数n,求,i从1到n的和

// 第一题&#xff0c;输入整数n&#xff0c;求&#xff0c;i从1到n的和 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() {int n 0;printf("请输入一个整数n:");scanf("%d", &n);int i 0;int j 0;for (j 1; j < n; j){i…

频率限制:WAF保护网站免受恶意攻击的关键功能

频率限制&#xff08;Rate Limiting&#xff09;是一项有效的安全措施&#xff0c;用于控制每个 IP 地址的访问速率&#xff0c;以防止恶意用户利用大量请求对网站进行攻击&#xff0c;例如防止 CC 攻击等。频率限制不仅能保护网站资源&#xff0c;还能提升服务的稳定性。 下面…

C++基础: string(3)

文章目录 1. 两道题目1. [387. 字符串中的第一个唯一字符 - 力扣&#xff08;LeetCode&#xff09;](https://leetcode.cn/problems/first-unique-character-in-a-string/description/)2.[415. 字符串相加 - 力扣&#xff08;LeetCode&#xff09;](https://leetcode.cn/proble…

建设NFS服务器并实现文件共享

关闭防火墙和s0 systemctl stop firewalld setenforce 0 安装NFS yum install nfs-utils -y 新建共享目录并设置权限 echo "hello" > /nfs/shared/test1 chmod -Rf 777 /nfs/shared/ 配置服务端的NFS配置文件 vim /etc/exports /nfs/shared *(ro) 启动…

【Java】方法的使用 —— 语法要求、方法的重载和签名、方法递归

目录 1. 方法基础知识 1.1 方法的概念 1.2 语法格式 * 注意事项【与C不同】 1.3 return —— 返回值的严格检查【比C语言严格】 2. 形参与实参的关系 3. 方法重载 3.1 什么是方法重载&#xff1f;为什么要方法重载&#xff1f; 3.2 方法重载的规则 4. 方法签名 5. 递…

Chrome和Firefox如何保护用户的浏览数据

在当今数字化时代&#xff0c;保护用户的浏览数据变得尤为重要。浏览器作为我们日常上网的主要工具&#xff0c;其安全性直接关系到个人信息的保密性。本文将详细介绍Chrome和Firefox这两款主流浏览器如何通过一系列功能来保护用户的浏览数据。&#xff08;本文由https://chrom…

20241030在荣品PRO-RK3566开发板的适配Rockchip原厂的buildroot的时候配置DTS中的电源域

20241030在荣品PRO-RK3566开发板的适配Rockchip原厂的buildroot的时候配置DTS中的电源域 2024/10/30 17:38 请问 RK3566开发板上的 电源配置 和 DTS文件是如何对应的&#xff1f; 底板原理图 PRO-RK3566-B-20210329原理图.pdf vccio4-supply 是1.8V。 对不上呀&#xff1f; Z:…

Java 内部类(13/30)

目录 Java 内部类 1. 内部类的概念和类型 1.1 成员内部类 1.2 局部内部类 1.3 匿名内部类 1.4 静态内部类 2. 内部类的用途和优势 3. 内部类的注意事项 总结与后续 Java 内部类 内部类&#xff08;Inner Class&#xff09;是定义在另一个类内部的类。在 Java 中&…

c++编解码封装

多态版编解码 对服务器和客户端的结构体进行序列化然后对数据进行反序列化 案例分析 代码demo Codec.h #pragma once #include <iostream>class Codec { public:Codec();virtual std::string encodeMsg();//string是标准库的string类virtual void* decodeMsg();virtu…

WPF拖拽交互全攻略及实现自定义拖拽控件及数据交换技巧解析

目录 1. 基本概念2 . 实现拖拽功能概述需要要实现基本的拖放&#xff0c;完成以下任务&#xff1a;其他操作 示例3.1 设置拖拽源&#xff0c;拖拽开始3.2 设置拖拽效果DragDropEffects 3.3 设置放置目标&#xff0c;处理拖拽数据拖拽输入DragEnter事件DragOver事件拖拽离开Drag…

【jvm】为什么Xms和Xmx的值通常设置为相同的?

目录 1. 说明2. 避免性能开销3. 提升稳定性4. 简化配置5. 优化垃圾收集6. 获取参数6.1 代码示例6.2 结果示例 1. 说明 1.-Xms 和 -Xmx 参数分别用于设置堆内存的初始大小&#xff08;最小值&#xff09;和最大大小。2.在开发环境中&#xff0c;开发人员可能希望快速启动应用程…

c++提示函数可能不安全This function or variable may be unsafe

现象 编译c代码时&#xff0c;经常会出现如下提示&#xff0c;大约有几种情况&#xff1a; 现象一 warning C4996: strcpy: This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online …

苹果ANE引擎以及使用编程方法介绍

苹果ANE引擎介绍 大多数新款iPhone和iPad都配备了一个神经网络引擎&#xff08;Neural Engine&#xff09;&#xff0c;这是一种特殊的处理器&#xff0c;能让机器学习模型的运行速度变得非常快。然而&#xff0c;关于这个处理器具体如何工作&#xff0c;公众所知的信息并不多…

Python | Leetcode Python题解之第516题最长回文子序列

题目&#xff1a; 题解&#xff1a; class Solution:def longestPalindromeSubseq(self, s: str) -> int:n len(s)dp [[0] * n for _ in range(n)]for i in range(n - 1, -1, -1):dp[i][i] 1for j in range(i 1, n):if s[i] s[j]:dp[i][j] dp[i 1][j - 1] 2else:dp…

网络编程 UDP编程 Linux环境 C语言实现

UDP编程 1. 一般UDP编程 UDP传输特点&#xff1a;非面向连接、不可靠的、无序的 报式传输 支持组播和广播 UDP应用数据最大长度建议&#xff1a;MTU(以太网分组数据的最大长度)1500 - 20(IP头) - 8(UDP头) 1472Bytes 客户端&#xff1a;支持两种形式的代码编写: 1. 不定向…

高压线路覆冰厚度测量,输电线路微波覆冰监测装置守护电网安全

随着北方地区正式步入冬季&#xff0c;气温急剧下降&#xff0c;高压线路覆冰现象日益严重&#xff0c;给电网安全带来了前所未有的挑战。近日&#xff0c;在东北某省&#xff0c;由于连续低温天气&#xff0c;多条高压线路遭受了严重的覆冰侵袭&#xff0c;这不仅极大地加重了…

Java面试经典 150 题.P88. 合并两个有序数组(001)

本题来自&#xff1a;力扣-面试经典 150 题 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台https://leetcode.cn/studyplan/top-interview-150/ 题解 class Solution {public void merge(int[] nums1, int m, int[] nums2, …