Linux中软件raid设置函数md_run_setup的实现
·
Linux软件RAID系统初始化架构总览
主初始化 (md_run_setup)
功能:软件RAID驱动在系统启动时的总控初始化
执行流程:
1. 创建设备文件 → 2. 检查自动检测配置 → 3. 执行自动检测 → 4. 设置驱动
关键特性:
- 设备接口准备:创建
/dev/md0控制设备 - 配置灵活性:支持
raid_noautodetect内核参数
设备文件管理 (create_dev + find_in_devfs)
功能:在devfs环境中创建设备文件和符号链接
协作流程:
create_dev:
1. 清理旧文件 → 2. devfs设备处理 → 3. 设备查找 → 4. 符号链接创建
↓
find_in_devfs (递归调用):
1. 目录读取 → 2. 深度优先搜索 → 3. 设备号匹配 → 4. 路径构建
设计特点:
devfs兼容:充分支持设备文件系统- 递归搜索:深度优先遍历目录树
- 相对路径:创建可移植的符号链接
驱动配置 (md_setup_drive)
功能:根据内核启动参数配置和启动RAID阵列
配置流程:
for 每个配置条目:
1. 参数解析 → 2. 设备创建 → 3. 磁盘收集 → 4. 阵列配置 → 5. 阵列启动
阵列类型支持:
- 持久性阵列:从超级块读取配置
- 非持久性阵列:运行时配置参数
数据结构关系体系
软件RAID驱动在系统启动时的初始化md_run_setup
void __init md_run_setup(void)
{
create_dev("/dev/md0", MKDEV(MD_MAJOR, 0), "md/0");
if (raid_noautodetect)
printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n");
else {
int fd = sys_open("/dev/md0", 0, 0);
if (fd >= 0) {
sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
sys_close(fd);
}
}
md_setup_drive();
}
int __init create_dev(char *name, dev_t dev, char *devfs_name)
{
char path[64];
sys_unlink(name);
if (devfs_name && devfs_name[0]) {
if (strncmp(devfs_name, "/dev/", 5) == 0)
devfs_name += 5;
sprintf(path, "/dev/%s", devfs_name);
if (sys_access(path, 0) == 0)
return sys_symlink(devfs_name, name);
}
if (!dev)
return -1;
strcpy(path, "/dev");
if (find_in_devfs(path, new_encode_dev(dev)) < 0)
return -1;
return sys_symlink(path + 5, name);
}
md_run_setup 函数
函数声明
void __init md_run_setup(void)
__init:这个函数只在系统初始化时使用,初始化完成后内存会被释放void:没有返回值- 这是MD驱动在系统启动时的初始化函数
创建设备文件
create_dev("/dev/md0", MKDEV(MD_MAJOR, 0), "md/0");
create_dev:创建设备文件的函数"/dev/md0":要创建的设备文件路径MKDEV(MD_MAJOR, 0):创建设备号,MD_MAJOR是MD设备的主设备号,0是次设备号"md/0":devfs(设备文件系统)中的设备名
检查自动检测配置
if (raid_noautodetect)
printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n");
raid_noautodetect:内核启动参数,如果设置则跳过自动检测- 如果设置了该参数,打印跳过自动检测的信息
- 这允许用户在启动时通过内核参数控制RAID自动检测行为
执行自动检测
else {
int fd = sys_open("/dev/md0", 0, 0);
if (fd >= 0) {
sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
sys_close(fd);
}
}
- 如果没有禁用自动检测:
sys_open("/dev/md0", 0, 0):打开刚刚创建的md0设备文件fd >= 0:检查文件打开是否成功sys_ioctl(fd, RAID_AUTORUN, raid_autopart):执行自动检测ioctl命令sys_close(fd):关闭设备文件
设置驱动器
md_setup_drive();
}
- 调用MD驱动设置函数
- 完成MD系统的初始化
create_dev 函数
函数声明
int __init create_dev(char *name, dev_t dev, char *devfs_name)
__init:初始化时使用的函数name:要创建的设备文件路径dev:设备号devfs_name:devfs中的设备名
变量声明和清理旧文件
{
char path[64];
sys_unlink(name);
path[64]:临时路径缓冲区sys_unlink(name):删除已存在的设备文件(如果有)- 确保从头开始创建设备文件
devfs设备处理
if (devfs_name && devfs_name[0]) {
if (strncmp(devfs_name, "/dev/", 5) == 0)
devfs_name += 5;
- 检查
devfs_name是否存在且非空 strncmp(devfs_name, "/dev/", 5):检查设备名是否以"/dev/"开头- 如果是,将指针向前移动5个字符,跳过"/dev/"前缀
检查devfs设备是否存在
sprintf(path, "/dev/%s", devfs_name);
if (sys_access(path, 0) == 0)
return sys_symlink(devfs_name, name);
}
sprintf(path, "/dev/%s", devfs_name):构造完整的devfs设备路径sys_access(path, 0):检查该设备文件是否存在且可访问- 如果存在,创建符号链接并返回
无设备号的情况
if (!dev)
return -1;
- 如果没有提供设备号,返回错误-1
- 这是安全检查,确保有有效的设备号
在devfs中查找设备
strcpy(path, "/dev");
if (find_in_devfs(path, new_encode_dev(dev)) < 0)
return -1;
strcpy(path, "/dev"):初始化路径为/dev目录find_in_devfs(path, new_encode_dev(dev)):在devfs中查找对应设备号的设备new_encode_dev(dev):将设备号编码为devfs使用的格式- 如果查找失败,返回错误-1
创建符号链接
return sys_symlink(path + 5, name);
}
path + 5:跳过"/dev/"前缀,只使用相对路径sys_symlink(path + 5, name):创建从name指向实际设备的符号链接- 返回操作结果
函数功能详解
md_run_setup 功能
主要功能:Linux软件RAID驱动在系统启动时的初始化例程
详细执行流程:
-
设备文件准备阶段
- 创建主要的MD控制设备
/dev/md0 - 为后续的
ioctl操作提供接口
- 创建主要的MD控制设备
-
自动检测配置检查
- 检查内核启动参数
raid_noautodetect - 允许用户在启动时控制RAID自动检测行为
- 提供灵活的部署选项
- 检查内核启动参数
-
自动检测执行阶段
- 打开刚创建的MD设备文件
- 通过
ioctl调用触发RAID阵列的自动检测 - 传递
raid_autopart参数控制分区行为
-
驱动设置完成阶段
- 调用MD驱动的最终设置函数
- 完成整个MD子系统的初始化
create_dev 功能
主要功能:在devfs环境中创建设备文件符号链接
详细执行流程:
-
清理准备阶段
- 删除可能已存在的旧设备文件
- 确保干净的创建环境
-
devfs优先处理- 优先使用
devfs设备名称 - 处理
devfs路径格式 - 检查
devfs设备是否实际存在
- 优先使用
-
设备号验证
- 确保提供了有效的设备号
- 防止无效设备创建
-
设备查找阶段
- 在
devfs文件系统中查找对应设备号的设备节点 - 使用设备号编码进行精确匹配
- 在
-
符号链接创建
- 创建从用户指定名称到实际设备节点的符号链接
- 处理路径格式以确保正确链接
在devfs文件系统中递归查找指定设备号的设备文件find_in_devfs
static int __init find_in_devfs(char *path, unsigned dev)
{
char *end = path + strlen(path);
int rest = path + 64 - end;
int size;
char *p = read_dir(path, &size);
char *s;
if (!p)
return -1;
for (s = p; s < p + size; s += ((struct linux_dirent64 *)s)->d_reclen) {
struct linux_dirent64 *d = (struct linux_dirent64 *)s;
if (strlen(d->d_name) + 2 > rest)
continue;
switch (d->d_type) {
case DT_BLK:
sprintf(end, "/%s", d->d_name);
if (bstat(path) != dev)
break;
kfree(p);
return 0;
case DT_DIR:
if (strcmp(d->d_name, ".") == 0)
break;
if (strcmp(d->d_name, "..") == 0)
break;
sprintf(end, "/%s", d->d_name);
if (find_in_devfs(path, dev) < 0)
break;
kfree(p);
return 0;
}
}
kfree(p);
return -1;
}
函数声明和变量定义
static int __init find_in_devfs(char *path, unsigned dev)
{
char *end = path + strlen(path);
int rest = path + 64 - end;
int size;
char *p = read_dir(path, &size);
char *s;
static int __init:静态初始化函数,只在系统启动时运行char *path:要搜索的目录路径(输入输出参数)unsigned dev:要查找的设备号char *end = path + strlen(path):指向路径字符串的末尾int rest = path + 64 - end:计算剩余缓冲区空间(路径最大64字节)int size:目录内容大小char *p = read_dir(path, &size):读取目录内容char *s:目录项遍历指针
读取目录和初始检查
if (!p)
return -1;
if (!p):检查目录读取是否成功- 如果
read_dir返回NULL,表示目录读取失败,返回-1
遍历目录项
for (s = p; s < p + size; s += ((struct linux_dirent64 *)s)->d_reclen) {
struct linux_dirent64 *d = (struct linux_dirent64 *)s;
if (strlen(d->d_name) + 2 > rest)
continue;
for (s = p; s < p + size; ...):遍历整个目录缓冲区s += ((struct linux_dirent64 *)s)->d_reclen:移动到下一个目录项struct linux_dirent64 *d = (struct linux_dirent64 *)s:转换为目录项结构if (strlen(d->d_name) + 2 > rest):检查文件名长度是否超出缓冲区剩余空间+2是为了/和字符串结束符\0- 如果超出,跳过这个目录项
处理块设备文件
switch (d->d_type) {
case DT_BLK:
sprintf(end, "/%s", d->d_name);
if (bstat(path) != dev)
break;
kfree(p);
return 0;
case DT_BLK:处理块设备类型的目录项sprintf(end, "/%s", d->d_name):将文件名追加到路径末尾if (bstat(path) != dev):获取该路径的设备号并与目标比较bstat(path):获取文件对应的设备号- 如果不匹配,跳出switch继续下一个
- 如果匹配,释放目录缓冲区并返回0(成功)
处理子目录(递归搜索)
case DT_DIR:
if (strcmp(d->d_name, ".") == 0)
break;
if (strcmp(d->d_name, "..") == 0)
break;
sprintf(end, "/%s", d->d_name);
if (find_in_devfs(path, dev) < 0)
break;
kfree(p);
return 0;
case DT_DIR:处理目录类型的目录项- 跳过当前目录
.和父目录..,避免无限递归 sprintf(end, "/%s", d->d_name):将子目录名追加到路径if (find_in_devfs(path, dev) < 0):递归调用在子目录中继续搜索- 如果递归搜索成功,释放缓冲区并返回0
清理和返回
}
}
kfree(p);
return -1;
}
- 循环结束:所有目录项都检查完毕
kfree(p):释放目录内容缓冲区return -1:没有找到匹配的设备,返回失败
递归搜索过程示意图
函数功能详解
主要功能:在devfs文件系统中递归查找指定设备号的设备文件
详细执行流程:
-
初始化阶段
- 计算当前路径的末尾位置
- 检查缓冲区剩余空间
- 读取目录内容到内存
-
目录遍历阶段
- 逐个处理目录项
- 跳过文件名过长的项(防止缓冲区溢出)
- 根据文件类型分别处理
-
块设备检查阶段
- 对于每个块设备文件
- 构造完整路径
- 获取设备号并比较
- 找到匹配立即返回
-
目录递归阶段
- 对于每个子目录(跳过
.和..) - 递归调用搜索函数
- 深度优先搜索整个目录树
- 对于每个子目录(跳过
-
清理返回阶段
- 释放目录内容内存
- 返回搜索结果
根据内核启动参数设置和启动软件RAID阵列md_setup_drive
static void __init md_setup_drive(void)
{
int minor, i, ent, partitioned;
dev_t dev;
dev_t devices[MD_SB_DISKS+1];
for (ent = 0; ent < md_setup_ents ; ent++) {
int fd;
int err = 0;
char *devname;
mdu_disk_info_t dinfo;
char name[16], devfs_name[16];
minor = md_setup_args[ent].minor;
partitioned = md_setup_args[ent].partitioned;
devname = md_setup_args[ent].device_names;
sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor);
sprintf(devfs_name, "/dev/md/%s%d", partitioned?"d":"", minor);
if (partitioned)
dev = MKDEV(mdp_major, minor << MdpMinorShift);
else
dev = MKDEV(MD_MAJOR, minor);
create_dev(name, dev, devfs_name);
for (i = 0; i < MD_SB_DISKS && devname != 0; i++) {
char *p;
char comp_name[64];
u32 rdev;
p = strchr(devname, ',');
if (p)
*p++ = 0;
dev = name_to_dev_t(devname);
if (strncmp(devname, "/dev/", 5) == 0)
devname += 5;
snprintf(comp_name, 63, "/dev/%s", devname);
rdev = bstat(comp_name);
if (rdev)
dev = new_decode_dev(rdev);
if (!dev) {
printk(KERN_WARNING "md: Unknown device name: %s\n", devname);
break;
}
devices[i] = dev;
devname = p;
}
devices[i] = 0;
if (!i)
continue;
printk(KERN_INFO "md: Loading md%s%d: %s\n",
partitioned ? "_d" : "", minor,
md_setup_args[ent].device_names);
fd = sys_open(name, 0, 0);
if (fd < 0) {
printk(KERN_ERR "md: open failed - cannot start "
"array %s\n", name);
continue;
}
if (sys_ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) {
printk(KERN_WARNING
"md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n",
minor);
sys_close(fd);
continue;
}
if (md_setup_args[ent].pers) {
/* non-persistent */
mdu_array_info_t ainfo;
ainfo.level = pers_to_level(md_setup_args[ent].pers);
ainfo.size = 0;
ainfo.nr_disks =0;
ainfo.raid_disks =0;
while (devices[ainfo.raid_disks])
ainfo.raid_disks++;
ainfo.md_minor =minor;
ainfo.not_persistent = 1;
ainfo.state = (1 << MD_SB_CLEAN);
ainfo.layout = 0;
ainfo.chunk_size = md_setup_args[ent].chunk;
err = sys_ioctl(fd, SET_ARRAY_INFO, (long)&ainfo);
for (i = 0; !err && i <= MD_SB_DISKS; i++) {
dev = devices[i];
if (!dev)
break;
dinfo.number = i;
dinfo.raid_disk = i;
dinfo.state = (1<<MD_DISK_ACTIVE)|(1<<MD_DISK_SYNC);
dinfo.major = MAJOR(dev);
dinfo.minor = MINOR(dev);
err = sys_ioctl(fd, ADD_NEW_DISK, (long)&dinfo);
}
} else {
/* persistent */
for (i = 0; i <= MD_SB_DISKS; i++) {
dev = devices[i];
if (!dev)
break;
dinfo.major = MAJOR(dev);
dinfo.minor = MINOR(dev);
sys_ioctl(fd, ADD_NEW_DISK, (long)&dinfo);
}
}
if (!err)
err = sys_ioctl(fd, RUN_ARRAY, 0);
if (err)
printk(KERN_WARNING "md: starting md%d failed\n", minor);
sys_close(fd);
}
}
函数声明和变量定义
static void __init md_setup_drive(void)
{
int minor, i, ent, partitioned;
dev_t dev;
dev_t devices[MD_SB_DISKS+1];
static void __init:静态初始化函数,只在系统启动时运行minor:MD设备的次设备号i:循环计数器ent:配置条目索引partitioned:分区标志dev:设备号变量devices[MD_SB_DISKS+1]:设备号数组,存储组成RAID的所有磁盘设备
外层循环:处理每个配置条目
for (ent = 0; ent < md_setup_ents ; ent++) {
int fd;
int err = 0;
char *devname;
mdu_disk_info_t dinfo;
char name[16], devfs_name[16];
md_setup_ents:配置条目总数(来自内核启动参数)fd:文件描述符err:错误标志devname:设备名字符串指针dinfo:磁盘信息结构name[16]:设备文件名缓冲区devfs_name[16]:devfs设备名缓冲区
获取配置参数
minor = md_setup_args[ent].minor;
partitioned = md_setup_args[ent].partitioned;
devname = md_setup_args[ent].device_names;
- 从配置数组中获取参数:
minor:次设备号partitioned:是否为分区设备devname:设备名称列表(逗号分隔)
创建设备文件
sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor);
sprintf(devfs_name, "/dev/md/%s%d", partitioned?"d":"", minor);
if (partitioned)
dev = MKDEV(mdp_major, minor << MdpMinorShift);
else
dev = MKDEV(MD_MAJOR, minor);
create_dev(name, dev, devfs_name);
- 根据分区标志生成不同的设备名:
- 分区设备:
/dev/md_d0,devfs:/dev/md/d0 - 整盘设备:
/dev/md0,devfs:/dev/md/0
- 分区设备:
- 生成对应的设备号
- 调用
create_dev创建设备文件
解析设备名称列表
for (i = 0; i < MD_SB_DISKS && devname != 0; i++) {
char *p;
char comp_name[64];
u32 rdev;
p = strchr(devname, ',');
if (p)
*p++ = 0;
- 遍历设备名称列表(逗号分隔)
strchr(devname, ','):查找逗号分隔符- 如果找到逗号,将其替换为
\0并移动指针到下一个设备名
转换设备名称为设备号
dev = name_to_dev_t(devname);
if (strncmp(devname, "/dev/", 5) == 0)
devname += 5;
snprintf(comp_name, 63, "/dev/%s", devname);
rdev = bstat(comp_name);
if (rdev)
dev = new_decode_dev(rdev);
name_to_dev_t(devname):将设备名转换为设备号- 如果设备名以
/dev/开头,跳过此前缀 - 构造完整设备路径
bstat(comp_name):获取设备的实际设备号- 如果获取成功,使用实际的设备号
设备号验证和存储
if (!dev) {
printk(KERN_WARNING "md: Unknown device name: %s\n", devname);
break;
}
devices[i] = dev;
devname = p;
}
devices[i] = 0;
- 检查设备号是否有效
- 如果无效,打印警告并跳出循环
- 将设备号存入数组
- 移动到下一个设备名
- 在数组末尾添加0作为结束标记
跳过无效配置
if (!i)
continue;
- 如果没有有效的设备,跳过这个配置条目
打印加载信息
printk(KERN_INFO "md: Loading md%s%d: %s\n",
partitioned ? "_d" : "", minor,
md_setup_args[ent].device_names);
- 打印正在加载的MD设备信息
打开MD设备
fd = sys_open(name, 0, 0);
if (fd < 0) {
printk(KERN_ERR "md: open failed - cannot start "
"array %s\n", name);
continue;
}
- 打开MD设备文件
- 如果打开失败,打印错误并继续下一个配置
检查设备是否已自动检测
if (sys_ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) {
printk(KERN_WARNING
"md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n",
minor);
sys_close(fd);
continue;
}
- 尝试设置阵列信息
- 如果返回
-EBUSY,说明设备已被自动检测到 - 打印警告信息,关闭设备并继续下一个
非持久性阵列设置
if (md_setup_args[ent].pers) {
/* non-persistent */
mdu_array_info_t ainfo;
ainfo.level = pers_to_level(md_setup_args[ent].pers);
ainfo.size = 0;
ainfo.nr_disks =0;
ainfo.raid_disks =0;
while (devices[ainfo.raid_disks])
ainfo.raid_disks++;
ainfo.md_minor =minor;
ainfo.not_persistent = 1;
ainfo.state = (1 << MD_SB_CLEAN);
ainfo.layout = 0;
ainfo.chunk_size = md_setup_args[ent].chunk;
err = sys_ioctl(fd, SET_ARRAY_INFO, (long)&ainfo);
- 为非持久性阵列设置参数:
level:RAID级别size:大小为0(自动计算)raid_disks:统计实际磁盘数量not_persistent:标记为非持久性state:设置为clean状态chunk_size:块大小
- 调用
SET_ARRAY_INFOioctl设置阵列信息
添加磁盘到非持久性阵列
for (i = 0; !err && i <= MD_SB_DISKS; i++) {
dev = devices[i];
if (!dev)
break;
dinfo.number = i;
dinfo.raid_disk = i;
dinfo.state = (1<<MD_DISK_ACTIVE)|(1<<MD_DISK_SYNC);
dinfo.major = MAJOR(dev);
dinfo.minor = MINOR(dev);
err = sys_ioctl(fd, ADD_NEW_DISK, (long)&dinfo);
}
- 遍历所有设备,逐个添加到阵列
- 设置磁盘状态为活跃和同步
- 调用
ADD_NEW_DISKioctl添加磁盘
持久性阵列设置
} else {
/* persistent */
for (i = 0; i <= MD_SB_DISKS; i++) {
dev = devices[i];
if (!dev)
break;
dinfo.major = MAJOR(dev);
dinfo.minor = MINOR(dev);
sys_ioctl(fd, ADD_NEW_DISK, (long)&dinfo);
}
}
- 对于持久性阵列,直接添加所有磁盘
- 不需要设置阵列信息(从超级块读取)
启动阵列和清理
if (!err)
err = sys_ioctl(fd, RUN_ARRAY, 0);
if (err)
printk(KERN_WARNING "md: starting md%d failed\n", minor);
sys_close(fd);
}
}
- 如果没有错误,启动阵列
- 如果启动失败,打印警告信息
- 关闭设备文件
- 循环结束
函数功能详解
主要功能:根据内核启动参数设置和启动软件RAID阵列
详细执行流程:
-
配置解析阶段
- 遍历所有通过内核参数配置的RAID阵列
- 解析每个配置条目的参数
-
设备准备阶段
- 创建设备文件(如
/dev/md0) - 解析组件磁盘的设备名称列表
- 将设备名转换为设备号
- 创建设备文件(如
-
阵列配置阶段
- 区分持久性和非持久性阵列
- 设置RAID级别、磁盘数量等参数
- 添加所有组件磁盘到阵列
-
阵列启动阶段
- 启动配置好的RAID阵列
- 处理启动失败的情况
更多推荐

所有评论(0)