linux-raid (一) md

源文件
        linux/include/uapi/linux/raid/md_p.h
        linux/include/uapi/linux/raid/md_u.h
        linux/drivers/md/md.h
        linux/drivers/md/md.c

md 是 multiple devices 的缩写,实现了 MD RAID Framework。它将 RAID drivers 跟上层的 block layer 连接在一起,这样可以通过 block layer 访问到底层的 RAID drivers,比如 RAID1/RAID5 driver。同时它作为一个 RAID driver 之上的抽象层,也完成了大量的工作,比如对 superblock 的操作;当然,比较细节的工作还是需要由 RAID driver 自己来完成,毕竟不同 RAID driver 有自己的 RAID 算法需要实现。

md 对上层的接口定义在 md_fops 中:

static const struct block_device_operations md_fops =
{
    .owner                  = THIS_MODULE,
    .open                    = md_open,
    .release                = md_release,
    .ioctl                    = md_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl       = md_compat_ioctl,
#endif
    .getgeo                = md_getgeo,
    .media_changed  = md_media_changed,
    .revalidate_disk   = md_revalidate,
};

struct mddev 表示阵列设备,struct md_rdev 表示阵列中的单个磁盘设备。md 通过结构 struct md_personality 定义了底层 RAID drivers 的接口,RAID driver 模块只需要将自己的 md_pers 注册到 pers_list 列表中,上层通过 md 就可以驱动底层 RAID driver 的具体操作。md 中的大量代码也是操作这 3 个核心数据结构,还有支持大量 sysfs 系统文件的 show/store 函数。

所有的 active md 阵列链接到 all_mddevs 列表中。

关于 superblock

md 有自己的 superblock,存放关于磁盘阵列的元数据信息,比如 RAID level。不同版本的 superblock 有不同的格式,在磁盘上有不同的存放位置(https://raid.wiki.kernel.org/index.php/RAID_superblock_formats 和 linux/include/uapi/linux/raid/md_p.h)。缺省版本为 1.2

superblock 最初是由用户态工具写入的 mdadm。在内核态,md 可以对 superblock 进行读/更新操作。为了支持不同版本的 superblock,md 定义了 super_type 接口:

 978struct super_type  {
 979        char                *name;
 980        struct module       *owner;
 981        int                 (*load_super)(struct md_rdev *rdev,
 982                                          struct md_rdev *refdev,
 983                                          int minor_version);    
 984        int                 (*validate_super)(struct mddev *mddev,
 985                                              struct md_rdev *rdev);
 986        void                (*sync_super)(struct mddev *mddev,
 987                                          struct md_rdev *rdev);
 988        unsigned long long  (*rdev_size_change)(struct md_rdev *rdev,
 989                                                sector_t num_sectors);
 990        int                 (*allow_new_offset)(struct md_rdev *rdev,
 991                                                unsigned long long new_offset);
 992};

 956 *   int load_super(struct md_rdev *dev, struct md_rdev *refdev, int minor_version)
 957 *          loads and validates a superblock on dev.
 958 *          if refdev != NULL, compare superblocks on both devices
 959 *        Return:
 960 *              0 - dev has a superblock that is compatible with refdev
 961 *              1 - dev has a superblock that is compatible and newer than refdev
 962 *                  so dev should be used as the refdev in future
 963 *             -EINVAL superblock incompatible or invalid
 964 *             -othererror e.g. -EIO
 965 *
 966 *   int validate_super(struct mddev *mddev, struct md_rdev *dev)
 967 *          Verify that dev is acceptable into mddev.
 968 *           The first time, mddev->raid_disks will be 0, and data from
 969 *           dev should be merged in.  Subsequent calls check that dev
 970 *           is new enough.  Return 0 or -EINVAL
 971 *
 972 *   void sync_super(struct mddev *mddev, struct md_rdev *dev)
 973 *         Update the superblock for rdev with data in mddev
 974 *         This does not write to disc.

当前实现了 2 个接口,以支持 2 类版本的 superblock:

static struct super_type super_types[] = {
    [0] = {
        .name            = "0.90.0",
        .owner            = THIS_MODULE,
        .load_super        = super_90_load,
        .validate_super        = super_90_validate,
        .sync_super        = super_90_sync,
        .rdev_size_change   = super_90_rdev_size_change,
        .allow_new_offset   = super_90_allow_new_offset,
    },
    [1] = {
        .name            = "md-1",            支持 1.0/1.1/1.2 版本
        .owner            = THIS_MODULE,
        .load_super        = super_1_load,
        .validate_super        = super_1_validate,
        .sync_super        = super_1_sync,
        .rdev_size_change   = super_1_rdev_size_change,
        .allow_new_offset   = super_1_allow_new_offset,
    },
};

从 super_90_load/super_1_load 可以看到不同版本的 superblock,其存放位置、占用空间大小都是不一样的。

md 相关的系统文件

static const struct file_operations md_seq_fops     ---    /proc/mdstat    
static struct attribute *md_default_attrs[]                    /sys/block/mdX/md/
static struct attribute *rdev_default_attrs[]                  /sys/block/mdX/md/[disk]/

/proc/sys/dev/raid/speed_limit_min                 # sysctl 变量 static int sysctl_speed_limit_min = 1000
/proc/sys/dev/raid/speed_limit_max                # sysctl 变量 static int sysctl_speed_limit_max = 200000
   
/sys/block/mdX/md/sync_speed_{min,max} # mddev->sync_speed_min 和 mddev->sync_speed_max

事件计数
static atomic_t md_event_count

* We have a system wide 'event count' that is incremented
 * on any 'interesting' event, and readers of /proc/mdstat
 * can use 'poll' or 'select' to find out when the event
 * count increases.
 *
 * Events are:
 *      start array, stop array, error, add device, remove device,
 *      start build, activate spare


ioctl

md 支持的 ioctl 命令在 linux/include/uapi/linux/raid/md_u.h 中定义:

  38/* status */
  39#define RAID_VERSION            _IOR (MD_MAJOR, 0x10, mdu_version_t)
  40#define GET_ARRAY_INFO          _IOR (MD_MAJOR, 0x11, mdu_array_info_t)
  41#define GET_DISK_INFO           _IOR (MD_MAJOR, 0x12, mdu_disk_info_t)
  42#define PRINT_RAID_DEBUG        _IO (MD_MAJOR, 0x13)
  43#define RAID_AUTORUN            _IO (MD_MAJOR, 0x14)
  44#define GET_BITMAP_FILE         _IOR (MD_MAJOR, 0x15, mdu_bitmap_file_t)
  45
  46/* configuration */
  47#define CLEAR_ARRAY             _IO (MD_MAJOR, 0x20)
  48#define ADD_NEW_DISK            _IOW (MD_MAJOR, 0x21, mdu_disk_info_t)
  49#define HOT_REMOVE_DISK         _IO (MD_MAJOR, 0x22)
  50#define SET_ARRAY_INFO          _IOW (MD_MAJOR, 0x23, mdu_array_info_t)
  51#define SET_DISK_INFO           _IO (MD_MAJOR, 0x24)
  52#define WRITE_RAID_INFO         _IO (MD_MAJOR, 0x25)
  53#define UNPROTECT_ARRAY         _IO (MD_MAJOR, 0x26)
  54#define PROTECT_ARRAY           _IO (MD_MAJOR, 0x27)
  55#define HOT_ADD_DISK            _IO (MD_MAJOR, 0x28)
  56#define SET_DISK_FAULTY         _IO (MD_MAJOR, 0x29)
  57#define HOT_GENERATE_ERROR      _IO (MD_MAJOR, 0x2a)
  58#define SET_BITMAP_FILE         _IOW (MD_MAJOR, 0x2b, int)
  59
  60/* usage */
  61#define RUN_ARRAY               _IOW (MD_MAJOR, 0x30, mdu_param_t)
  62/*  0x31 was START_ARRAY  */
  63#define STOP_ARRAY              _IO (MD_MAJOR, 0x32)
  64#define STOP_ARRAY_RO           _IO (MD_MAJOR, 0x33)
  65#define RESTART_ARRAY_RW        _IO (MD_MAJOR, 0x34)


md 初始化流程

    * 编译进内核:autostart_arrays 搜索 rdev "Autodetecting RAID arrays" -> autorun_devices 根据 rdev 列表构建 mddev 结构 -> autorun_array 启动 mddev 阵列 -> ... 运行 ...
    * 编译成模块:md_open -> static const struct block_device_operations md_fops -> ... 运行 ...

md 内核线程

md 一些工作是由内核线程完成的,比如 ->thread/->sync_thread,->sync_thread 通常运行的是函数 md_do_sync,而 ->thread 多运行 RAID mode 自己提供的函数,比如 RAID1 提供了 raid1d,RAID5 提供了 raid5d,RAID10 提供了 raid10d。

小结

md 只是扮演了一个 framework 的角色,通过桥梁作用,让底层的 RAID mode 模块向上层提供 RAID 算法。要更好的理解 md,需要结合具体的 RAID 算法来看。


* 尚未弄明白的问题

        * mdadm 写 superblock 时是对所有 real device 都写吗?还是不同 RAID mode 有不同的副本管理?
        * 内核 md 只是实现了比较基础的功能,而 mdadm 在用户态实现了大量的逻辑,以支持复杂的操作
                  需要好好看看 mdadm 实现 :-)
        * bitmap 在 md 中起什么作用? [linear/raid0 不使用 bitmap]
        * superblock 在 md 中就可以加载/更新,不需要各 RAID mode 提供专门的处理函数
        * 不同 RAID mode 在 reshape 时会怎么运作?
        * 什么条件下做 resync?有没有内核线程来自动做 resync,运行频率是怎样的?
        * reconstruction 什么时候做,跟 resync 的区别在哪里?有没有专门的线程来做?
                如果有磁盘故障,则会对第 1 个 spare disk 做 reconstruction 操作
        * md 中有多少内核线程,分别做什么?
        * 不同 RAID mode 对 badblocks 是如何检测、处理的?

   * takeove 什么时候发生?具体会有哪些改变?

posted on 2012-12-27 23:32  refrag  阅读(6266)  评论(1编辑  收藏  举报

导航