Chinaunix首页 | 论坛 | 博客
  • 博客访问: 51805
  • 博文数量: 9
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 87
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-04 23:05
文章分类

全部博文(9)

文章存档

2015年(7)

2014年(2)

我的朋友

分类: LINUX

2015-02-28 22:34:07

大概有半年时间没有分析过DRBD的源码了。今天又翻出了DRBD的代码,想看一下activelog的作用和原理。

首先是看DRBD手册中关于activelog的介绍:

手册的内容很精炼,只是简单介绍了工作原理,activelog中记录的是最近写入磁盘中的IO,每一条activelog对应着一个4M的数据块。62条这样的日志组成一个512bit的sector,整个activelog由若干个这样的sector组成。activelog的条数是可以指定的,在drbd.conf文件中可以配置,配置值为:activelog能记录的数据块的大小,注意不是日志的条数。该值的含义也就是:当出现故障重启时,需要在主备两端同步的数据块的总大小。


当写入新的4M数据块时,DRBD会刷新一次activelog,activelog是循环写入的,在元数据里面会记录当前的所有日志中,哪一条是最老的。因此当反转覆盖发生时,需要更新元数据。事实上,当运行的时间足够长时,反转覆盖在每一次添加一个4M数据块时都会发生。但是当指定的size值与512bit(代表240多M,具体值要根据代码计算。)不是整数倍关系时,在最后一个sector反转时,需要写两个sector,因此从这个角度讲,activelog的size最好是整数个sector,这样可以减少一部分不必要的写两个sector。

具体的activelog的结构为:

/* We maintain a trivial check sum in our on disk activity log.  * With that we can ensure correct operation even when the storage  * device might do a partial (last) sector write while loosing power.  */ struct __packed al_transaction {
    u32       magic;
    u32       tr_number; struct __packed {
        u32 pos;
        u32 extent; } updates[1 + AL_EXTENTS_PT];
    u32       xor_sum;
};

这里AL_EXTENTS_PT的值为62。

再来看具体的日志记录过程:当主端在开始写入一个新的4M数据块时,就会将该块需要写入的位置记录到activelog,写activelog的过程本身就在一次bio的处理过程中,在bio处理过程中,不能再调用bio的处理函数,因此写activelog需要使用一个异步任务,这个在代码里面的注释有详细的说明:

void drbd_al_begin_io(struct drbd_conf *mdev, sector_t sector)
{ unsigned int enr = (sector >> (AL_EXTENT_SHIFT-9)); struct lc_element *al_ext; struct update_al_work al_work;

    D_ASSERT(atomic_read(&mdev->local_cnt) > 0);

    trace_drbd_actlog(mdev, sector, "al_begin_io");

    wait_event(mdev->al_wait, (al_ext = _al_get(mdev, enr))); if (al_ext->lc_number != enr) { /* drbd_al_write_transaction(mdev,al_ext,enr);  * recurses into generic_make_request(), which  * disallows recursion, bios being serialized on the  * current->bio_tail list now.  * we have to delegate updates to the activity log  * to the worker thread. */ init_completion(&al_work.event);
        al_work.al_ext = al_ext;
        al_work.enr = enr;
        al_work.old_enr = al_ext->lc_number;
        al_work.w.cb = w_al_write_transaction;
        drbd_queue_work_front(&mdev->data.work, &al_work.w);
        wait_for_completion(&al_work.event);
        dev_err(DEV, "w_al_write_transaction called\n");
        mdev->al_writ_cnt++; /*  DUMPI(al_ext->lc_number);  DUMPI(mdev->act_log->new_number);  */ spin_lock_irq(&mdev->al_lock);
        lc_changed(mdev->act_log, al_ext);
        spin_unlock_irq(&mdev->al_lock);
        wake_up(&mdev->al_wait);
    }
}

等待activelog写完成后,才发起IO本地写入流程和发往对端。

假设,本端写入完成后,IO还没有发往对端前,此时本端出现类似掉电的情况,这样本端的IO已经下盘,而对端的IO还没有下盘,两端出现了数据不一致,这样当本端启动时,则需要从此时的主端同步所有的数据,当整个镜像盘非常大时,这个同步过程会非常漫长。有了activelog后,同步慢的问题可以得到很好的解决,如果对端升主后,写入的数据未超出activelog能cover的数据块的size时,可以只比对本端记录的acitvesize的数据,将这部分数据同步到本端即可。因此同步时间,取决于activelog的大小。当然如果本端下点时间过长,对端在做主期间写入了太多的数据,那么还是需要作全盘同步的。这是不可避免的。activelog只是解决临时的主节点重启的问题。

在DRBD的官网上面有一篇文章详细介绍activelog的设计原理:。

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