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:设备文件的inode
  • file:设备文件结构
  • cmd:ioctl命令
  • arg:用户空间参数指针
  • err:错误返回值,初始为0
  • argp:转换为用户空间指针的参数
  • 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_DISKSTOP_ARRAYRUN_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_unlockabort_unlock:释放MD设备锁
  • mddev_unlock(mddev):释放互斥锁
  • done:如果仍有错误,触发MD模块的BUG检查
  • abort:直接返回错误

函数功能详解

主要功能:Linux软件RAID(MD)设备的管理接口

支持的操作为三大类

  1. 全局RAID操作

    • 获取驱动版本信息
    • 打印调试信息
    • 自动启动阵列
  2. 阵列配置操作

    • 设置阵列信息
    • 启动/停止阵列
    • 添加/移除磁盘
    • 设置磁盘状态
  3. 查询操作

    • 获取阵列信息
    • 获取磁盘信息
    • 获取虚拟几何参数

安全特性

  • 需要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阵列

详细执行流程

  1. 初始化阶段

    • 打印开始检测的日志信息
    • 准备遍历所有预先检测到的块设备
  2. 设备导入阶段

    • 遍历 detected_devices 数组中的每个设备
    • 调用 md_import_device 创建设备描述符
    • 检查设备是否可用(非故障状态)
  3. 设备收集阶段

    • 将所有可用的RAID设备添加到待处理列表
    • 使用链表管理这些设备
    • 重置全局计数器防止重复处理
  4. 自动启动阶段

    • 调用 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:数据偏移量为0
  • atomic_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)系统中

详细执行流程

  1. 内存分配阶段

    • 分配RAID设备描述符结构体
    • 分配超级块内存页面
    • 初始化所有字段为默认值
  2. 设备访问阶段

    • 锁定设备防止并发访问
    • 打开块设备文件
    • 验证设备大小有效性
  3. 超级块处理阶段

    • 如果指定了超级块格式,读取并验证超级块
    • 支持多种RAID元数据格式
    • 验证超级块的完整性和有效性
  4. 初始化完成阶段

    • 初始化设备链表节点
    • 返回可用的设备描述符

自动检测、组装并启动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阵列

详细执行流程

  1. 初始化阶段

    • 遍历所有待处理的RAID磁盘
    • 以第一个设备为参考点
  2. 设备分组阶段

    • 根据超级块信息将兼容设备分组
    • 使用版本0.90超级块格式进行兼容性检查
    • 创建候选设备列表
  3. 设备创建阶段

    • 验证设备号有效性
    • 根据分区标志创建设备号
    • 探测并分配MD设备结构
  4. 状态验证阶段

    • 检查设备是否已被锁定
    • 验证设备是否已在运行
    • 确保设备处于可配置状态
  5. 阵列组装阶段

    • 将候选设备绑定到MD阵列
    • 自动启动阵列运行
    • 处理绑定失败的情况
  6. 清理阶段

    • 清理剩余的候选设备
    • 释放MD设备引用计数
    • 继续处理下一个设备组

关键特性

  • 自动检测:根据超级块信息自动识别RAID阵列
  • 设备分组:将属于同一阵列的设备分组处理
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐