Skip to content

Instantly share code, notes, and snippets.

@akiradeveloper
Last active April 23, 2016 01:46
Show Gist options
  • Save akiradeveloper/da7168cda5c12e35f3ebb0d6563e7e13 to your computer and use it in GitHub Desktop.
Save akiradeveloper/da7168cda5c12e35f3ebb0d6563e7e13 to your computer and use it in GitHub Desktop.
dm-writeboost 2.2.0改造メモ

Read

                if (dirtiness.is_dirty)
                        io_res = writeback_mb(wb, res.found_seg, res.found_mb, dirtiness.data_bits, true);
                        if (!io_res)
                                if (mark_clean_mb(wb, res.found_mb))
                                        dec_nr_dirty_caches(wb);

この, mbをクリーンしてnr dirty cachesをdecするパスはunlikelyなのでそもそもあまり価値がないから削除してもいい

2.2.0では, 危険. 同じパスに二度入ったことを考えると, 2回目では, HDDにデータがライトバックされていないにも関わらずdirtyではなくなっておりHDDのデータのみが採用される. (従って部分的に古くなる)


現状でも, SSDにフルヒットしたパスはio_resに依存していない. io_resに依存してるものを先にガードで省いてしまう方がコードとしてクリア これはリファクタリングの範疇

これはライトパスを一切いじらないでも安全 なのでまずリードからやって2.1.3とする?(リリースのことを考えると, クリティカルでなければあまり細かいリリースはしない方がいいか?)

リードパスでトータル1日という感じ (形を変えるのはすぐだが, データの合成などを実装するため. わざと実装を誤って, 回帰テストがコケるかどうか見るという手はある. 回帰テストがここをカバーしているかどうかを見るため)

Write

prepare_overwrite内でデータの合成を行ったあと, いきなりmetadataをmark cleanしてしまうのは危険.

        if (mark_clean_mb(wb, old_mb))
                dec_nr_dirty_caches(wb);

        ht_del(wb, old_mb);

もし, このスレッドがRAM bufferに書き込む前に突然死したあとに同キャッシュをreadすると, キャッシュヒット出来ない. (こんなケースが存在するかは不明)

まともな言語で書くならば書いたあとのcallbackとして書くところ (ただし, 参照を持ち続けるため, 良いともいえない)

このうち, ht_delは, 後に新しいブロックに書くために絶対に必要. (書く前にht_registerする仕組みになってるので, ここは棄てるしかないかもしれない) (書いてしまったあとにhtが重複するのもおかしいので, ht_delとregisterをatomicに出来ないならばしょうがない)


ライトパスでデータの合成を行うと,

新しいmbを登録したあと, それをtaintする書き込む前にreadした場合, わけのわからないデータを読みこむことになる潜在バグがありそう. これは全くの新規ライトの場合でもありうる. (古いやつをreplaceするかどうかは関係ないと思う)

安全に行くならば, write_on_rambufferまでmutexで囲むことだが, 性能が落ちる.

今は, mutexの中でHDDにライトバックしてしまうということが救いになってる. 今のままだと, あるブロックに対してライトしてる時は(超一瞬だが)状態が不定ということになりうる.

        ht_register(wb, res.head, ret, &res.key);

        mutex_unlock(&wb->io_lock);

        return ret;
}

/*
 * Write bio data to RAM buffer.
 */
static int do_process_write(struct wb_device *wb, struct metablock *write_pos, struct bio *bio)
{
        if (taint_mb(wb, write_pos, bio))
                inc_nr_dirty_caches(wb);

        write_on_rambuffer(wb, write_pos, bio);

作業計画

まずはReadからやる. Readの変更は単純に設計改善に相当するので, この時点で回帰テストは動作する. Readからやる理由はWriteよりは変更が小さい(ロックの変更がない)というのと, Readに関してSUBMITTEDで返すことが正しいかどうか判断出来ないから.

  1. バッファをbioのbvecにコピーする汎用関数を作る
  2. フローを組み替えてテスト
  3. データ合成をon buffer hit, on ssd hitのケースで実装してそれぞれ回帰テスト. ここでは上記関数をいじって, 敢えてバグるようにして(バイトずれとか), 回帰テストが有効化をあぶり出す
  4. リファクタリング. ここでReadに関してfixする. 動くならばdevelopにマージする.
@akiradeveloper
Copy link
Author

実は,

        cache_lookup(wb, bio, &res);
        if (res.found) {
                if (unlikely(res.on_buffer)) {
                        /* Overwrite on the ram buffer */
                        mutex_unlock(&wb->io_lock);
                        return res.found_mb;
                } else {
                        /*
                         * Invalidate the old cache on the cache device because
                         * we can't overwrite cache block on the cache device.
                         */
                        prepare_overwrite(wb, res.found_seg, res.found_mb, io_fullsize(bio));
                        dec_inflight_ios(wb, res.found_seg);
                }
        } else
                might_cancel_read_cache_cell(wb, bio);

この区間は, writeのための準備期間と考えて, readlockで包めないかな?
その後の部分だけをwrite lockで包む

@akiradeveloper
Copy link
Author

まぁでも結局, write_on_rambufferを何かで包まないといけないのであれば,
結局そこで律速される運命は避けられないので,
簡明のため上から下まで全部くくるという割り切りでもいいかも知れない

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment