Linux中软件raid管理接口md_ioctl函数的实现
·
Linux软件RAID管理系统架构总览
管理接口 (md_ioctl)
功能:提供完整的RAID设备管理API
// 三大命令类别:
1. 全局命令:RAID_VERSION, PRINT_RAID_DEBUG, RAID_AUTORUN
2. 配置命令:SET_ARRAY_INFO, ADD_NEW_DISK, RUN_ARRAY
3. 查询命令:GET_ARRAY_INFO, GET_DISK_INFO, HDIO_GETGEO
安全机制:
- 权限检查:
CAP_SYS_ADMIN - 并发控制:
mddev_lock/mddev_unlock - 状态验证:只读阵列限制、未初始化检查
自动检测 (autostart_arrays)
功能:系统启动时自动扫描和检测RAID设备
工作流程:
检测设备 → 导入设备 → 收集设备 → 自动启动
↓ ↓ ↓ ↓
dev_cnt md_import pending_ autorun_
_device list devices
设备导入 (md_import_device)
功能:将物理块设备转换为MD可管理设备
关键操作:
1. 内存分配:rdev结构 + 超级块页面
2. 设备锁定:防止并发访问
3. 超级块读取:验证RAID元数据
4. 属性初始化:desc_nr, faulty, in_sync等
自动运行 (autorun_devices)
功能:自动组装和启动检测到的RAID阵列
组装流程:
候选设备分组 → MD设备创建 → 设备绑定 → 阵列启动
↓ ↓ ↓ ↓
super_90_load md_probe bind_rdev autorun_
mddev_find _to_array array
软件RAID设备的管理接口md_ioctl
static int md_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
void __user *argp = (void __user *)arg;
struct hd_geometry __user *loc = argp;
mddev_t *mddev = NULL;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
/*
* Commands dealing with the RAID driver but not any
* particular array:
*/
switch (cmd)
{
case RAID_VERSION:
err = get_version(argp);
goto done;
case PRINT_RAID_DEBUG:
err = 0;
md_print_devices();
goto done;
#ifndef MODULE
case RAID_AUTORUN:
err = 0;
autostart_arrays(arg);
goto done;
#endif
default:;
}
/*
* Commands creating/starting a new array:
*/
mddev = inode->i_bdev->bd_disk->private_data;
if (!mddev) {
BUG();
goto abort;
}
if (cmd == START_ARRAY) {
/* START_ARRAY doesn't need to lock the array as autostart_array
* does the locking, and it could even be a different array
*/
static int cnt = 3;
if (cnt > 0 ) {
printk(KERN_WARNING
"md: %s(pid %d) used deprecated START_ARRAY ioctl. "
"This will not be supported beyond 2.6\n",
current->comm, current->pid);
cnt--;
}
err = autostart_array(new_decode_dev(arg));
if (err) {
printk(KERN_WARNING "md: autostart failed!\n");
goto abort;
}
goto done;
}
err = mddev_lock(mddev);
if (err) {
printk(KERN_INFO
"md: ioctl lock interrupted, reason %d, cmd %d\n",
err, cmd);
goto abort;
}
switch (cmd)
{
case SET_ARRAY_INFO:
{
mdu_array_info_t info;
if (!arg)
memset(&info, 0, sizeof(info));
else if (copy_from_user(&info, argp, sizeof(info))) {
err = -EFAULT;
goto abort_unlock;
}
if (mddev->pers) {
err = update_array_info(mddev, &info);
if (err) {
printk(KERN_WARNING "md: couldn't update"
" array info. %d\n", err);
goto abort_unlock;
}
goto done_unlock;
}
if (!list_empty(&mddev->disks)) {
printk(KERN_WARNING
"md: array %s already has disks!\n",
mdname(mddev));
err = -EBUSY;
goto abort_unlock;
}
if (mddev->raid_disks) {
printk(KERN_WARNING
"md: array %s already initialised!\n",
mdname(mddev));
err = -EBUSY;
goto abort_unlock;
}
err = set_array_info(mddev, &info);
if (err) {
printk(KERN_WARNING "md: couldn't set"
" array info. %d\n", err);
goto abort_unlock;
}
}
goto done_unlock;
default:;
}
/*
* Commands querying/configuring an existing array:
*/
/* if we are initialised yet, only ADD_NEW_DISK or STOP_ARRAY is allowed */
if (!mddev->raid_disks && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY && cmd != RUN_ARRAY) {
err = -ENODEV;
goto abort_unlock;
}
/*
* Commands even a read-only array can execute:
*/
switch (cmd)
{
case GET_ARRAY_INFO:
err = get_array_info(mddev, argp);
goto done_unlock;
case GET_DISK_INFO:
err = get_disk_info(mddev, argp);
goto done_unlock;
case RESTART_ARRAY_RW:
err = restart_array(mddev);
goto done_unlock;
case STOP_ARRAY:
err = do_md_stop (mddev, 0);
goto done_unlock;
case STOP_ARRAY_RO:
err = do_md_stop (mddev, 1);
goto done_unlock;
/*
* We have a problem here : there is no easy way to give a CHS
* virtual geometry. We currently pretend that we have a 2 heads
* 4 sectors (with a BIG number of cylinders...). This drives
* dosfs just mad... ;-)
*/
case HDIO_GETGEO:
if (!loc) {
err = -EINVAL;
goto abort_unlock;
}
err = put_user (2, (char __user *) &loc->heads);
if (err)
goto abort_unlock;
err = put_user (4, (char __user *) &loc->sectors);
if (err)
goto abort_unlock;
err = put_user(get_capacity(mddev->gendisk)/8,
(short __user *) &loc->cylinders);
if (err)
goto abort_unlock;
err = put_user (get_start_sect(inode->i_bdev),
(long __user *) &loc->start);
goto done_unlock;
}
/*
* The remaining ioctls are changing the state of the
* superblock, so we do not allow read-only arrays
* here:
*/
if (mddev->ro) {
err = -EROFS;
goto abort_unlock;
}
switch (cmd)
{
case ADD_NEW_DISK:
{
mdu_disk_info_t info;
if (copy_from_user(&info, argp, sizeof(info)))
err = -EFAULT;
else
err = add_new_disk(mddev, &info);
goto done_unlock;
}
case HOT_REMOVE_DISK:
err = hot_remove_disk(mddev, new_decode_dev(arg));
goto done_unlock;
case HOT_ADD_DISK:
err = hot_add_disk(mddev, new_decode_dev(arg));
goto done_unlock;
case SET_DISK_FAULTY:
err = set_disk_faulty(mddev, new_decode_dev(arg));
goto done_unlock;
case RUN_ARRAY:
err = do_md_run (mddev);
goto done_unlock;
default:
if (_IOC_TYPE(cmd) == MD_MAJOR)
printk(KERN_WARNING "md: %s(pid %d) used"
" obsolete MD ioctl, upgrade your"
" software to use new ictls.\n",
current->comm, current->pid);
err = -EINVAL;
goto abort_unlock;
}
done_unlock:
abort_unlock:
mddev_unlock(mddev);
return err;
done:
if (err)
MD_BUG();
abort:
return err;
}
函数声明和变量定义
static int md_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
void __user *argp = (void __user *)arg;
struct hd_geometry __user *loc = argp;
mddev_t *mddev = NULL;
inode:设备文件的inodefile:设备文件结构cmd:ioctl命令arg:用户空间参数指针err:错误返回值,初始为0argp:转换为用户空间指针的参数loc:硬盘几何结构指针(用于HDIO_GETGEO命令)mddev:MD(多磁盘)设备指针,初始为NULL
权限检查
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
capable(CAP_SYS_ADMIN):检查当前进程是否有系统管理员权限- 如果没有权限,返回
-EACCES(Permission denied) - MD设备操作需要root权限
全局RAID命令处理
switch (cmd)
{
case RAID_VERSION:
err = get_version(argp);
goto done;
case PRINT_RAID_DEBUG:
err = 0;
md_print_devices();
goto done;
#ifndef MODULE
case RAID_AUTORUN:
err = 0;
autostart_arrays(arg);
goto done;
#endif
default:;
}
RAID_VERSION:获取RAID驱动版本信息PRINT_RAID_DEBUG:打印所有RAID设备调试信息RAID_AUTORUN:自动启动阵列- 这些命令不针对特定阵列,处理完直接返回
获取MD设备指针
mddev = inode->i_bdev->bd_disk->private_data;
if (!mddev) {
BUG();
goto abort;
}
- 从块设备磁盘结构获取MD设备私有数据
- 如果
mddev为NULL,触发内核BUG(严重错误) - 跳转到abort标签处理
START_ARRAY命令处理(已弃用)
if (cmd == START_ARRAY) {
static int cnt = 3;
if (cnt > 0 ) {
printk(KERN_WARNING
"md: %s(pid %d) used deprecated START_ARRAY ioctl. "
"This will not be supported beyond 2.6\n",
current->comm, current->pid);
cnt--;
}
err = autostart_array(new_decode_dev(arg));
if (err) {
printk(KERN_WARNING "md: autostart failed!\n");
goto abort;
}
goto done;
}
- 显示弃用警告,最多显示3次
new_decode_dev(arg):将设备号参数转换为dev_t类型autostart_array():自动启动指定设备号的阵列- 如果启动失败,打印警告并跳转
获取MD设备锁
err = mddev_lock(mddev);
if (err) {
printk(KERN_INFO
"md: ioctl lock interrupted, reason %d, cmd %d\n",
err, cmd);
goto abort;
}
mddev_lock(mddev):获取MD设备的互斥锁- 如果获取锁被中断,打印信息并跳转
- 确保对MD设备的操作是原子的
SET_ARRAY_INFO命令处理
case SET_ARRAY_INFO:
{
mdu_array_info_t info;
if (!arg)
memset(&info, 0, sizeof(info));
else if (copy_from_user(&info, argp, sizeof(info))) {
err = -EFAULT;
goto abort_unlock;
}
- 定义阵列信息结构
- 如果
arg为NULL,清空info结构 - 否则从用户空间复制数据,失败返回
-EFAULT
if (mddev->pers) {
err = update_array_info(mddev, &info);
if (err) {
printk(KERN_WARNING "md: couldn't update"
" array info. %d\n", err);
goto abort_unlock;
}
goto done_unlock;
}
mddev->pers:检查阵列是否已有个性模块(已启动)- 如果已启动,更新阵列信息
- 更新失败打印警告
if (!list_empty(&mddev->disks)) {
printk(KERN_WARNING
"md: array %s already has disks!\n",
mdname(mddev));
err = -EBUSY;
goto abort_unlock;
}
if (mddev->raid_disks) {
printk(KERN_WARNING
"md: array %s already initialised!\n",
mdname(mddev));
err = -EBUSY;
goto abort_unlock;
}
- 检查磁盘列表是否为空
- 检查raid_disks是否为0(未初始化)
- 如果阵列已有磁盘或已初始化,返回
-EBUSY
err = set_array_info(mddev, &info);
if (err) {
printk(KERN_WARNING "md: couldn't set"
" array info. %d\n", err);
goto abort_unlock;
}
}
goto done_unlock;
未初始化阵列的命令限制
if (!mddev->raid_disks && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY && cmd != RUN_ARRAY) {
err = -ENODEV;
goto abort_unlock;
}
- 如果阵列未初始化(raid_disks为0)
- 只允许
ADD_NEW_DISK、STOP_ARRAY、RUN_ARRAY命令 - 其他命令返回
-ENODEV(No such device)
只读阵列允许的命令
switch (cmd)
{
case GET_ARRAY_INFO:
err = get_array_info(mddev, argp);
goto done_unlock;
case GET_DISK_INFO:
err = get_disk_info(mddev, argp);
goto done_unlock;
case RESTART_ARRAY_RW:
err = restart_array(mddev);
goto done_unlock;
case STOP_ARRAY:
err = do_md_stop (mddev, 0);
goto done_unlock;
case STOP_ARRAY_RO:
err = do_md_stop (mddev, 1);
goto done_unlock;
- 查询命令和停止命令对只读阵列也允许
GET_ARRAY_INFO:获取阵列信息GET_DISK_INFO:获取磁盘信息RESTART_ARRAY_RW:重启阵列为读写模式STOP_ARRAY:停止阵列STOP_ARRAY_RO:停止阵列为只读模式
HDIO_GETGEO命令处理
case HDIO_GETGEO:
if (!loc) {
err = -EINVAL;
goto abort_unlock;
}
err = put_user (2, (char __user *) &loc->heads);
if (err)
goto abort_unlock;
err = put_user (4, (char __user *) &loc->sectors);
if (err)
goto abort_unlock;
err = put_user(get_capacity(mddev->gendisk)/8,
(short __user *) &loc->cylinders);
if (err)
goto abort_unlock;
err = put_user (get_start_sect(inode->i_bdev),
(long __user *) &loc->start);
goto done_unlock;
- 提供虚拟的硬盘几何参数:
- heads = 2(磁头数)
- sectors = 4(每磁道扇区数)
- cylinders = 总容量/8(柱面数)
- start = 起始扇区
- 使用
put_user将数据写入用户空间
只读阵列检查
if (mddev->ro) {
err = -EROFS;
goto abort_unlock;
}
- 如果阵列是只读的,拒绝修改操作
- 返回
-EROFS(Read-only file system)
写操作命令处理
case ADD_NEW_DISK:
{
mdu_disk_info_t info;
if (copy_from_user(&info, argp, sizeof(info)))
err = -EFAULT;
else
err = add_new_disk(mddev, &info);
goto done_unlock;
}
case HOT_REMOVE_DISK:
err = hot_remove_disk(mddev, new_decode_dev(arg));
goto done_unlock;
case HOT_ADD_DISK:
err = hot_add_disk(mddev, new_decode_dev(arg));
goto done_unlock;
case SET_DISK_FAULTY:
err = set_disk_faulty(mddev, new_decode_dev(arg));
goto done_unlock;
case RUN_ARRAY:
err = do_md_run (mddev);
goto done_unlock;
ADD_NEW_DISK:添加新磁盘到阵列HOT_REMOVE_DISK:热移除磁盘HOT_ADD_DISK:热添加磁盘SET_DISK_FAULTY:设置磁盘为故障状态RUN_ARRAY:运行阵列
未知命令处理
default:
if (_IOC_TYPE(cmd) == MD_MAJOR)
printk(KERN_WARNING "md: %s(pid %d) used"
" obsolete MD ioctl, upgrade your"
" software to use new ictls.\n",
current->comm, current->pid);
err = -EINVAL;
goto abort_unlock;
- 检查命令类型是否为MD主设备号
- 如果是过时的MD
ioctl,打印升级警告 - 返回
-EINVAL(Invalid argument)
清理和返回
done_unlock:
abort_unlock:
mddev_unlock(mddev);
return err;
done:
if (err)
MD_BUG();
abort:
return err;
}
done_unlock和abort_unlock:释放MD设备锁mddev_unlock(mddev):释放互斥锁done:如果仍有错误,触发MD模块的BUG检查abort:直接返回错误
函数功能详解
主要功能:Linux软件RAID(MD)设备的管理接口
支持的操作为三大类:
-
全局RAID操作:
- 获取驱动版本信息
- 打印调试信息
- 自动启动阵列
-
阵列配置操作:
- 设置阵列信息
- 启动/停止阵列
- 添加/移除磁盘
- 设置磁盘状态
-
查询操作:
- 获取阵列信息
- 获取磁盘信息
- 获取虚拟几何参数
安全特性:
- 需要root权限(CAP_SYS_ADMIN)
- 读写阵列的状态检查
- 并发访问的锁保护
- 用户空间参数验证
自动检测并启动RAID阵列autostart_arrays
static void autostart_arrays(int part)
{
mdk_rdev_t *rdev;
int i;
printk(KERN_INFO "md: Autodetecting RAID arrays.\n");
for (i = 0; i < dev_cnt; i++) {
dev_t dev = detected_devices[i];
rdev = md_import_device(dev,0, 0);
if (IS_ERR(rdev))
continue;
if (rdev->faulty) {
MD_BUG();
continue;
}
list_add(&rdev->same_set, &pending_raid_disks);
}
dev_cnt = 0;
autorun_devices(part);
}
函数声明和变量定义
static void autostart_arrays(int part)
{
mdk_rdev_t *rdev;
int i;
static:函数只在当前文件内可见void:没有返回值part:分区参数,用于控制自动启动的行为rdev:指向MD设备中的单个磁盘设备i:循环计数器
打印检测信息
printk(KERN_INFO "md: Autodetecting RAID arrays.\n");
KERN_INFO:内核信息级别日志- 打印信息表明开始自动检测RAID阵列
- 这是系统启动时可以看到的日志信息
遍历检测到的设备
for (i = 0; i < dev_cnt; i++) {
dev_t dev = detected_devices[i];
dev_cnt:全局变量,记录检测到的设备数量detected_devices:全局数组,存储检测到的设备号dev_t dev:从数组中获取设备号- 循环遍历所有检测到的设备
导入设备
rdev = md_import_device(dev,0, 0);
if (IS_ERR(rdev))
continue;
md_import_device(dev, 0, 0):导入设备并创建rdev结构- 第一个
0:起始扇区(从设备开始处) - 第二个
0:大小(0表示整个设备)
- 第一个
IS_ERR(rdev):检查导入是否成功- 如果失败,跳过此设备继续下一个
检查设备状态
if (rdev->faulty) {
MD_BUG();
continue;
}
rdev->faulty:检查设备是否被标记为故障MD_BUG():如果设备故障,触发MD模块的BUG检查- 跳过故障设备继续处理下一个
添加到待处理列表
list_add(&rdev->same_set, &pending_raid_disks);
list_add(&rdev->same_set, &pending_raid_disks):将设备添加到待处理RAID磁盘列表rdev->same_set:rdev结构中的链表节点pending_raid_disks:全局链表头,存储所有待处理的RAID磁盘
重置设备计数
}
dev_cnt = 0;
- 循环结束
dev_cnt = 0:重置检测设备计数器,防止重复处理
启动设备自动运行
autorun_devices(part);
}
autorun_devices(part):调用自动运行函数处理所有待处理设备part:传递分区参数,控制启动行为- 函数结束
函数功能详解
主要功能:自动检测并启动RAID阵列
详细执行流程:
-
初始化阶段
- 打印开始检测的日志信息
- 准备遍历所有预先检测到的块设备
-
设备导入阶段
- 遍历
detected_devices数组中的每个设备 - 调用
md_import_device创建设备描述符 - 检查设备是否可用(非故障状态)
- 遍历
-
设备收集阶段
- 将所有可用的RAID设备添加到待处理列表
- 使用链表管理这些设备
- 重置全局计数器防止重复处理
-
自动启动阶段
- 调用
autorun_devices处理所有收集的设备 - 根据设备上的元数据自动组装RAID阵列
- 启动可用的RAID阵列
- 调用
导入一个块设备到软件RAID系统中md_import_device
static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor)
{
char b[BDEVNAME_SIZE];
int err;
mdk_rdev_t *rdev;
sector_t size;
rdev = (mdk_rdev_t *) kmalloc(sizeof(*rdev), GFP_KERNEL);
if (!rdev) {
printk(KERN_ERR "md: could not alloc mem for new device!\n");
return ERR_PTR(-ENOMEM);
}
memset(rdev, 0, sizeof(*rdev));
if ((err = alloc_disk_sb(rdev)))
goto abort_free;
err = lock_rdev(rdev, newdev);
if (err)
goto abort_free;
rdev->desc_nr = -1;
rdev->faulty = 0;
rdev->in_sync = 0;
rdev->data_offset = 0;
atomic_set(&rdev->nr_pending, 0);
size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
if (!size) {
printk(KERN_WARNING
"md: %s has zero or unknown size, marking faulty!\n",
bdevname(rdev->bdev,b));
err = -EINVAL;
goto abort_free;
}
if (super_format >= 0) {
err = super_types[super_format].
load_super(rdev, NULL, super_minor);
if (err == -EINVAL) {
printk(KERN_WARNING
"md: %s has invalid sb, not importing!\n",
bdevname(rdev->bdev,b));
goto abort_free;
}
if (err < 0) {
printk(KERN_WARNING
"md: could not read %s's sb, not importing!\n",
bdevname(rdev->bdev,b));
goto abort_free;
}
}
INIT_LIST_HEAD(&rdev->same_set);
return rdev;
abort_free:
if (rdev->sb_page) {
if (rdev->bdev)
unlock_rdev(rdev);
free_disk_sb(rdev);
}
kfree(rdev);
return ERR_PTR(err);
}
函数声明和变量定义
static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor)
{
char b[BDEVNAME_SIZE];
int err;
mdk_rdev_t *rdev;
sector_t size;
static mdk_rdev_t *:返回指向RAID设备结构的指针dev_t newdev:要导入的新设备号int super_format:超级块格式(-1表示自动检测)int super_minor:超级块次版本号b[BDEVNAME_SIZE]:缓冲区用于存储设备名err:错误码rdev:RAID设备结构指针size:设备大小(扇区数)
分配内存并初始化
rdev = (mdk_rdev_t *) kmalloc(sizeof(*rdev), GFP_KERNEL);
if (!rdev) {
printk(KERN_ERR "md: could not alloc mem for new device!\n");
return ERR_PTR(-ENOMEM);
}
memset(rdev, 0, sizeof(*rdev));
kmalloc(sizeof(*rdev), GFP_KERNEL):分配RAID设备结构内存GFP_KERNEL:标准内核内存分配标志- 如果分配失败,打印错误并返回
-ENOMEM memset(rdev, 0, sizeof(*rdev)):清空结构体,初始化为0
分配超级块内存
if ((err = alloc_disk_sb(rdev)))
goto abort_free;
alloc_disk_sb(rdev):为设备分配超级块内存页面- 如果分配失败,设置错误码并跳转到清理代码
锁定设备
err = lock_rdev(rdev, newdev);
if (err)
goto abort_free;
lock_rdev(rdev, newdev):锁定设备并打开块设备- 防止设备被并发访问
- 如果锁定失败,跳转到清理代码
初始化设备属性
rdev->desc_nr = -1;
rdev->faulty = 0;
rdev->in_sync = 0;
rdev->data_offset = 0;
atomic_set(&rdev->nr_pending, 0);
desc_nr = -1:描述符编号,-1表示未分配faulty = 0:设备正常(非故障状态)in_sync = 0:数据不同步(需要同步)data_offset = 0:数据偏移量为0atomic_set(&rdev->nr_pending, 0):挂起IO计数初始化为0
检查设备大小
size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
if (!size) {
printk(KERN_WARNING
"md: %s has zero or unknown size, marking faulty!\n",
bdevname(rdev->bdev,b));
err = -EINVAL;
goto abort_free;
}
rdev->bdev->bd_inode->i_size:获取块设备的inode大小(字节)>> BLOCK_SIZE_BITS:右移转换为扇区数(通常512字节/扇区)- 如果大小为0,打印警告,标记为无效设备
- 设置错误码
-EINVAL并跳转到清理
读取超级块(如果指定了格式)
if (super_format >= 0) {
err = super_types[super_format].
load_super(rdev, NULL, super_minor);
- 如果
super_format >= 0,表示指定了具体的超级块格式 super_types[super_format].load_super:调用特定格式的超级块加载函数- 参数:设备指针、NULL、次版本号
if (err == -EINVAL) {
printk(KERN_WARNING
"md: %s has invalid sb, not importing!\n",
bdevname(rdev->bdev,b));
goto abort_free;
}
- 如果返回
-EINVAL,表示超级块无效 - 打印警告信息并跳转到清理
if (err < 0) {
printk(KERN_WARNING
"md: could not read %s's sb, not importing!\n",
bdevname(rdev->bdev,b));
goto abort_free;
}
}
- 如果返回其他错误,表示读取超级块失败
- 打印警告信息并跳转到清理
初始化链表并返回
INIT_LIST_HEAD(&rdev->same_set);
return rdev;
INIT_LIST_HEAD(&rdev->same_set):初始化设备链表头same_set:用于将设备链接到同一个RAID集合中- 返回成功创建的
rdev指针
错误处理清理代码
abort_free:
if (rdev->sb_page) {
if (rdev->bdev)
unlock_rdev(rdev);
free_disk_sb(rdev);
}
kfree(rdev);
return ERR_PTR(err);
}
abort_free:错误处理标签- 如果分配了超级块页面:
- 如果打开了块设备,解锁设备
- 释放超级块内存
kfree(rdev):释放设备结构内存ERR_PTR(err):返回错误码指针
函数功能详解
主要功能:导入一个块设备到MD(软件RAID)系统中
详细执行流程:
-
内存分配阶段
- 分配RAID设备描述符结构体
- 分配超级块内存页面
- 初始化所有字段为默认值
-
设备访问阶段
- 锁定设备防止并发访问
- 打开块设备文件
- 验证设备大小有效性
-
超级块处理阶段
- 如果指定了超级块格式,读取并验证超级块
- 支持多种RAID元数据格式
- 验证超级块的完整性和有效性
-
初始化完成阶段
- 初始化设备链表节点
- 返回可用的设备描述符
自动检测、组装并启动RAID阵列autorun_devices
static void autorun_devices(int part)
{
struct list_head candidates;
struct list_head *tmp;
mdk_rdev_t *rdev0, *rdev;
mddev_t *mddev;
char b[BDEVNAME_SIZE];
printk(KERN_INFO "md: autorun ...\n");
while (!list_empty(&pending_raid_disks)) {
dev_t dev;
rdev0 = list_entry(pending_raid_disks.next,
mdk_rdev_t, same_set);
printk(KERN_INFO "md: considering %s ...\n",
bdevname(rdev0->bdev,b));
INIT_LIST_HEAD(&candidates);
ITERATE_RDEV_PENDING(rdev,tmp)
if (super_90_load(rdev, rdev0, 0) >= 0) {
printk(KERN_INFO "md: adding %s ...\n",
bdevname(rdev->bdev,b));
list_move(&rdev->same_set, &candidates);
}
/*
* now we have a set of devices, with all of them having
* mostly sane superblocks. It's time to allocate the
* mddev.
*/
if (rdev0->preferred_minor < 0 || rdev0->preferred_minor >= MAX_MD_DEVS) {
printk(KERN_INFO "md: unit number in %s is bad: %d\n",
bdevname(rdev0->bdev, b), rdev0->preferred_minor);
break;
}
if (part)
dev = MKDEV(mdp_major,
rdev0->preferred_minor << MdpMinorShift);
else
dev = MKDEV(MD_MAJOR, rdev0->preferred_minor);
md_probe(dev, NULL, NULL);
mddev = mddev_find(dev);
if (!mddev) {
printk(KERN_ERR
"md: cannot allocate memory for md drive.\n");
break;
}
if (mddev_lock(mddev))
printk(KERN_WARNING "md: %s locked, cannot run\n",
mdname(mddev));
else if (mddev->raid_disks || mddev->major_version
|| !list_empty(&mddev->disks)) {
printk(KERN_WARNING
"md: %s already running, cannot run %s\n",
mdname(mddev), bdevname(rdev0->bdev,b));
mddev_unlock(mddev);
} else {
printk(KERN_INFO "md: created %s\n", mdname(mddev));
ITERATE_RDEV_GENERIC(candidates,rdev,tmp) {
list_del_init(&rdev->same_set);
if (bind_rdev_to_array(rdev, mddev))
export_rdev(rdev);
}
autorun_array(mddev);
mddev_unlock(mddev);
}
/* on success, candidates will be empty, on error
* it won't...
*/
ITERATE_RDEV_GENERIC(candidates,rdev,tmp)
export_rdev(rdev);
mddev_put(mddev);
}
printk(KERN_INFO "md: ... autorun DONE.\n");
}
函数声明和变量定义
static void autorun_devices(int part)
{
struct list_head candidates;
struct list_head *tmp;
mdk_rdev_t *rdev0, *rdev;
mddev_t *mddev;
char b[BDEVNAME_SIZE];
static void:静态函数,无返回值int part:分区标志,控制设备号生成方式candidates:候选设备链表头tmp:链表遍历临时指针rdev0:参考设备(第一个设备)rdev:遍历用的设备指针mddev:MD设备结构指针b[BDEVNAME_SIZE]:设备名缓冲区
开始自动运行
printk(KERN_INFO "md: autorun ...\n");
while (!list_empty(&pending_raid_disks)) {
- 打印开始自动运行的信息
while (!list_empty(&pending_raid_disks)):循环处理所有待处理的RAID磁盘pending_raid_disks:全局链表,包含所有待处理的设备
获取参考设备
dev_t dev;
rdev0 = list_entry(pending_raid_disks.next,
mdk_rdev_t, same_set);
printk(KERN_INFO "md: considering %s ...\n",
bdevname(rdev0->bdev,b));
list_entry(pending_raid_disks.next, mdk_rdev_t, same_set):从链表获取第一个设备list_entry宏:通过链表节点指针获取包含它的结构体指针- 打印正在考虑的设备名称
初始化候选设备列表
INIT_LIST_HEAD(&candidates);
INIT_LIST_HEAD(&candidates):初始化候选设备链表- 这个链表将存储属于同一个RAID集合的所有设备
查找兼容设备
ITERATE_RDEV_PENDING(rdev,tmp)
if (super_90_load(rdev, rdev0, 0) >= 0) {
printk(KERN_INFO "md: adding %s ...\n",
bdevname(rdev->bdev,b));
list_move(&rdev->same_set, &candidates);
}
ITERATE_RDEV_PENDING(rdev,tmp):宏,遍历所有待处理设备super_90_load(rdev, rdev0, 0):检查设备是否使用版本0.90超级块格式且与rdev0兼容- 如果兼容,打印添加信息并将设备移动到候选列表
list_move(&rdev->same_set, &candidates):从待处理列表移动到候选列表
检查设备号有效性
if (rdev0->preferred_minor < 0 || rdev0->preferred_minor >= MAX_MD_DEVS) {
printk(KERN_INFO "md: unit number in %s is bad: %d\n",
bdevname(rdev0->bdev, b), rdev0->preferred_minor);
break;
}
- 检查首选次设备号是否在有效范围内
rdev0->preferred_minor:从超级块中读取的期望次设备号- 如果无效,打印错误信息并跳出循环
创建设备号
if (part)
dev = MKDEV(mdp_major,
rdev0->preferred_minor << MdpMinorShift);
else
dev = MKDEV(MD_MAJOR, rdev0->preferred_minor);
part参数决定设备号生成方式:- 如果为真:使用分区设备号
mdp_major,次设备号需要移位 - 如果为假:使用标准MD设备号
MD_MAJOR
- 如果为真:使用分区设备号
MKDEV宏:将主次设备号组合成dev_t
探测和查找MD设备
md_probe(dev, NULL, NULL);
mddev = mddev_find(dev);
if (!mddev) {
printk(KERN_ERR
"md: cannot allocate memory for md drive.\n");
break;
}
md_probe(dev, NULL, NULL):探测并创建设备mddev_find(dev):查找对应的MD设备结构- 如果找不到,打印内存分配错误并跳出循环
检查设备状态
if (mddev_lock(mddev))
printk(KERN_WARNING "md: %s locked, cannot run\n",
mdname(mddev));
else if (mddev->raid_disks || mddev->major_version
|| !list_empty(&mddev->disks)) {
printk(KERN_WARNING
"md: %s already running, cannot run %s\n",
mdname(mddev), bdevname(rdev0->bdev,b));
mddev_unlock(mddev);
}
mddev_lock(mddev):尝试锁定MD设备- 如果锁定失败,设备被其他进程使用
- 检查设备是否已配置:有磁盘数、版本号或非空磁盘列表
- 如果设备已在运行,打印警告并解锁
组装和启动阵列
else {
printk(KERN_INFO "md: created %s\n", mdname(mddev));
ITERATE_RDEV_GENERIC(candidates,rdev,tmp) {
list_del_init(&rdev->same_set);
if (bind_rdev_to_array(rdev, mddev))
export_rdev(rdev);
}
autorun_array(mddev);
mddev_unlock(mddev);
}
- 打印成功创建MD设备的信息
ITERATE_RDEV_GENERIC(candidates,rdev,tmp):遍历所有候选设备list_del_init(&rdev->same_set):从候选列表移除设备bind_rdev_to_array(rdev, mddev):将设备绑定到MD阵列- 如果绑定失败,
export_rdev(rdev)导出设备(清理) autorun_array(mddev):自动启动阵列mddev_unlock(mddev):解锁设备
清理剩余候选设备
ITERATE_RDEV_GENERIC(candidates,rdev,tmp)
export_rdev(rdev);
mddev_put(mddev);
- 再次遍历候选列表,清理任何剩余设备
export_rdev(rdev):导出并释放设备资源mddev_put(mddev):释放MD设备引用计数
完成自动运行
}
printk(KERN_INFO "md: ... autorun DONE.\n");
}
- 循环结束(所有待处理设备处理完毕)
- 打印自动运行完成信息
函数功能详解
主要功能:自动检测、组装并启动RAID阵列
详细执行流程:
-
初始化阶段
- 遍历所有待处理的RAID磁盘
- 以第一个设备为参考点
-
设备分组阶段
- 根据超级块信息将兼容设备分组
- 使用版本0.90超级块格式进行兼容性检查
- 创建候选设备列表
-
设备创建阶段
- 验证设备号有效性
- 根据分区标志创建设备号
- 探测并分配MD设备结构
-
状态验证阶段
- 检查设备是否已被锁定
- 验证设备是否已在运行
- 确保设备处于可配置状态
-
阵列组装阶段
- 将候选设备绑定到MD阵列
- 自动启动阵列运行
- 处理绑定失败的情况
-
清理阶段
- 清理剩余的候选设备
- 释放MD设备引用计数
- 继续处理下一个设备组
关键特性:
- 自动检测:根据超级块信息自动识别RAID阵列
- 设备分组:将属于同一阵列的设备分组处理
更多推荐

所有评论(0)