nodejsのmomentの関数をstubする

現在時刻取得のところmomentを使ってるけど、テスト書くときは固定値になって欲しいよねというところのめも。

momentを使っているクラスがこんなんだとして、

'use strict';

const moment = require('moment');

module.exports = class Foo {
        static get unixtime() {
                return moment().unix();
        }
};

stubでスタブを作る場合はstub()の第一引数にはmomentではなくてmoment.fnを渡す

'use strict';

const sinon = require('sinon');

const moment = require('moment');

const Foo = require('./foo');

const stub = sinon.stub(moment.fn, 'unix');

stub.onCall(0).returns(1475819617);
stub.onCall(1).returns(1475819617 + 10);
stub.returns(1475819617 + 20);

for (let i = 0; i < 5; i++) {
        console.log(`${i}: ${Foo.unixtime}`);
}

stub.restore();

console.log(`restored: ${Foo.unixtime}`);

実行結果

masami@arch moment_stub$ node ./test.js
0: 1475819617
1: 1475819627
2: 1475819637
3: 1475819637
4: 1475819637
restored: 1475820545

( ´ー`)フゥー...

Linuxのカーネルモジュールがロードされるアドレス範囲

カーネルモジュールがロードされるアドレスって範囲決まってたはずだけど、自分で言っといてホントそうだったけ?とか思ったので確認。

調べるのはLinux 4.8。 まず、Documentation/x86/x86_64/mm.txtを確認して、以下のところをチェック。

 22 ffffffffa0000000 - ffffffffff5fffff (=1526 MB) module mapping space

で、この範囲にロードされるよねってとこです。

それでは、モジュールがどのように配置されるのかをコードで確認してみます。まずkernel/module.cのload_module()からみます。

この関数の最初のほうでlayout_and_allocate()を実行します。

3589         /* Figure out module layout, and allocate all the memory. */
3590         mod = layout_and_allocate(info, flags);

layout_and_allocate()ではmove_module()という関数を呼びます。

3266         /* Allocate and move to the final place */
3267         err = move_module(mod, info);

そして、ここで呼び出しているmodule_alloc()が実際にアドレスを指定してモジュール用のメモリを確保するところです。

3068 static int move_module(struct module *mod, struct load_info *info)
3069 {
3070         int i;
3071         void *ptr;
3072 
3073         /* Do the allocs. */
3074         ptr = module_alloc(mod->core_layout.size);

ただ、module_alloc()はアーキテクチャ依存な関数になります。module.cにあるmodule_alloc()はweak属性が付いているのでx86_64アーキテクチャにおいてはダミーです。

2688 void * __weak module_alloc(unsigned long size)
2689 {
2690         return vmalloc_exec(size);
2691 }

x86_64でのmodule_alloc()はarch/x86/kernel/module.cにあります。

 79 void *module_alloc(unsigned long size)
 80 {
 81         void *p;
 82 
 83         if (PAGE_ALIGN(size) > MODULES_LEN)
 84                 return NULL;
 85 
 86         p = __vmalloc_node_range(size, MODULE_ALIGN,
 87                                     MODULES_VADDR + get_module_load_offset(),
 88                                     MODULES_END, GFP_KERNEL | __GFP_HIGHMEM,
 89                                     PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
 90                                     __builtin_return_address(0));
 91         if (p && (kasan_module_alloc(p, size) < 0)) {
 92                 vfree(p);
 93                 return NULL;
 94         }
 95 
 96         return p;
 97 }
 98 

ここでは__vmalloc_node_range()を使ってメモリを確保しますが、この時にアドレスの範囲を指定します。__vmalloc_node_range()の3番目の引数は開始アドレス、4番目が終了アドレスです。

MODULES_VADDRとMODULES_ENDはarch/x86/include/asm/pgtable_64_types.hで定義されています。

 67 #define MODULES_VADDR    (__START_KERNEL_map + KERNEL_IMAGE_SIZE)
 68 #define MODULES_END      _AC(0xffffffffff000000, UL)

__START_KERNEL_mapとKERNEL_IMAGE_SIZEはarch/x86/include/asm/page_64_types.hで定義されています。

__START_KERNEL_mapはこうです。

 46 #define __START_KERNEL_map      _AC(0xffffffff80000000, UL)

KERNEL_IMAGE_SIZEは.configの設定次第ですが、うちはCONFIG_RANDOMIZE_BASEが有効なので60行目のほうを使います。

 59 #if defined(CONFIG_RANDOMIZE_BASE)
 60 #define KERNEL_IMAGE_SIZE       (1024 * 1024 * 1024)
 61 #else
 62 #define KERNEL_IMAGE_SIZE       (512 * 1024 * 1024)
 63 #endif

なんで、こんな感じになります。

>>> hex(int("0xffffffff80000000", 16) + (1024 * 1024 * 1024))
'0xffffffffc0000000'

ということで、カーネルモジュールは0xffffffffc0000000〜0xffffffffff000000に置かれると。

実際にこれを確認するのは/proc/modulesを見れば良いので、こんな感じのスクリプトでチェックできます。

#!/usr/bin/env python

with open('/proc/modules', 'r') as f:
    lines = f.readlines()
    for line in lines:
        data = line.split(' ')
        name = data[0]
        if (data[2] != '0'):
            addr = int(data[len(data) - 1].strip(), 16)
            if addr >= 0xffffffffc0000000 and addr < 0xffffffffff5fffff:
                print("%s:%x is in module mapping space " % (name, addr))
            else:
                print("%s:%x is not in module mapping space" % (name, addr))

stack traceとって流れを確認するならこんなsystemtapスクリプト(これくらいだとbccよりかんたんなのでこっちにしました)を動かして、適当なモジュールをinsmodすればOKです。

#!/usr/bin/env stap

probe begin {
        printf("Start\n")
}

probe kernel.function("module_alloc") {
        printf("%s\n", symfileline(addr()));
        print_backtrace()
        exit()
}

probe end {
        printf("Done\n")
}

こんな結果になります。

masami@saga:~/mod$ sudo stap -g module_alloc.stp
Start
0xffffffffa705d8e0
 0xffffffffa705d8e0 : module_alloc+0x0/0xd0 [kernel]
 0xffffffffa7106cd6 : load_module+0x1576/0x2620 [kernel] (inexact)
 0xffffffffa7204a31 : __vfs_read+0xe1/0x130 [kernel] (inexact)
 0xffffffffa720578c : vfs_read+0x11c/0x130 [kernel] (inexact)
 0xffffffffa720c238 : kernel_read_file+0x1e8/0x210 [kernel] (inexact)
 0xffffffffa720c2a9 : kernel_read_file_from_fd+0x49/0x80 [kernel] (inexact)
 0xffffffffa7107ff4 : SyS_finit_module+0xe4/0x120 [kernel] (inexact)
 0xffffffffa75dc7f2 : entry_SYSCALL_64_fastpath+0x1a/0xa4 [kernel] (inexact)
Done

ということで、一応合ってたようですε-(´∀`*)ホッ

pid cgroupのコードを読む

この記事はLinuxカーネルもくもく会 #24 - connpassで書いてます。今日は前に追加されたcgroupsのpid管理のコードを読んでみます。

ファイルはcgroup_pids.cです。Linux 4.7のコードを対象にします。

最初にデータ構造です。pidの場合はpids_cgroup構造体が使われます。この構造体はcgroup_pids.cで定義されています。

 43 struct pids_cgroup {
 44         struct cgroup_subsys_state      css;
 45 
 46         /*
 47          * Use 64-bit types so that we can safely represent "max" as
 48          * %PIDS_MAX = (%PID_MAX_LIMIT + 1).
 49          */
 50         atomic64_t                      counter;
 51         int64_t                         limit;
 52 };

構造も結構スッキリしていて、今のPID数と上限、それにcgroup_subsys_state構造体です。cgroup_subsys_state構造体から親階層のpids_cgroupを取得したりします。

pid cgroupsで定義されている操作はcgroup_subsys構造体で定義します。css_alloc/css_free, can_attach/cancel_attach, can_fork/cancel_forkはペアですね。

306 struct cgroup_subsys pids_cgrp_subsys = {
307         .css_alloc      = pids_css_alloc,
308         .css_free       = pids_css_free,
309         .can_attach     = pids_can_attach,
310         .cancel_attach  = pids_cancel_attach,
311         .can_fork       = pids_can_fork,
312         .cancel_fork    = pids_cancel_fork,
313         .free           = pids_free,
314         .legacy_cftypes = pids_files,
315         .dfl_cftypes    = pids_files,
316 };
317 

pid cgroupsのファイルとしてはこれらのファイルがあります。

291 static struct cftype pids_files[] = {
292         {
293                 .name = "max",
294                 .write = pids_max_write,
295                 .seq_show = pids_max_show,
296                 .flags = CFTYPE_NOT_ON_ROOT,
297         },
298         {
299                 .name = "current",
300                 .read_s64 = pids_current_read,
301                 .flags = CFTYPE_NOT_ON_ROOT,
302         },
303         { }     /* terminate */
304 };

maxのほうは最大値を設定するファイルで読み(pids_max_show)書き(pids_max_write)可能です。currentの方は現在の値を表示するだけなので読み込み(pids_current_read)しか設定されていません。

これらのファイルはflagsにCFTYPE_NOT_ON_ROOTが設定されているので、pid cgroupのトップディレクトリには表示されません。

[root@miko user.slice]# ls -la /sys/fs/cgroup/pids
total 0
dr-xr-xr-x  5 root root   0 Sep 16 02:36 ./
drwxr-xr-x 11 root root 260 Sep 16 02:35 ../
-rw-r--r--  1 root root   0 Sep 16 02:36 cgroup.clone_children
-rw-r--r--  1 root root   0 Sep 16 02:36 cgroup.procs
-r--r--r--  1 root root   0 Sep 16 02:36 cgroup.sane_behavior
drwxr-xr-x  2 root root   0 Sep 16 02:35 init.scope/
-rw-r--r--  1 root root   0 Sep 16 02:36 notify_on_release
-rw-r--r--  1 root root   0 Sep 16 02:36 release_agent
drwxr-xr-x 31 root root   0 Sep 16 02:35 system.slice/
-rw-r--r--  1 root root   0 Sep 16 02:36 tasks
drwxr-xr-x  3 root root   0 Sep 16 02:36 user.slice/

pid cgroupsの下の階層には存在します。pids.maxとpids.currentがそのファイルです。

[root@miko user.slice]# ls -la /sys/fs/cgroup/pids/user.slice
total 0
drwxr-xr-x 3 root root 0 Sep 16 02:36 ./
dr-xr-xr-x 5 root root 0 Sep 16 02:36 ../
-rw-r--r-- 1 root root 0 Sep 16 02:36 cgroup.clone_children
-rw-r--r-- 1 root root 0 Sep 16 02:36 cgroup.procs
-rw-r--r-- 1 root root 0 Sep 16 02:36 notify_on_release
-r--r--r-- 1 root root 0 Sep 16 02:36 pids.current
-rw-r--r-- 1 root root 0 Sep 16 02:36 pids.max
-rw-r--r-- 1 root root 0 Sep 16 02:36 tasks
drwxr-xr-x 4 root root 0 Sep 16 02:36 user-1000.slice/

ファイルの内容はこんな感じです。

[root@miko user.slice]# cat pids.max
max
[root@miko user.slice]# cat pids.current
9

では、コードを適当に読んでいきます。まずはpids_css_alloc()。新しくサブシステムが作られる時に呼ばれます。個々で作成するのはcgroup_subsys_state構造体です。実際にはpids_cgroup構造体を作って、その中のcss変数を返しています。この辺はカーネル定番のcontainer_ofマクロの出番ですね。

 64 static struct cgroup_subsys_state *
 65 pids_css_alloc(struct cgroup_subsys_state *parent)
 66 {
 67         struct pids_cgroup *pids;
 68 
 69         pids = kzalloc(sizeof(struct pids_cgroup), GFP_KERNEL);
 70         if (!pids)
 71                 return ERR_PTR(-ENOMEM);
 72 
 73         pids->limit = PIDS_MAX;
 74         atomic64_set(&pids->counter, 0);
 75         return &pids->css;
 76 }

PIDS_MAXは↓のように定義されています。

40 #define PIDS_MAX (PID_MAX_LIMIT + 1ULL)

PID_MAX_LIMITはinclude/linux/threads.hにて↓のように決まります。

 33 #define PID_MAX_LIMIT (CONFIG_BASE_SMALL ? PAGE_SIZE * 8 : \
 34         (sizeof(long) > 4 ? 4 * 1024 * 1024 : PID_MAX_DEFAULT))

limit以外は初期値0って感じになります。

pids_css_free()はpids_css_allocで作ったpids_cgroup構造体のメモリをkfree()で解放するだけです。

次はpids_can_attach()です。この関数はプロセスがあるcgroupから別のcgroupに移動する時に呼ばれます。処理内容としては移動元のpid cgroupからPIDの使用数(counter)を減らして、新しいほうのPIDの使用数を増やします。

165 static int pids_can_attach(struct cgroup_taskset *tset)
166 {
167         struct task_struct *task;
168         struct cgroup_subsys_state *dst_css;
169 
170         cgroup_taskset_for_each(task, dst_css, tset) {
171                 struct pids_cgroup *pids = css_pids(dst_css);
172                 struct cgroup_subsys_state *old_css;
173                 struct pids_cgroup *old_pids;
174 
175                 /*
176                  * No need to pin @old_css between here and cancel_attach()
177                  * because cgroup core protects it from being freed before
178                  * the migration completes or fails.
179                  */
180                 old_css = task_css(task, pids_cgrp_id);
181                 old_pids = css_pids(old_css);
182 
183                 pids_charge(pids, 1);
184                 pids_uncharge(old_pids, 1);
185         }
186 
187         return 0;
188 }

countを増やしたり減らしたりするのがpids_charge()とpids_uncharge()です。

pids_charge()はこのような処理で、階層を上位に移動しながら、pidのcounterを増やします。

122 static void pids_charge(struct pids_cgroup *pids, int num)
123 {
124         struct pids_cgroup *p;
125 
126         for (p = pids; parent_pids(p); p = parent_pids(p))
127                 atomic64_add(num, &p->counter);
128 }

pids_uncharge()のほうは単純にいうとpids_charge()と逆の処理をします。実際にcounterの値を更新するのはpids_cancel()ですが。

pids_cancel_attach()はアタッチをキャンセルする処理で、やることは移動先のcounterを減らして移動元のcounterの値を増やします。

pids_can_fork()はfork/clone/vfork時にcopy_process()の途中で呼ばれます。cgroup_can_fork()がサブシステムのcan_fork()を呼び出す形です。

1567         retval = cgroup_can_fork(p);
1568         if (retval)
1569                 goto bad_fork_free_pid;

そして、pids_can_fork()の処理ですが、これはpids_try_charge()を呼んでpid数の上限に達していないか確認し、問題なければ0を返します。上限に達している場合はEAGAINが返ります。なので、fork爆弾とかはここで上限値チェックに引っかかるんじゃないでしょうか。試してませんが。。。

pids_cancel_fork()はキャンセル処理なので増やしたcounterの値を減らします。

次に、pids.maxファイルへの書き込み時のpids_max_write()ですが、これは大方の予想通りなことしかしてません。 読み込みのときにmaxって文字列を出してましたが、これは条件があります。pids_max_show()で、pid cgroupに設定されているlimitがPIDS_MAX以上の場合はmaxという文字列が表示されます。

269 static int pids_max_show(struct seq_file *sf, void *v)
270 {
271         struct cgroup_subsys_state *css = seq_css(sf);
272         struct pids_cgroup *pids = css_pids(css);
273         int64_t limit = pids->limit;
274 
275         if (limit >= PIDS_MAX)
276                 seq_printf(sf, "%s\n", PIDS_MAX_STR);
277         else
278                 seq_printf(sf, "%lld\n", limit);
279 
280         return 0;
281 }

limitの初期値はPIDS_MAXなので、pids.maxを弄ってなければデフォルトはmaxになります。

pid cgroupはだいたいこれくらいですね。結構短くて読みやすいですね。

改訂3版 Linuxエンジニア養成読本 (Software Design plus)

改訂3版 Linuxエンジニア養成読本 (Software Design plus)

bccめも:bpf_probe_read()

bpf_probe_read()は3番目の引数を安全に読み出すためのものという認識で間違ってないとは思うけど。

linuxカーネルsamples/bpf/bpf_helpers.hを見ると引数名はunsafe_ptrとかなってるし。strpcyとかだとinvalid opcode的なエラーになったので、この辺を使っておくのが良いのだろう。たぶん。

 18 static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) =
 19         (void *) BPF_FUNC_probe_read;

memo.

#!/usr/bin/env python

from bcc import BPF
import ctypes as ct

prog = """
#include <uapi/linux/ptrace.h>
#include <linux/kernfs.h>

BPF_PERF_OUTPUT(events);

struct cgroup_rmdir_dname {
    char name[256];
};

void kprobe__cgroup_rmdir(struct pt_regs *ctx, struct kernfs_node *kn)
{
        struct cgroup_rmdir_dname crd = {};
        bpf_probe_read(&crd.name, sizeof(crd.name) - 1, (void *) kn->name);
        events.perf_submit(ctx, &crd, sizeof(crd));
}
"""

class CgroupRmdirData(ct.Structure):
    _fields_ = [
        ("name", ct.c_char * 256)
    ]

def print_event(cpu, data, size):
    event = ct.cast(data, ct.POINTER(CgroupRmdirData)).contents
    print("name: %s" % (event.name))

print("Ctrl-c to stop")
b = BPF(text=prog)
b["events"].open_perf_buffer(print_event)
while 1:
    b.kprobe_poll()

こんな感じ。

# mkdir test && rmdir test
# ./cgroup_rmdir.py
Ctrl-c to stop
name: b'test'

改訂3版 Linuxエンジニア養成読本 (Software Design plus)

改訂3版 Linuxエンジニア養成読本 (Software Design plus)

Linux 2.0.40の頃のkmalloc()

たまには古いカーネルでも読んでみましょう的なところで、Linux 2.0.40の頃はkmalloc()はどんな実装だったのかというところでも見てみましょう。最近のkmalloc()の実装は使用しているディストーションが採用しているスラブアローケータの__kmalloc()の実装を見てみましょう。今のkmalloc()はスラブアローケータを使用しています。SLUBだとこんな感じです。

で、Linux 2.0.40はkernel.orgのファイル作成日時で見ると08-Feb-2004 07:13となっているので、12年前にリリースされたんですね。このバージョンが2.0系の最終バージョンです。

この当時のkmallocはスラブアローケータは使用していないけど、スラブアローケータ的に要求されたサイズを一番近い2の累乗サイズに切り上げて使っています。サイズはblocksizeという変数で管理してます。

 97 static const unsigned int blocksize[] = {
 98         32,
 99         64,
100         128,
101         252,
102         508,
103         1020,
104         2040,
105         4096 - 16,
106         8192 - 16,
107         16384 - 16,
108         32768 - 16,
109         65536 - 16,
110         131072 - 16,
111         0
112 };

切り上げかたは「要求されたサイズ+管理用のヘッダーサイズ」に近い次のサイズです。管理用のヘッダーサイズと言うのは、割り当てたメモリ領域を管理するためのヘッダーです。データとしてはstruct block_header型で、x86_32だと8byteになります。

 46 struct block_header {
 47         unsigned long bh_flags;
 48         union {
 49                 unsigned long ubh_length;
 50                 struct block_header *fbh_next;
 51         } vp;
 52 };

例えば、要求サイズが20byteだったとして、8byteがヘッダーに必要なので一番近い32byteのメモリを確保して返すことになります。呼び出し元にはヘッダーの領域を返す必要な無いので、ヘッダーの終了アドレスの次の位置を返すことになります。図にすると↓のような感じですね。

f:id:masami256:20160819232743p:plain

struct block_headerのfbh_nextが次の空き領域を指してるわけですね。

ちなみにSLUBだとヘッダーで管理って方法はとっていません。雑に図を描くとこうなります。

f:id:masami256:20160820000040p:plain

SLUBのポイントは32byteのスラブオブジェクトはヘッダーとかなくて、そのまま32byteの領域です。未使用の領域は先頭に次の空きスラブオブジェクトを指すポインタとして使用します。使用するときはこの領域毎返します。使用中のオブジェクトをkfreeで解放する場合は、また先頭の領域を次の空きスラブオブジェクト指すようにします。次の空きスラブオブジェクトをどう知るかってのはstruct kmem_cache_cpu構造体を使いますが、今日はSLUBの話ではないので、興味のある人はmm/slub.cを読みましょう。

では、kmalloc()を読んできます。

まず最初のほう。

229 void *kmalloc(size_t size, int priority)
230 {
231         unsigned long flags;
232         unsigned long type;
233         int order, dma;
234         struct block_header *p;
235         struct page_descriptor *page, **pg;
236         struct size_descriptor *bucket = sizes;
237 
238         /* Get order */
239         order = 0;
240         {
241                 unsigned int realsize = size + sizeof(struct block_header);
242                 for (;;) {
243                         int ordersize = BLOCKSIZE(order);
244                         if (realsize <= ordersize)
245                                 break;
246                         order++;
247                         bucket++;
248                         if (ordersize)
249                                 continue;
250                         printk("kmalloc of too large a block (%d bytes).\n", (int) size);
251                         return NULL;
252                 }
253         }
254 

最初に必要なサイズを設定し、適切なサイズの領域があるか調べます。 次に、gfpのチェックですね。DMA領域からメモリ確保するかとか。

255         dma = 0;
256         type = MF_USED;
257         pg = &bucket->firstfree;
258         if (priority & GFP_DMA) {
259                 dma = 1;
260                 type = MF_DMA;
261                 pg = &bucket->dmafree;
262         }
263 
264         priority &= GFP_LEVEL_MASK;
265 

ここは割り込み中なのにGFP_ATOMICを付けずにkmalloc()を呼んだ場合の処理ですね。

266 /* Sanity check... */
267         if (intr_count && priority != GFP_ATOMIC) {
268                 static int count = 0;
269                 if (++count < 5) {
270                         printk("kmalloc called nonatomically from interrupt %p\n",
271                                __builtin_return_address(0));
272                         priority = GFP_ATOMIC;
273                 }
274         }

ここから割り込みを禁止して処理が進みます。page変数は最近のSLUBとかを読んでるとstruct pageかな?って思うんですがstruct page_descriptorです。 これはkmalloc用に確保したpageを管理するのに使ってます。そして、この構造体がNULLの場合は、空き領域が無い場合なのでno_bucket_pageラベルにジャンプします。 そうでない場合はpage->firstfreeが次に返すメモリとなります。MF_FREEというのはこの領域を使用していない場合に設定されるようなので、使えると思った領域が実は空いて無かった場合のチェックですね。この場合はnot_free_on_freelistラベルに飛びます。pはstruct block_headerです。

276         save_flags(flags);
277         cli();
278         page = *pg;
279         if (!page)
280                 goto no_bucket_page;
281 
282         p = page->firstfree;
283         if (p->bh_flags != MF_FREE)
284                 goto not_free_on_freelist;

無事に空き領域が見つかった場合はそのままfound_itラベルのところの処理になります。firstfree変数を更新や、空き領域数(nfree)を減らしたりなどなどです。 returnするときはsizeof(struct block_header)の分を飛ばして、ヘッダーより後のアドレスを返しています。

286 found_it:
287         page->firstfree = p->bh_next;
288         page->nfree--;
289         if (!page->nfree)
290                 *pg = page->next;
291         restore_flags(flags);
292         bucket->nmallocs++;
293         bucket->nbytesmalloced += size;
294         p->bh_flags = type;     /* As of now this block is officially in use */
295         p->bh_length = size;
296 #if CONFIG_SADISTIC_KMALLOC
297         memset(p+1, 0xf0, size);
298 #endif
299         return p + 1;           /* Pointer arithmetic: increments past header */

次はno_bucket_pageラベルのところです。ここはpageを確保する程度です。pageはstruct pageではなくてstruct page_descriptorです。get_kmalloc_pages()はバディアローケータからページを確保して返しますが、受け取る方はstruct page_descriptor *で受け取ってるだけです。ページが確保できなければno_free_pageラベルにジャンプします。

302 no_bucket_page:
303         /*
304          * If we didn't find a page already allocated for this
305          * bucket size, we need to get one..
306          *
307          * This can be done with ints on: it is private to this invocation
308          */
309         restore_flags(flags);
310 
311         {
312                 int i, sz;
313                 
314                 /* sz is the size of the blocks we're dealing with */
315                 sz = BLOCKSIZE(order);
316 
317                 page = get_kmalloc_pages(priority, bucket->gfporder, dma);
318                 if (!page)
319                         goto no_free_page;

ページを確保できた場合はfound_cached_pageラベルのところの処理に入ります。ここでは確保したページに対してkmallocで返す領域のヘッダーを設定していく感じですね。

320 found_cached_page:
321 
322                 bucket->npages++;
323 
324                 page->order = order;
325                 /* Loop for all but last block: */
326                 i = (page->nfree = bucket->nblocks) - 1;
327                 p = BH(page + 1);
328                 while (i > 0) {
329                         i--;
330                         p->bh_flags = MF_FREE;
331                         p->bh_next = BH(((long) p) + sz);
332                         p = p->bh_next;
333                 }
334                 /* Last block: */
335                 p->bh_flags = MF_FREE;
336                 p->bh_next = NULL;
337 
338                 p = BH(page+1);
339         }

ヘッダーの設定が終わったら、found_itラベルにジャンプします。

341         /*
342          * Now we're going to muck with the "global" freelist
343          * for this size: this should be uninterruptible
344          */
345         cli();
346         page->next = *pg;
347         *pg = page;
348         goto found_it;

ページを確保できなかった場合は、kmalloc_cacheというキャッシュにページがあるか調べて、あればそのページを使います。このキャッシュはkfree時に作ってるようです。条件としてはorder < 3で、DMA領域以外の場合みたいです。キャッシュも無ければ諦めます/(^o^)\

351 no_free_page:
352         /*
353          * No free pages, check the kmalloc cache of
354          * pages to see if maybe we have something available
355          */
356         if (!dma && order < MAX_CACHE_ORDER) {
357                 page = xchg(kmalloc_cache+order, page);
358                 if (page)
359                         goto found_cached_page;
360         }
361         {
362                 static unsigned long last = 0;
363                 if (priority != GFP_BUFFER && priority != GFP_IO &&
364                     (last + 10 * HZ < jiffies)) {
365                         last = jiffies;
366                         printk("Couldn't get a free page.....\n");
367                 }
368                 return NULL;
369         }
370 

最後はnot_free_on_freelistラベルです。空いていると思った領域が空いてないのはおかしいのでエラーメッセージを出してからNULLを返します。

371 not_free_on_freelist:
372         restore_flags(flags);
373         printk("Problem: block on freelist at %08lx isn't free.\n", (long) p);
374         return NULL;
375 }

というわけで、2.0.40のkmalloc()を見てみました。この記事だと新しいほうの解説はほとんどしてないですけども、昔のカーネルと今のカーネルを比較しながら読んでみるのも面白いと思います。

改訂3版 Linuxエンジニア養成読本 (Software Design plus)

改訂3版 Linuxエンジニア養成読本 (Software Design plus)

Linux 4.7の新機能「perf trace calls stack」めも

kernelnewbies.orgのLinux Changesを見てて良さげだなと思った「perf trace calls stack」のめもを。

基本的な使い方は説明が書かれているので、

perfでread、open、closeシステムコールを指定する場合。

masami@saga:~/tmp$ sudo perf trace -e read,open,close cat test.c
#include <stdio.h>

static void print_args(char **argv)
{
        while (*argv) {
                printf("%s\n", *argv++);
        }
}

int main(int argc, char **argv)
{
        print_args(argv);
        return 0;
}
     0.019 ( 0.019 ms): cat/8165 open(filename: 0xf9f9fc78, flags: CLOEXEC                             ) = 3
     0.038 ( 0.002 ms): cat/8165 close(fd: 3                                                           ) = 0
     0.065 ( 0.011 ms): cat/8165 open(filename: 0xfa1a7ed0, flags: CLOEXEC                             ) = 3
     0.070 ( 0.003 ms): cat/8165 read(fd: 3, buf: 0x7fff4703af68, count: 832                           ) = 832
     0.164 ( 0.002 ms): cat/8165 close(fd: 3                                                           ) = 0
     0.447 ( 0.015 ms): cat/8165 open(filename: 0xf9d4b910, flags: CLOEXEC                             ) = 3
     0.474 ( 0.002 ms): cat/8165 close(fd: 3                                                           ) = 0
     0.531 ( 0.012 ms): cat/8165 open(filename: 0x4703cd0b, mode: ISGID|0xfffe0000                     ) = 3
     0.563 ( 0.005 ms): cat/8165 read(fd: 3, buf: 0x7f29fa184000, count: 131072                        ) = 174
     0.581 ( 0.002 ms): cat/8165 read(fd: 3, buf: 0x7f29fa184000, count: 131072                        ) = 0
     0.594 ( 0.002 ms): cat/8165 close(fd: 3                                                           ) = 0
     0.608 ( 0.002 ms): cat/8165 close(fd: 1                                                           ) = 0
     0.612 ( 0.002 ms): cat/8165 close(fd: 2                                                           ) = 0

traceコマンドの場合。

masami@saga:~/tmp$ sudo trace --call dwarf cat ./test.c
#include <stdio.h>

static void print_args(char **argv)
{
        while (*argv) {
                printf("%s\n", *argv++);
        }
}

int main(int argc, char **argv)
{
        print_args(argv);
        return 0;
}
     0.066 ( 0.003 ms): cat/10174 brk(                                                                  ) = 0x13f0000
                                       brk+0x9 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x2c5 (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.106 ( 0.013 ms): cat/10174 access(filename: 0x82769f80, mode: R                                  ) = -1 ENOENT No such file or directory
                                       access+0x7 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1650 (/usr/lib/ld-2.23.so)
     0.130 ( 0.014 ms): cat/10174 open(filename: 0x82767c78, flags: CLOEXEC                             ) = 3
                                       open64+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_read_whole_file+0x26 (/usr/lib/ld-2.23.so)
                                       _dl_load_cache_lookup+0x2d6 (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x550 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.138 ( 0.003 ms): cat/10174 fstat(fd: 3, statbuf: 0x7ffc46e20da0                                  ) = 0
                                       _fxstat+0x14 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_read_whole_file+0x3b (/usr/lib/ld-2.23.so)
                                       _dl_load_cache_lookup+0x2d6 (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x550 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.157 ( 0.015 ms): cat/10174 mmap(len: 158913, prot: READ, flags: PRIVATE, fd: 3                   ) = 0x7f6882946000
                                       mmap64+0x3a (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_read_whole_file+0x85 (/usr/lib/ld-2.23.so)
                                       _dl_load_cache_lookup+0x2d6 (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x550 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.163 ( 0.002 ms): cat/10174 close(fd: 3                                                           ) = 0
                                       close+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_read_whole_file+0x5b (/usr/lib/ld-2.23.so)
                                       _dl_load_cache_lookup+0x2d6 (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x550 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.192 ( 0.011 ms): cat/10174 open(filename: 0x8296fed0, flags: CLOEXEC                             ) = 3
                                       open64+0x7 (/usr/lib/ld-2.23.so)
                                       open_verify.constprop.7+0x58 (/usr/lib/ld-2.23.so)
     0.199 ( 0.003 ms): cat/10174 read(fd: 3, buf: 0x7ffc46e20f48, count: 832                           ) = 832
                                       read+0x7 (/usr/lib/ld-2.23.so)
                                       open_verify.constprop.7+0x9c (/usr/lib/ld-2.23.so)
     0.206 ( 0.002 ms): cat/10174 fstat(fd: 3, statbuf: 0x7ffc46e20de0                                  ) = 0
                                       _fxstat+0x14 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_from_fd+0x5c (/usr/lib/ld-2.23.so)
     0.219 ( 0.009 ms): cat/10174 mmap(len: 4096, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS, fd: -1    ) = 0x7f6882945000
                                       mmap64+0x3a (/usr/lib/ld-2.23.so)
                                       __libc_memalign+0x7e (/usr/lib/ld-2.23.so)
                                       _dl_new_object+0x86 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_from_fd+0x16d (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x211 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.249 ( 0.015 ms): cat/10174 mmap(len: 3803440, prot: EXEC|READ, flags: PRIVATE|DENYWRITE, fd: 3   ) = 0x7f68823a9000
                                       mmap64+0x3a (/usr/lib/ld-2.23.so)
                                       _dl_map_object_from_fd+0x533 (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x211 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.276 ( 0.023 ms): cat/10174 mprotect(start: 0x7f6882540000, len: 2097152                          ) = 0
                                       mprotect+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_from_fd+0xdf1 (/usr/lib/ld-2.23.so)
     0.302 ( 0.021 ms): cat/10174 mmap(addr: 0x7f6882740000, len: 24576, prot: READ|WRITE, flags: PRIVATE|DENYWRITE|FIXED, fd: 3, off: 1667072) = 0x7f6882740000
                                       mmap64+0x3a (/usr/lib/ld-2.23.so)
                                       _dl_map_object_from_fd+0xe5a (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x211 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.326 ( 0.009 ms): cat/10174 mmap(addr: 0x7f6882746000, len: 14640, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS|FIXED, fd: -1) = 0x7f6882746000
                                       mmap64+0x3a (/usr/lib/ld-2.23.so)
                                       _dl_map_object_from_fd+0xf37 (/usr/lib/ld-2.23.so)
                                       _dl_map_object+0x211 (/usr/lib/ld-2.23.so)
                                       openaux+0x30 (/usr/lib/ld-2.23.so)
                                       _dl_catch_error+0x74 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_deps+0x275 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1699 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.338 ( 0.002 ms): cat/10174 close(fd: 3                                                           ) = 0
                                       close+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_map_object_from_fd+0x9d2 (/usr/lib/ld-2.23.so)
     0.364 ( 0.005 ms): cat/10174 mmap(len: 4096, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS, fd: -1    ) = 0x7f6882944000
                                       mmap64+0x3a (/usr/lib/ld-2.23.so)
                                       __libc_memalign+0x7e (/usr/lib/ld-2.23.so)
                                       _dl_allocate_tls_storage+0x1c (/usr/lib/ld-2.23.so)
                                       init_tls+0xd0 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1cb3 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.376 ( 0.004 ms): cat/10174 mmap(len: 4096, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS, fd: -1    ) = 0x7f6882943000
                                       mmap64+0x3a (/usr/lib/ld-2.23.so)
                                       __libc_memalign+0x7e (/usr/lib/ld-2.23.so)
                                       allocate_dtv+0x22 (/usr/lib/ld-2.23.so)
                                       _dl_allocate_tls_storage+0x62 (/usr/lib/ld-2.23.so)
                                       init_tls+0xd0 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1cb3 (/usr/lib/ld-2.23.so)
                                       _dl_sysdep_start+0x40c (/usr/lib/ld-2.23.so)
                                       _dl_start+0x288 (/usr/lib/ld-2.23.so)
                                       _dl_start_user+0x0 (/usr/lib/ld-2.23.so)
     0.385 ( 0.002 ms): cat/10174 arch_prctl(option: 4098, arg2: 140086844081920, arg3: 140086844076048, arg4: 34, arg5: -1) = 0
                                       init_tls+0x118 (/usr/lib/ld-2.23.so)
                                       dl_main+0x1cb3 (/usr/lib/ld-2.23.so)
     0.474 ( 0.021 ms): cat/10174 mprotect(start: 0x7f6882740000, len: 16384, prot: READ                ) = 0
                                       mprotect+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_protect_relro+0x3d (/usr/lib/ld-2.23.so)
                                       _dl_relocate_object+0x32a (/usr/lib/ld-2.23.so)
     0.498 ( 0.015 ms): cat/10174 mprotect(start: 0x60b000, len: 4096, prot: READ                       ) = 0
                                       mprotect+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_protect_relro+0x3d (/usr/lib/ld-2.23.so)
                                       _dl_relocate_object+0x32a (/usr/lib/ld-2.23.so)
     0.525 ( 0.019 ms): cat/10174 mprotect(start: 0x7f688296d000, len: 4096, prot: READ                 ) = 0
                                       mprotect+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_protect_relro+0x3d (/usr/lib/ld-2.23.so)
                                       _dl_relocate_object+0x32a (/usr/lib/ld-2.23.so)
     0.542 ( 0.011 ms): cat/10174 munmap(addr: 0x7f6882946000, len: 158913                              ) = 0
                                       munmap+0x7 (/usr/lib/ld-2.23.so)
                                       _dl_unload_cache+0x28 (/usr/lib/ld-2.23.so)
                                       dl_main+0x21db (/usr/lib/ld-2.23.so)
no symbols found in /usr/bin/cat, maybe install a debug package?
     0.653 ( 0.002 ms): cat/10174 brk(                                                                  ) = 0x13f0000
                                       __brk+0x9 (/usr/lib/libc-2.23.so)
                                       __sbrk+0x5f (/usr/lib/libc-2.23.so)
                                       __default_morecore+0x9 (/usr/lib/libc-2.23.so)
                                       sysmalloc+0x3ef (/usr/lib/libc-2.23.so)
                                       _int_malloc+0x80d (/usr/lib/libc-2.23.so)
                                       malloc+0x54 (/usr/lib/libc-2.23.so)
                                       _nl_normalize_codeset+0x6a (/usr/lib/libc-2.23.so)
                                       _nl_load_locale_from_archive+0x352 (/usr/lib/libc-2.23.so)
                                       _nl_find_locale+0x43a (/usr/lib/libc-2.23.so)
                                       setlocale+0x48f (/usr/lib/libc-2.23.so)
                                       [0x1a51] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.668 ( 0.009 ms): cat/10174 brk(brk: 0x1411000                                                    ) = 0x1411000
                                       __brk+0x9 (/usr/lib/libc-2.23.so)
                                       __sbrk+0x79 (/usr/lib/libc-2.23.so)
                                       __default_morecore+0x9 (/usr/lib/libc-2.23.so)
                                       sysmalloc+0x3ef (/usr/lib/libc-2.23.so)
                                       _int_malloc+0x80d (/usr/lib/libc-2.23.so)
                                       malloc+0x54 (/usr/lib/libc-2.23.so)
                                       _nl_normalize_codeset+0x6a (/usr/lib/libc-2.23.so)
                                       _nl_load_locale_from_archive+0x352 (/usr/lib/libc-2.23.so)
                                       _nl_find_locale+0x43a (/usr/lib/libc-2.23.so)
                                       setlocale+0x48f (/usr/lib/libc-2.23.so)
                                       [0x1a51] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.706 ( 0.020 ms): cat/10174 open(filename: 0x82513910, flags: CLOEXEC                             ) = 3
                                       _nl_load_locale_from_archive+0x419 (/usr/lib/libc-2.23.so)
     0.713 ( 0.002 ms): cat/10174 fstat(fd: 3, statbuf: 0x7f6882745960                                  ) = 0
                                       __fxstat64+0x14 (/usr/lib/libc-2.23.so)
                                       _nl_load_locale_from_archive+0x443 (/usr/lib/libc-2.23.so)
     0.735 ( 0.016 ms): cat/10174 mmap(len: 2704400, prot: READ, flags: PRIVATE, fd: 3                  ) = 0x7f6882114000
                                       __mmap64+0x3a (/usr/lib/libc-2.23.so)
                                       _nl_load_locale_from_archive+0x46d (/usr/lib/libc-2.23.so)
                                       _nl_find_locale+0x43a (/usr/lib/libc-2.23.so)
                                       setlocale+0x48f (/usr/lib/libc-2.23.so)
                                       [0x1a51] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.748 ( 0.002 ms): cat/10174 close(fd: 3                                                           ) = 0
                                       _nl_load_locale_from_archive+0x4c6 (/usr/lib/libc-2.23.so)
     0.803 ( 0.003 ms): cat/10174 fstat(fd: 1, statbuf: 0x7ffc46e215d0                                  ) = 0
                                       __fxstat64+0x14 (/usr/lib/libc-2.23.so)
                                       [0x1bc7] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.823 ( 0.013 ms): cat/10174 open(filename: 0x46e22d12, mode: ISGID|0xfffe0000                     ) = 3
                                       __GI___libc_open+0x10 (/usr/lib/libc-2.23.so)
                                       [0x1c88] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.830 ( 0.002 ms): cat/10174 fstat(fd: 3, statbuf: 0x7ffc46e215d0                                  ) = 0
                                       __fxstat64+0x14 (/usr/lib/libc-2.23.so)
                                       [0x1cae] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.839 ( 0.002 ms): cat/10174 fadvise64(fd: 3, advice: 2                                            ) = 0
                                       posix_fadvise+0xd (/usr/lib/libc-2.23.so)
                                       [0x1cf1] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.855 ( 0.010 ms): cat/10174 mmap(len: 139264, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS, fd: -1  ) = 0x7f688294b000
                                       __mmap64+0x3a (/usr/lib/libc-2.23.so)
                                       sysmalloc+0x87e (/usr/lib/libc-2.23.so)
                                       _int_malloc+0x80d (/usr/lib/libc-2.23.so)
                                       malloc+0x54 (/usr/lib/libc-2.23.so)
                                       [0x5ed9] (/usr/bin/cat)
                                       [0x2412] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.877 ( 0.005 ms): cat/10174 read(fd: 3, buf: 0x7f688294c000, count: 131072                        ) = 174
                                       __GI___libc_read+0x10 (/usr/lib/libc-2.23.so)
                                       [0x5286] (/usr/bin/cat)
                                       [0x2463] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.900 ( 0.017 ms): cat/10174 write(fd: 1, buf: 0x7f688294c000, count: 174                          ) = 174
                                       __GI___libc_write+0x10 (/usr/lib/libc-2.23.so)
                                       [0x52e6] (/usr/bin/cat)
                                       [0x2c1e] (/usr/bin/cat)
                                       [0x2449] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.908 ( 0.002 ms): cat/10174 read(fd: 3, buf: 0x7f688294c000, count: 131072                        ) = 0
                                       __GI___libc_read+0x10 (/usr/lib/libc-2.23.so)
                                       [0x5286] (/usr/bin/cat)
                                       [0x2463] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.924 ( 0.010 ms): cat/10174 munmap(addr: 0x7f688294b000, len: 139264                              ) = 0
                                       __GI___munmap+0x7 (/usr/lib/libc-2.23.so)
                                       [0x21cd] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.932 ( 0.002 ms): cat/10174 close(fd: 3                                                           ) = 0
                                       __GI___libc_close+0x10 (/usr/lib/libc-2.23.so)
                                       [0x21e4] (/usr/bin/cat)
                                       __libc_start_main+0xf1 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.950 ( 0.002 ms): cat/10174 close(fd: 1                                                           ) = 0
                                       _IO_file_close+0xb (/usr/lib/libc-2.23.so)
                                       _IO_file_close_it@@GLIBC_2.2.5+0x120 (/usr/lib/libc-2.23.so)
                                       fclose@@GLIBC_2.2.5+0x19f (/usr/lib/libc-2.23.so)
                                       [0x61fa] (/usr/bin/cat)
                                       [0x2b02] (/usr/bin/cat)
                                       __run_exit_handlers+0xe8 (/usr/lib/libc-2.23.so)
                                       [0x35c35] (/usr/lib/libc-2.23.so)
                                       __libc_start_main+0xf8 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.959 ( 0.002 ms): cat/10174 close(fd: 2                                                           ) = 0
                                       _IO_file_close+0xb (/usr/lib/libc-2.23.so)
                                       _IO_file_close_it@@GLIBC_2.2.5+0x120 (/usr/lib/libc-2.23.so)
                                       fclose@@GLIBC_2.2.5+0x19f (/usr/lib/libc-2.23.so)
                                       [0x61fa] (/usr/bin/cat)
                                       [0x2b25] (/usr/bin/cat)
                                       __run_exit_handlers+0xe8 (/usr/lib/libc-2.23.so)
                                       [0x35c35] (/usr/lib/libc-2.23.so)
                                       __libc_start_main+0xf8 (/usr/lib/libc-2.23.so)
                                       [0x25d9] (/usr/bin/cat)
     0.966 ( 0.000 ms): cat/10174 exit_group(                                                           )

Working With Unix Processes (English Edition)

Working With Unix Processes (English Edition)

linux 4.7でライブパッチ用のカーネルモジュールにelfのセクションチェックが入ったのでめも

Linux 4.7からライブパッチ用のカーネルモジュールをロードする時にELFのセクションをチェックするようになりました。

このチェックが入ったこととで、今までのモジュールを再コンパイルするだけだと以下のようなエラーになってロードに失敗します。

[  153.824961] livepatch: module livetest is not marked as a livepatch module
[  153.826718] livetest: failed to register patch

これはkernel/livepatch/core.cのklp_resolve_symbols()という関数でチェックしています。

233         for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) {
234                 sym = pmod->core_kallsyms.symtab + ELF_R_SYM(relas[i].r_info);
235                 if (sym->st_shndx != SHN_LIVEPATCH) {
236                         pr_err("symbol %s is not marked as a livepatch symbol",
237                                strtab + sym->st_name);
238                         return -EINVAL;
239                 }

SHN_LIVEPATCHはinclude/uapi/linux/elf.hで定義されています。 これをどうやって設定するかですが、livepatch: reuse module loader code to write relocations · torvalds/linux@425595a · GitHubを見ると、

MODULE_INFO(livepatch, "Y");

こんな行が追加されているので、自分のモジュールにも追加します。そして再度コンパイルすればロードできるようになります。

動くメカニズムを図解&実験! Linux超入門 (My Linuxシリーズ)

動くメカニズムを図解&実験! Linux超入門 (My Linuxシリーズ)