lkmlにメモリーリーク修正のパッチを投げたので修正内容のメモ

どんなことをやったかをメモしておこう。
といっても今回のは比較的簡単に調べられたけど。パッチを作ったカーネルは3.13-rc4です。

2013/12/20 追記:
(´-`).。oO(Reviewed-byしてもらったのでそのうちメインラインにも入るだろう


kmemleakさんから以下のようなレポートがあった訳です。

unreferenced object 0xffff880118f14700 (size 32):
  comm "swapper/0", pid 1, jiffies 4294877401 (age 123.283s)
  hex dump (first 32 bytes):
    00 01 10 00 00 00 ad de 00 02 20 00 00 00 ad de  .......... .....
    00 d4 d2 18 01 88 ff ff 01 00 00 00 00 04 00 00  ................
  backtrace:
    [<ffffffff814edb1e>] kmemleak_alloc+0x4e/0xb0
    [<ffffffff811889dc>] kmem_cache_alloc_trace+0x1ec/0x260
    [<ffffffff810aba66>] pm_vt_switch_required+0x76/0xb0
    [<ffffffff812f39f5>] register_framebuffer+0x195/0x320
    [<ffffffff8130af18>] efifb_probe+0x718/0x780
    [<ffffffff81391495>] platform_drv_probe+0x45/0xb0
    [<ffffffff8138f407>] driver_probe_device+0x87/0x3a0
    [<ffffffff8138f7f3>] __driver_attach+0x93/0xa0
    [<ffffffff8138d413>] bus_for_each_dev+0x63/0xa0
    [<ffffffff8138ee5e>] driver_attach+0x1e/0x20
    [<ffffffff8138ea40>] bus_add_driver+0x180/0x250
    [<ffffffff8138fe74>] driver_register+0x64/0xf0
    [<ffffffff813913ba>] __platform_driver_register+0x4a/0x50
    [<ffffffff8191e028>] efifb_driver_init+0x12/0x14
    [<ffffffff8100214a>] do_one_initcall+0xfa/0x1b0
    [<ffffffff818e40e0>] kernel_init_freeable+0x17b/0x201

トレースからpm_vt_switch_required()がメモリ確保系の関数を使った事が分かるのでコードを見る。こっちのコードは現時点では3.12だけどそんな変わらないのでおk

 44 void pm_vt_switch_required(struct device *dev, bool required)
 45 {
 46         struct pm_vt_switch *entry, *tmp;
 47 
 48         mutex_lock(&vt_switch_mutex);
 49         list_for_each_entry(tmp, &pm_vt_switch_list, head) {
 50                 if (tmp->dev == dev) {
 51                         /* already registered, update requirement */
 52                         tmp->required = required;
 53                         goto out;
 54                 }
 55         }
 56 
 57         entry = kmalloc(sizeof(*entry), GFP_KERNEL);
 58         if (!entry)
 59                 goto out;
 60 
 61         entry->required = required;
 62         entry->dev = dev;
 63 
 64         list_add(&entry->head, &pm_vt_switch_list);
 65 out:
 66         mutex_unlock(&vt_switch_mutex);
 67 }

これはメモリ確保している処理が57行目のkmalloc()しかないのでこいつがリークしたと考える。
そうすると、こいつがリストから削除されるタイミングが怪しいのでは?なんて思うのでそこを見てみる。
list_delをしているところを探してみるとpm_vt_switch_unregister()がヒット。名前的にもばっちりですねぇ。

 76 void pm_vt_switch_unregister(struct device *dev)
 77 {
 78         struct pm_vt_switch *tmp;
 79 
 80         mutex_lock(&vt_switch_mutex);
 81         list_for_each_entry(tmp, &pm_vt_switch_list, head) {
 82                 if (tmp->dev == dev) {
 83                         list_del(&tmp->head);
 84                         break;
 85                 }
 86         }
 87         mutex_unlock(&vt_switch_mutex);
 88 }

83行目でリストからオブジェクトを外しているけどそれだけですね。このオブジェクトはkmalloc()でメモリを確保しているのでkfree()を呼ぶ必要あるじゃん!
ということでこんなパッチを作成して関係者&MLに送信っと。

kernel/power/console.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/power/console.c b/kernel/power/console.c
index 463aa673..eacb8bd 100644
--- a/kernel/power/console.c
+++ b/kernel/power/console.c
@@ -81,6 +81,7 @@ void pm_vt_switch_unregister(struct device *dev)
        list_for_each_entry(tmp, &pm_vt_switch_list, head) {
                if (tmp->dev == dev) {
                        list_del(&tmp->head);
+                       kfree(tmp);
                        break;
                }
        }

今回一番ハマったのはgit send-emailで以下の2パッケージを入れないとダメだったというとこですねw

yaourt -S perl-net-smtp-ssl
yaourt -S perl-authen-sasl

Linuxカーネル Hacks ―パフォーマンス改善、開発効率向上、省電力化のためのテクニック

Linuxカーネル Hacks ―パフォーマンス改善、開発効率向上、省電力化のためのテクニック