手稿核心章节技术架构 - md5 位图机制

本文深入探讨了MD设备中Bitmap的实现原理与工作机制,详细解释了核心数据结构、位图结构及其状态变迁等内容。

1   Bitmap实现

1.1 bitmap原理

简单的说,bitmap的工作原理就是使用一个位图,每个位对应md设备的一个数据块,在md设备有数据写入时,设置该数据块对应的位,等待写入完成后,再清除该数据块对应的位。

当需要进行数据同步/恢复时,参照bitmap的位图,只有设置了位对应的数据块才需要进行同步/恢复操作。

1.2 核心数据结构

1.2.1 Bitmap_info结构

此结构定义在mddev结构中,其核心部分为:

核心成员

描述

struct file *file

描述bitmap file,当指定为md自身时,为NULL

loff_t offset

可以为负,不能为0,描述bitmap文件存放在超级块之前还是之后,为负,就在超级块前

struct default_offset

当md设备创建后,再指定bitmapfile时会使用

unsigned long chunksize

存放bitmap chunksize

int external

有cli传入,这里传入internal时,此值为0

 

1.2.2 Bitmap_page结构

核心成员

描述

void *map

指向一个page的指针起始位置,一个page中每2B的大小保存一个16位的bimtap_counter描述一个chunk的状态

unsigned in hijacked:1

当map申请失败时会被使用

unsigned int count:31

描述在这个page中有dirty位的bit个数

 

1.2.3 Bitmap结构

以下列出bitmap结构的核心部分:

核心成员

描述

struct bitmap_page *bp

申请为一个数组,每个元素对应一个bitmap内存页

unsigned long pages

描述bp数组元素的个数

unsigned long chunkshift

通过bitmap-chunk计算出来的shift值

unsigned long chunks

底层磁盘大小计算出来的chunk个数

spinlock_t lock

Bitmap的自旋锁,保护filemap 的page

struct page *sb_page

存放md的bitmap的superblock的信息

struct page **filemap

描述整个bitmap file的信息,以页的形式保存

unsigned long *filemap_attr

每4bit对应filemap的一页

unsigned long file_pages

描述bitmap file在filemap中用了多少个page

unsigned long flags

描述bitmap的状态

   

1.2.4 Bitmap_super_t结构

此结构描述超级块的信息,详细信息如下:

成员

描述

__le32 magic

0 - 4  宏BITMAP_MAGIC,定义成0x6d746962

__le32 version

4 - 8 the bitmap major for now, could change...

__u8   uuid[16]

8 - 24 128 bit uuid - must match md device uuid

__le64 events

24 - 32 event counter for the bitmap (1)

__le64 events_cleared

32 - 40 event counter when last bit cleared (2)

__le64 sync_size

40 - 48 the size of the md device's sync range(3)

__le32 state

48 - 52 bitmap state information

__le32 chunksize

52 - 56 the bitmap chunk size in bytes

__le32 daemon_sleep

56 - 60 seconds between disk flushes

__le32 write_behind

60 - 64 number of outstanding write-behind writes

__le8  pad[256 - 64]

set to zero

 

以上值在创建md设备时,由cli部分进行赋值。在kernel中,是直接通过读取bitmap file对应位置的信息保存在sb_page中,然后通过内存映射,将这些值读出来的。

值得关注的是,event记录md设备的最后一个事件,这个事件被记录在md的超级块的events和mddev结构中events中,在此磁盘加载时,可以比较这两个结构中的events确定bitmap文件是否已经过期。

1.3 Bitmap文件

Bitmap磁盘文件可以存在在md设备之外,单独指定一个磁盘/文件,此时mddev结构中的bitmap_file表示这个bitmap文件。

Bitmap磁盘文件也可以存放在md设备自身,bitmap文件相对于md的超级块的位置由bitmap_offset指定,当这个值为负值时,表示bitmap文件存放在超级块之前。这里假设bitmap_offset大于0,则表示bitmap磁盘文件在超级块之后。

Bitmap文件状态如下,记录在bitmap->flags和bitmap->sb->state中:

1.  BITMAP_STALE  = 0x002:此状态被设置,表示bitmap file已经过期;

2.  BITMAP_WRITE_ERROR = 0x004:此状态在发生写错误时,被设置;

3.  BITMAP_HOSTENDIAN = 0x8000:

当bitmap->sb.version == BITMAP_MAJOR_HOSTENDIAN(3)时被设置

下面以bitmap文件存放在md设备自身为例,此时bitmap_file=NULL。

磁盘文件由超级块和位图组成,超级块大小为256(见前面结构bitmap_super_t),随后紧跟位图。位图中,一个位对应md设备中一段条带段,这个大小由超级块sb->chunksize字段确定。位被设置,表示对应的chunk需要同步;位被清除,表示对应chunk状态一致,无需同步。

1.4 Bitmap位图结构

1.4.1 位图结构说明

Bitmap中共有以下两个位图结构:

1.  磁盘位图,md中一级位图,保存在bitmap->filemap数组中,而bitmap->filemap_attr描述每个bitmap->filemap数组元素的状态;

说明:在md设备创建时,整个bitmap磁盘文件会被映射到内存,保存在filemap中,描述底层磁盘大小的位图,在这里1bit对应底层磁盘一个chunk(计算公式后面会提到);其中filemap_attr数组每4bit对应filemap的一页(最小为sizeof(unsigned long))。Bitmap->filemap[0]被保存在bitmap->sb_page中,描述超级块信息。

2.  内存位图,md中二级位图,保存在bitmap->bp数组中,每一项数组元素中的map指针指向一个页,该页保存bitmap的一个内存位图结构(1个page描述2048个chunk);

说明:每个bp数组所描述的一个页(4096B)代表的2048个chunk,均有以下三个状态(状态占用2B 16位),状态保存在bitmap_count_t *bmc中:

a)  NEEDED_MASK:1<<(COUNTER_BITS-1)

b)  RESYNC_MASK:1<<(COUNTER_BITS-2)

c)  COUNTER_MAX:RESYNC_MASK-1

注:COUNTER_BITS 16

1.4.2 位图示例图

1.4.2.1    内存位图

如图:

bitmap->bp内存位图

这里以bitmap_chunksize=512K,mddev->chunk=64K=128sector为例,当前mddev->dev_sector=10485760-2048-128=10483584=blocks,其计算公式为:

1.  bitmap->chunkshift=fzz(~bitmap_chunksize)=19;

2.  整个磁盘大小用bitmapchunk表达,可以算出chunk个数和需要的page个数:

a)  chunks=(blocks+(bitmap_chunksize*1024>>9)-1)>>(bitmap->chunkshift-9)=(10483584+1024-1)/1024=10238;

说明:将chunksize换算成sector单位,使用此公式就可以算出需要的bitmap chunk个数chunks

b)  pages=(chunks + (PAGE_SIZE <<3)/ COUNTER_BITS - 1)/((PAGE_SIZE << 3)/ COUNTER_BITS)=(10238+2048-1)/2048=5;

说明:用一个page(4096B)代表2048个chunk,每个chunk占用2B,2B 16bit长度用于描述每个chunk的状态

一个chunk对应md中的一个条带,在位图中一个chunk占用16bit:

1.  最高一位为是否需要同步标志,标志md对应条带是否需要同步;

2.  后面一位标志指针对应条带是否正在进行同步操作;

3.  低14位是一个计数器,表示对应条带内正在进行写操作的个数,其中0、1、2为特殊中,当为3时表示有1个写操作,4时有2个写操作,以此类推。

如下图所示:

* +--------------+----------+-----------------------------------------+
       
* | resync        | resync   |              counter                      |
       * | needed       | active    |                                                |
       * |  (0-1)        | (0-1)     |             (0-16383)                          |
       * +--------------+----------+-----------------------------------------+

1.4.2.2    磁盘位图

如图:


bitmap->filemap磁盘位图

其计算公式如下:

1.  Bitmap->filemap 的page个数计算公式:

Num_pages=((chunks + sizeof(bitmap_super_t)<<3 + 7) / 8) + PAGE_SIZE- 1) / PAGE_SIZE;

这个公式表示一个page的每一个size(0-4095B)代表8个chunks,相当于一个bit代表一个chunk;page指向bitmap->filemap[page_index];chunk的计算方法见内存位图部分。

2.  bitmap->filemap_attr:可以用来描述bitmap->filemap[page]的状态,这里为每个page申请了4bit的内存空间,不过此部分计算时,最少申请的长度为unsigned long,filemap的状态有:

BITMAP_PAGE_DIRTY=0;     //表示该bit位对应的数据需要往下刷

BITMAP_PAGE_CLEAN=1;     //表示该bit位需要清除

BITMAP_PAGE_NEEDWRITE=2;//表示该bit刚刚被清除,需要向下刷

1.5 Bitmap初始化

本部分开始介绍bitmap的初始化,其实现函数为bitmap_create,此函数在md设备创建时,或者指定一个bitmap文件时被调用。主要完成bitmap从磁盘到内存的加载。

Bitmap文件在磁盘中可能有两个位置,分别以文件指针file或者超级块之间的偏移来指定,这里以bitmap文件在md设备内举例说明。

1.5.1 函数bitmap_create

此函数流程如下:




函数说明:

1.  函数bitmap_read_sb:先调用函数read_sb_page,从mddev->disks链表头开始遍历所有的成员盘,从底层设备读取超级块的信息。如果读取成功,则此函数直接返回。

假设创建raid时指定三块成员盘为disk0、disk1、disk2,那么在这里只读了disk2的数据就会返回了。

读取时:

a)  bio->bi_sector=rdev->sb_start+offset+index* (PAGE_SIZE/512)=8+8+0=16;

b)  bio->bi_size=roundup(sizeof(bitmap_super_t),bdev_logical_block_size(rdev->bdev))=roundup(256+4096)=4096;

c)  page->index=0;

数据读取后:

a)  将page的引用计数加1

b)  page->private=NULL

c)  将读出来的数据赋值给bitmap->sb_page,并经过内存映射,得到bitmap_super_t *sb;判断sb->events<mddev->events,如果是,说明此bitmap数据已经失效,需要将sb->state标记上BITMAP_STALE。

2.  计算bitmap位图大小(公式见5.4.2.2),得出需要的page个数,然后给bitmap赋值:

bitmap->chunks = chunks;  // 把dev_sectors分成chunks个部分,每个大小为bitmap_chunk

bitmap->pages = pages;    //计算出来给bp数组申请的page个数

bitmap->counter_bits = COUNTER_BITS = 16

再按照算出来的pages个数,申请bitmap->bp结构。

3.  函数bitmap_init_from_disk:从底层磁盘读取bitmap,下一节详细介绍。

4.  函数bitmap_update_sb:调用函数write_sb_page,将bitmap信息写入每一个active成员盘(raid_disks>=0&&!FAULT)中;

1.5.2 函数bitmap_init_from_disk

此函数从底层磁盘读取bitmap,其传入参数为:

1.  Bitmap:bitmap结构

2.  Start:在磁盘完好没有降级或者 bitmap没有失效时start=mddev->recovery_cp,否则start=0.

下面为此函数流程:




函数说明:

1.  File_page_index计算当前chunk在哪个filemap page中,计算公式为:

(Chunk+(sizeof(bitmap_super_t) << 3)) >> (PAGE_SHIFT + 3)

2.  File_page_offset计算当前chunk在该page中的偏移,计算公式为:

(Chunk+(sizeof(bitmap_super_t) << 3)) & ((PAGE_SIZE<<3)- 1)

1.6 Bitmap的设置

Bitmap在md设备处理写访问请求时,调用了函数bitmap_startwrite设置bitmap内存信息,包括内存中的bitmap结构,以及内存中bitmap磁盘文件的映射filemap;在md设备实际将写请求提交给底层设备前,调用函数bitmap_unplug将bitmap信息写入磁盘。

除以上两个函数外,bitmap_start_sync,bitmap_end_sync,bitmap_endwrite等函数也和bitmap的一些标志设置有关。

1.6.1 函数bitmap_startwrite

函数在md设备处理写请求时(make_requestàadd_stripe_bio)调用。函数主要完成:

1.  如果是延迟写,增加延迟写计数;

2.  由于一个写操作的数据长度可能对应在内存位图结构bitmap_page中多个chunk,对于每个这样的chunk,有以下处理(这里传入的大小为STRIPE_SECTORS):

a)  获取该chunk对应的内存结构bp[page],返回bp数组指向的内容*bmc(表示该chunk的状态):调用函数bitmap_get_counter实现,如果找不到对应的bp[page],则申请一个新的page;

b)  如果该bitmap的*bmc达到最大值,说明md设备上对此chunk进行写的次数太多了,磁盘等待了太久,就需要启动设备的队列处理;

c)  如果bitmap上没有正在写操作(*bmc==0),设置此chunk在磁盘位图结构filemap对应page的bit位,再将此page对应的filemap_attr设置为BITMAP_PAGE_DIRTY位,最后设置*bmc=2;如果*bmc==1,也将此值赋值为2;

说明:

               i.     设置filemap对应page的bit位,调用函数ext2_set_bit(bit, kaddr)实现

              ii.     设置对应page的filemap_attr位,先算出此page对应在filemap_attr中的位置

(page->index<<2),然后将此值加上BITMAP_PAGE_DIRTY得到一个新的值tmp,然后调用__set_bit(tmp,bitmap->filemap_attr)进行位设置。

d)  将*bmc ++,此时此值为3,表示当前chunk写了1次(*bmc为0,表示没有写入操作;为1和2,特殊状态;为3,有1个写入,以此类推);

1.6.2 函数bitmap_unplug

此函数只在md.c中设置bitmap_set_bits接口中 以及 raid5.c中raid5d函数中调用。函数实现功能为检查每个filemap,然后将信息写入底层磁盘。流程如下:


流程说明:

1.  遍历bitmap所有的filemap页,如有标记为脏的页,或者需要写入的页,需要调用函数write_page将该页写入磁盘;

2.  如果有脏页,需要等待mddev->biolist(保存bitmap未处理完成的bio)所有bio处理结束函数才能返回;

3.  函数write_page,实际调用函数write_sb_page处理;此函数会先判断下发的sector是否越界,没有越界,调用函数md_super_write将数据写入底层磁盘,公式如下(成员盘rdev前data_offset=2048存放超级块和bitmap数据):

rdev->sb_start+offset+page->index*(PAGE_SIZE/512)+size/512>rdev->data_offset

4.  如果只有BITMAP_PAGE_NEEDWRITE标记的页,是不需要等待的。因为bit的清除并不是很关键,即使这个信息丢失,最多不过是多余的同步操作而已,没有副作用。而bit设置则需要可靠写入磁盘后方可进行md设备数据的写入,否则在数据写入底层磁盘过程中,bitmap写入底层磁盘前,md设备出现异常,可能导致数据不一致而bitmap不能发觉。

1.6.3 函数bitmap_endwrite

此函数在函数handle_failed_stripe和函数handle_stripe_clean_event中调用,第一个函数只有在raid5设备有两块以及以上成员盘失效时被调用,这里不考虑;第二个函数在hanle_stripe5中写数据完成后调用,会循环所有的成员盘,然后逐个调用此函数。

函数bitmap_endwrite实现功能如下:

1.  调用函数bitmap_get_counter,取出对应chunk在内存位图结构bitmap_page中的状态*bmc;

2.  将*bmc递减,表示一个写入操作完成

3.  检查*bmc值,如果小于3,设置对应filemap页标记BITMAP_PAGE_CLEAN

1.6.4 函数bitmap_start_sync

此函数在md设备需要恢复、同步时调用(函数sync_request中)。

主要调用函数bitmap_get_counter,获取对应sector在bitmap->bp[page].map[pageoff]中的状态bitmap_counter,检测此状态:

1.  如果NEED标志或者RESYNC标志被设置,就认为该数据块需要同步;如果NEED标志被设置,且md设备工作完好,需要清除NEED标志,设置RESYNC标志

2.  如果以上两个标志都没有设置或者sector在bitmap->bp[page].map不存在,则认为该数据块不需要同步。

1.6.5 函数bitmap_end_sync

此函数同样也在函数sync_request中调用。

主要调用函数bitmap_get_counter,获取对应sector在bitmap->bp[page].mp[pageoff]中的状态bitmap_counter,检测此状态:

1.  如果RESYNC标志被设置,清除该标志

2.  如果数据恢复、同步被终止,设置NEED标志

3.  如果bitmap_counter值小于3,表示当前page内对应的sector没有写入操作,设置该page对应的filemap_attr中的标记BITMAP_PAGE_CLEAN,表示该页需要清除bit位。

1.6.6 函数bitmap_store

此函数在sys接口bitmap_set_bits中设置需要恢复的数据段时被调用,此接口支持的输入格式为:echo “0 7-11 50 52-100 ” > /sys/block/mdx/md/bitmap_set_bits,其中“0 7-11 50 52-100 ”数值段代表bitmap_chunk号,md设备共有多少个chunk可以根据mddev->devsectors值和输入的bitmap_chunksize计算得出(参考5.4.2.2);

函数流程为:

1.  获取其中一段数据段,如7-11,chunk=7,end_chunk=11

2.  将chunk的值和end_chunk的值传入函数bitmap_dirty_bits:

a)  从chunk开始,设置该chunk代表磁盘位图filemap中的状态为BITMAP_PAGE_CLEAN,并且为该chunk在内存位图bp中申请一个page,并将内存位图中描述此chunk的状态*bmc设置成NEEDED_MASK

b)  设备该chunk在磁盘位图filemap中对应的bit位,并将其对应在filemap中的page设置为BITMAP_PAGE_DIRTY标记

c)  修改当前恢复位置mddev->recovery_cp为此chunk对应的第一个sector位置

3.  调用函数bitmap_unplug(见5.6.2)

1.7 Bitmap的清除

上一节中所列出的函数,涵盖了全部的bitmap核心函数。上面的流程中只有bit的设置,但没有bit的真正清除。对于可以清除的bit,最多只是设置了filemap对应页的CLEAN标记,没有实际的清除filemap中的bit和写入磁盘操作。

Bitmap的bit清除操作是在bitmap_daemon_work函数,此函数在raid5d中的md_check_recovery函数中调用,而且是一进函数就被调用。因为raid5d是一个守护进程,所以此函数在每次守护进程运行时,都会被调用。

1.7.1 函数bitmap_daemon_work

1.7.1.1    函数实现流程

流程图如下:

图一主流程


图二流程1


图三流程2


图四流程3


图五流程4

函数流程说明:

1.  检查bitmap_daemon_work是否睡眠足够长时间(当前时间-(上次运行时间+5s)),没有睡够就退出了;睡眠一段时间可以将一定时间内的写盘操作集中批量处理;

2.  遍历整个底层磁盘的trunks,获取每个trunk对应在mdev->filemap数组中的page,然后处理如下:

a)  如果该page没有设置BITMAP_PAGE_CLEAN(简称CLEAN)标记,再检查有没有设置BITMAP_PAGE_NEEDWRITE(简称NEED)标记:

               i.     没有NEED标记,则跳过此page

              ii.     有NEED标记,先清除NEED标记,然后将此page的内容写入底层磁盘(不需要等待bio完全返回),然后跳过此page

b)  如果该page设置了CLEAN标记,清除CLEAN标记;在本次bitmap_daemon_work执行中,清除该page的CLEAN标记只有这一次机会;如果在本次bitmap_daemon_work执行中,再次设置了CLEAN标记,也只能等待下次再执行时被清除了。

c)  获取该chunk对应的内存位图结构bitmap_page(二级位图)中对应map(指向page指针)中的状态*bmc(bitmap_counter),

               i.     如果bmc存在,且bmc的状态NEED、RESYNC没有设置(*bmc=2,表明该chunk对应所在的内存结构中的md数据段已经没有写访问),设置*bmc=1,并且将该chunk对应的磁盘位图结构filemap(一级位图)中对应的page设置标记CLEAN;

              ii.     如果bmc存在,且*bmc=1,则设置*bmc=0,调用函数bitmap_count_page,减少此chunk对应内存位图结构bitmap_page中的计数count,如果计数count==0,则释放该内存bitmap_page中申请的page;调用函数ext2_clear_bit清除该chunk对应磁盘位图结构filemap中的bit位;

             iii.     如果bmc不存在,继续跳转到内存位图结构bitmap_page中下一个map所对应的起始chunk;

d)  当磁盘位图结构中每个page中的chunk处理完成,都会判断该page是否设置了标记BITMAP_PAGE_NEEDWRITE(前提条件是该页的CLEAN标记在处理时被设置,然后刚刚在此流程中清除):

               i.     标记被设置,则清除NEED标记,然后将该页写入底层磁盘;

              ii.     标记没有被设置,则设置NEED标记;

1.7.1.2    函数实现原理

这个函数逻辑上比较绕,但可以看到真正执行磁盘位图结构filemap中每个chunk清除的工作是在该chunk对应的内存位图结构bitmap_page的*bmc=1后进行的,而写盘操作是在filemap页的状态为NEEDWRITE后进行的。所以要理清逻辑关系,还需要对每个chunk的*bmc的值和filemap的状态变化以及相互的关系做一个详细的了解:

1.  *bmc是用来描述每个chunk对应在内存位图结构bitmap_page中的状态,共占16 bit:

a)  最高位为NEED_MASK,表示需要进行数据恢复;

b)  第二位为RESYNC_MASK,表示数据恢复到当前chunk被终止,需要下次从此位置开始接着恢复;

c)  低14位是一个计数器counter,表示当前chunk被写的次数

描述写次数的计数器Counter其值也分0、1、2以及大于2:

a)  0、1、2比较特殊,这三个值都说明该chunk没有写操作进行,真正的写操作是从2开始累加的(bitmap_startwrite中);但是0表示该chunk没有设置,1表示已经设置,2表示所有写操作刚刚结束

b)  从3开始,描述被写的次数,为3表示写1次,一次类推;

2.  Filemap页的属性,CLEAN表示该页有bit需要清除;在CLEAN被清除后,会被设置NEEDWRITE属性,再次走进此daemon时,检测到NEEDWRITE标志被设置,会先清除此标记,然后把bitmap数据写入底层磁盘。

1.7.2 函数bitmap_count_page

此函数传入参数说明:

1.  struct bitmap *bitmap:bitmap结构;

2.  sector_t offset:单位sector;

3.  int inc:可以传入1或者-1,用于加/减bitmap->bp[page].count计数,表示此page对应有一个写或者一个写完成。

函数会在内部调用函数bitmap_checkfree,当判断到bitmap->bp[page].count==0时,表示此bitmap->bp[page]已经完全处理,这个时候可以释放掉申请的page空间,bitmap->bp[page]->map=NULL。

注:在写流程中调用函数bitmap_startwrite时会给bitmap->bp[page]->map申请page空间。

1.8 状态变迁图

下面是描述一个chunk对应在一级位图和二级位图中的状态变迁图:

    

    下面再单独说一下*bmc的状态在数据恢复过程中的变化:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值