Chinaunix首页 | 论坛 | 博客
  • 博客访问: 164323
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 342
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-19 11:38
个人简介

A ZFS fan

文章分类
文章存档

2014年(17)

分类: 服务器与存储

2014-04-11 21:25:14

关于Space Map以及块分配策略的,请参见SpaceMap:http://blog.chinaunix.net/uid-24395800-id-4206221.html

                                                            Block Allocation:http://blog.chinaunix.net/uid-24395800-id-4206309.html

ZFS源码中,metaslab与space map的实现主要包含在以下5个文件中:

  • space_map.h
  • space_map.c
  • metaslab.h
  • metaslab_impl.h
  • metaslab.c

 

metaslab内存中的表示

按照Space Map的设计原理,每个虚拟设备都被分成几百个metaslab,每个metaslab对应一个Space Map。目前的代码中,每个虚拟设备中metaslab的个数大约是200个,这个在vdev.c的vdev_metaslab_set_size函数中有所体现。

 

metaslab结构体定义如下:

  1. struct metaslab {
  2.     kmutex_t ms_lock;                           /* metaslab lock */
  3.     space_map_obj_t ms_smo;                     /* synced space map object */
  4.     space_map_obj_t ms_smo_syncing;             /* syncing space map object */
  5.     space_map_t *ms_allocmap[TXG_SIZE];         /* allocated this txg */
  6.     space_map_t *ms_freemap[TXG_SIZE];          /* freed this txg */
  7.     space_map_t *ms_defermap[TXG_DEFER_SIZE];   /* deferred frees */
  8.     space_map_t *ms_map;                        /* in-core free space map */
  9.     int64_t ms_deferspace;                      /* sum of ms_defermap[] space */
  10.     uint64_t ms_weight;                         /* weight vs. others in group */
  11.     metaslab_group_t *ms_group;                 /* metaslab group */
  12.     avl_node_t ms_group_node;                   /* node in metaslab group tree */
  13.     txg_node_t ms_txg_node;                     /* per-txg dirty metaslab links */
  14. };

我们可以看到,metaslab结构体中有4个space_map_t,在初始化过程中,ZFS从磁盘上将space map对象读取出来,首先存放到ms_map中。在使用过程中,如果分配的block,那么就将对应的空间从ms_map中移除,添加到ms_allocmap中,如果有block被释放,则将它移到ms_freemap中,ms_defermap用于延迟释放。

ms_group_node用于将一个磁盘上的metaslab构成一棵AVL树。

 

定义了metaslab之后,ZFS为每个vdev维护一个metaslab_group结构体,详细定义如下:

  1. struct metaslab_group {
  2.     kmutex_t mg_lock;
  3.     avl_tree_t mg_metaslab_tree;
  4.     uint64_t mg_aliquot;
  5.     uint64_t mg_bonus_area;
  6.     uint64_t mg_alloc_failures;
  7.     boolean_t mg_allocatable; /* 是否可以被分配 */
  8.     uint64_t mg_free_capacity; /* 空闲空间百分比 */
  9.     int64_t mg_bias;
  10.     int64_t mg_activation_count;
  11.     metaslab_class_t *mg_class;
  12.     vdev_t *mg_vd;
  13.     metaslab_group_t *mg_prev;
  14.     metaslab_group_t *mg_next;
  15. };

这个结构体可以访问到当前磁盘上的所有metaslab(存储在mg_metaslab_tree中),包括:是否可被分配,使用空间百分比,磁盘选择时的轮转粒子等等。同时与当前pool中的其他磁盘相联系(mg_prev, mg_next)

 

最后,每个ZFS的每个存储池(zpool)都维护了一个metaslab_class(其实有两个,另一个用于ZIL,以后会详细说明)。metaslab_class结构体定义如下:

  1. struct metaslab_class {
  2.     spa_t *mc_spa;
  3.     metaslab_group_t*mc_rotor;
  4.     space_map_ops_t *mc_ops;
  5.     uint64_t mc_aliquot;
  6.     uint64_t mc_alloc_groups;/* 可以分配的metaslab_group的数目 */
  7.     uint64_t mc_alloc; /* 总共分配出去的空间 */
  8.     uint64_t mc_deferred; /* 总延迟释放的空间 */
  9.     uint64_t mc_space; /* total space (alloc + free) */
  10.     uint64_t mc_dspace; /* total deflated space */
  11. };

Space Map内存中的表示

  1. typedef struct space_map {
  2.     avl_tree_t sm_root; /* offset-ordered segment AVL tree */
  3.     uint64_t sm_space; /* sum of all segments in the map */
  4.     uint64_t sm_start; /* start of map */
  5.     uint64_t sm_size; /* size of map */
  6.     uint8_t sm_shift; /* unit shift */
  7.     uint8_t sm_loaded; /* map loaded? */
  8.     uint8_t sm_loading; /* map loading? */
  9.     uint8_t sm_condensing; /* map condensing? */
  10.     kcondvar_t sm_load_cv; /* map load completion */
  11.     space_map_ops_t *sm_ops; /* space map block picker ops vector */
  12.     avl_tree_t *sm_pp_root; /* size-ordered, picker-private tree */
  13.     void *sm_ppd; /* picker-private data */
  14.     kmutex_t *sm_lock; /* pointer to lock that protects map */
  15. } space_map_t;

这里面最重要的就是sm_root,维护了一棵AVL树,里面的节点就是空间段,定义如下:

  1. typedef struct space_seg {
  2.     avl_node_t ss_node; /* AVL node */
  3.     avl_node_t ss_pp_node; /* AVL picker-private node */
  4.     uint64_t ss_start; /* starting offset of this segment */
  5.     uint64_t ss_end; /* ending offset (non-inclusive) */
  6. } space_seg_t;


space_map_t中有两棵AVL树,他们分别对应space_seg中的ss_node和ss_pp_node。 sm_root是按照offset排序,sm_pp_root是按照块大小排序。一般情况下,space_map_t使用sm_root来进行分配、释放等操作,当metaslab中剩余空间小到一定程度时,ZFS分配空间将会切换分配算法,使用best-fit算法来分配空间,这是就要使用sm_pp_root。

Block Allocation

选择vdev

在分配block,按照分配策略,首先要选择一个虚拟设备(vdev),也就是说要从给定的metaslab_class_t(以下称mc)中选出一个metaslab_group(以下称mg)来。

选择mg的过程中,我们从mc的rotor开始,循环查找所有的mg,直到找到所需的。在这个过程中,不需要锁定mc_rotor或者mc_aliquot,以为就算我们错过一些更新也是没关系的,随着时间的推移,最终会达到平衡状态。

选择磁盘以及分配block的主要流程如以下流程图所示:

说明:流程中有个步骤是设置mg->mg_bias,这里主要用来在分配过程中平衡磁盘的使用率。比如一个设备的空间已经使用了80%,而整个pool才使用20%,那么这个磁盘在分配过程中就要少分配60%,mg_bias = (20-80) * 512K / 100 = -307K,也就是说这次迭代过程中,要少分配307K的空间。(512K是mg->mg_aliquot,即磁盘选择时的轮转因子)


从虚拟设备中分配metaslab

涉及函数为:

static uint64_t metaslab_group_alloc(metaslab_group_t *mg, uint64_t psize, uint64_t asize,

uint64_t txg, uint64_t min_distance, dva_t *dva, int d, int flags)

选择过程其实很简单,在给定的metaslab_group中找出距离给定的所有DVA(最多3个)最近,且权重最高的metaslab,并分配空间(因为离给定的DVA近,需要的寻址时间小,速度就快)。但是如果两者不属于同一个vdev,则不需要考虑距离因素。

选择完成之后,还要进行一系列的判断:

  • 被选择的metaslab是否已经达到允许分配失败的最高次数,如果达到了,则需要考虑跳过这个metaslab(当然,还有其他因素需要考虑)。
  • 被选择的metaslab权重是否在此过程中被其它线程修改过,如果修改过导致不符合条件,则需要重新选择。
  • 如果激活失败,需要重新选择
  • 如果选出的metaslab正在被压缩,需要重新选择

检查完毕,都符合条件的情况下,从选择出来的metaslab对应的space_map中分配空间。

 

metaslab权重计算

metaslab权重通过 static uint64_t metaslab_weight(metaslab_t *msp) 函数实现,其实现流程图如下:

块分配策略

First-fit块分配策略

从space map中,使用sm_root(按偏移排序)对应的AVL树,从中选择一个适合大小的块,分配空间。这是ZFS的默认分配策略。


动态分配策略

默认情况下,ZFS是用first-fit分配策略,但是当磁盘空间使用率到达一定程度(空闲空间变小)时,将转换使用Best Fit分配策略。具体转换条件由metaslab_df_alloc_threshold和metaslab_df_free_pct。

本文乃nnusun原创文章,请勿转载。如须转载请详细标明转载出处。

阅读(5008) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~