Linuxのnet namespaceにpernet_listというpernet_operation構造体のリストがあって、これがどのように設定されてるのかのめもです。 読んでるカーネルのバージョンは4.1です。
pernet_listを使ってるところは以前書いたこちらの記事にあります。 kernhack.hatenablog.com
で、このリストに要素を設定するのはどこかというとregister_pernet_device()で、kvmで動かしているarch linux(カーネルはlinux 4.6.0-rc3)で確認したところ、この関数が呼ばれるのはブートのタイミングでは3回でした。カーネルのinit処理のタイミングなのでWARN_ON()を入れて確認してます。呼ばれたのは3回で、呼ばれ方としては2パターンでした。
最初と2回目はnet_dev_init()から呼ばれてます。
Apr 17 20:49:08 kerntest kernel: [<ffffffff812e90f1>] dump_stack+0x63/0x82 Apr 17 20:49:08 kerntest kernel: [<ffffffff8107a65b>] __warn+0xcb/0xf0 Apr 17 20:49:08 kerntest kernel: [<ffffffff81b5f357>] ? netdev_boot_setup+0xef/0xef Apr 17 20:49:08 kerntest kernel: [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20 Apr 17 20:49:08 kerntest kernel: [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80 Apr 17 20:49:08 kerntest kernel: [<ffffffff81b5f4b4>] net_dev_init+0x15d/0x1b1 Apr 17 20:49:08 kerntest kernel: [<ffffffff81002123>] do_one_initcall+0xb3/0x200 Apr 17 20:49:08 kerntest kernel: [<ffffffff81099055>] ? parse_args+0x295/0x4b0 Apr 17 20:49:08 kerntest kernel: [<ffffffff81b0c194>] kernel_init_freeable+0x176/0x215 Apr 17 20:49:08 kerntest kernel: [<ffffffff815b038e>] kernel_init+0xe/0x100 Apr 17 20:49:08 kerntest kernel: [<ffffffff815bda42>] ret_from_fork+0x22/0x40 Apr 17 20:49:08 kerntest kernel: [<ffffffff815b0380>] ? rest_init+0x90/0x90
Apr 17 20:49:08 kerntest kernel: [<ffffffff812e90f1>] dump_stack+0x63/0x82 Apr 17 20:49:08 kerntest kernel: [<ffffffff8107a65b>] __warn+0xcb/0xf0 Apr 17 20:49:08 kerntest kernel: [<ffffffff81b5f357>] ? netdev_boot_setup+0xef/0xef Apr 17 20:49:08 kerntest kernel: [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20 Apr 17 20:49:08 kerntest kernel: [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80 Apr 17 20:49:08 kerntest kernel: [<ffffffff81b5f4c8>] net_dev_init+0x171/0x1b1 Apr 17 20:49:08 kerntest kernel: [<ffffffff81002123>] do_one_initcall+0xb3/0x200 Apr 17 20:49:08 kerntest kernel: [<ffffffff81099055>] ? parse_args+0x295/0x4b0 Apr 17 20:49:08 kerntest kernel: [<ffffffff81b0c194>] kernel_init_freeable+0x176/0x215 Apr 17 20:49:08 kerntest kernel: [<ffffffff815b038e>] kernel_init+0xe/0x100 Apr 17 20:49:08 kerntest kernel: [<ffffffff815bda42>] ret_from_fork+0x22/0x40 Apr 17 20:49:08 kerntest kernel: [<ffffffff815b0380>] ? rest_init+0x90/0x90
3回目はwifiのcfg80211モジュールの初期化っぽいです。
Apr 17 20:49:08 kerntest kernel: [<ffffffff812e90f1>] dump_stack+0x63/0x82 Apr 17 20:49:08 kerntest kernel: [<ffffffff8107a65b>] __warn+0xcb/0xf0 Apr 17 20:49:08 kerntest kernel: [<ffffffffa0579000>] ? 0xffffffffa0579000 Apr 17 20:49:08 kerntest kernel: [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20 Apr 17 20:49:08 kerntest kernel: [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80 Apr 17 20:49:08 kerntest kernel: [<ffffffffa0579011>] cfg80211_init+0x11/0xd6 [cfg80211] Apr 17 20:49:08 kerntest kernel: [<ffffffff81002123>] do_one_initcall+0xb3/0x200 Apr 17 20:49:08 kerntest kernel: [<ffffffff811d642f>] ? kmem_cache_alloc_trace+0x1bf/0x230 Apr 17 20:49:08 kerntest kernel: [<ffffffff8116fcd2>] do_init_module+0x5f/0x1ed Apr 17 20:49:08 kerntest kernel: [<ffffffff8110556c>] load_module+0x227c/0x2900 Apr 17 20:49:08 kerntest kernel: [<ffffffff81102560>] ? symbol_put_addr+0x50/0x50 Apr 17 20:49:08 kerntest kernel: [<ffffffff815b373e>] ? kmemleak_alloc+0x4e/0xb0 Apr 17 20:49:08 kerntest kernel: [<ffffffff81105d43>] SyS_init_module+0x153/0x1a0 Apr 17 20:49:08 kerntest kernel: [<ffffffff815bd832>] entry_SYSCALL_64_fastpath+0x1a/0xa4
net_dev_init()から呼ばれるほうはこのようにループバックデバイスとデフォルトデバイスの設定をしてます。
7498 /* The loopback device is special if any other network devices 7499 * is present in a network namespace the loopback device must 7500 * be present. Since we now dynamically allocate and free the 7501 * loopback device ensure this invariant is maintained by 7502 * keeping the loopback device as the first device on the 7503 * list of network devices. Ensuring the loopback devices 7504 * is the first device that appears and the last network device 7505 * that disappears. 7506 */ 7507 if (register_pernet_device(&loopback_net_ops)) 7508 goto out; 7509 7510 if (register_pernet_device(&default_device_ops)) 7511 goto out;
デフォルトデバイスのほうはコンストラクタはなくて終了時の処理のみのようです。
7438 static struct pernet_operations __net_initdata default_device_ops = { 7439 .exit = default_device_exit, 7440 .exit_batch = default_device_exit_batch, 7441 };
register_pernet_device()はこんな感じで、主だったことはregister_pernet_operations()がやってます。
889 int register_pernet_device(struct pernet_operations *ops) 890 { 891 int error; 892 mutex_lock(&net_mutex); 893 error = register_pernet_operations(&pernet_list, ops); 894 if (!error && (first_device == &pernet_list)) 895 first_device = &ops->list; 896 mutex_unlock(&net_mutex); 897 return error; 898 }
register_pernet_operations()はこうです。default_device_opsとかループバックデバイスもidは振ってないようなので、ops->idが設定されてないのでここは飛ばします。で、__register_pernet_operations()です。
788 static int register_pernet_operations(struct list_head *list, 789 struct pernet_operations *ops) 790 { 791 int error; 792 793 if (ops->id) { 794 again: 795 error = ida_get_new_above(&net_generic_ids, 1, ops->id); 796 if (error < 0) { 797 if (error == -EAGAIN) { 798 ida_pre_get(&net_generic_ids, GFP_KERNEL); 799 goto again; 800 } 801 return error; 802 } 803 max_gen_ptrs = max_t(unsigned int, max_gen_ptrs, *ops->id); 804 } 805 error = __register_pernet_operations(list, ops); 806 if (error) { 807 rcu_barrier(); 808 if (ops->id) 809 ida_remove(&net_generic_ids, *ops->id); 810 } 811 812 return error; 813 }
pernet_operations構造体の登録は登録だけじゃなくて、必要に応じてinit()の実行もしています。まず最初にpernet_listの末尾に追加します。そして、init()がせて治されてops_init()がエラーを返した場合の後処理時に使用します。
730 static int __register_pernet_operations(struct list_head *list, 731 struct pernet_operations *ops) 732 { 733 struct net *net; 734 int error; 735 LIST_HEAD(net_exit_list); 736 737 list_add_tail(&ops->list, list); 738 if (ops->init || (ops->id && ops->size)) { 739 for_each_net(net) { 740 error = ops_init(ops, net); 741 if (error) 742 goto out_undo; 743 list_add_tail(&net->exit_list, &net_exit_list); 744 } 745 } 746 return 0; 747 748 out_undo: 749 /* If I have an error cleanup all namespaces I initialized */ 750 list_del(&ops->list); 751 ops_exit_list(ops, &net_exit_list); 752 ops_free_list(ops, &net_exit_list); 753 return error; 754 }
最後にops_init()です。先の流れだとops->id && ops->sizeは偽になります。そして、init()が設定されている場合は実行します。
93 static int ops_init(const struct pernet_operations *ops, struct net *net) 94 { 95 int err = -ENOMEM; 96 void *data = NULL; 97 98 if (ops->id && ops->size) { 99 data = kzalloc(ops->size, GFP_KERNEL); 100 if (!data) 101 goto out; 102 103 err = net_assign_generic(net, *ops->id, data); 104 if (err) 105 goto cleanup; 106 } 107 err = 0; 108 if (ops->init) 109 err = ops->init(net); 110 if (!err) 111 return 0; 112 113 cleanup: 114 kfree(data); 115 116 out: 117 return err; 118 }
さて、これだとnet/ipv4/tcp_ipv4.cにあるtcp4_net_opsが登録されないため、tcp4_proc_net_init()が呼ばれません。では、これらはどうやって登録されているかというと、register_pernet_subsys()を使っています。
2327 int __init tcp4_proc_init(void) 2328 { 2329 return register_pernet_subsys(&tcp4_net_ops); 2330 }
register_pernet_subsys()はこのようになっていて、register_pernet_operations()よりシンプルになってます。
843 int register_pernet_subsys(struct pernet_operations *ops) 844 { 845 int error; 846 mutex_lock(&net_mutex); 847 error = register_pernet_operations(first_device, ops); 848 mutex_unlock(&net_mutex); 849 return error; 850 }
register_pernet_operations()に渡すリストはfirst_deviceというリストでnet/net_namespce.cの冒頭で宣言されています。初期値はpernet_listを指しています。
29 static struct list_head *first_device = &pernet_list;
実際のところ、register_pernet_subsys()を使ってるところのほうが多いみたいです。
大体arch linuxの設定に近いはずのうちのカーネルで、ops_init()でops->init()により呼ばれた関数はこれらです。
ffffffff81272410 t proc_net_ns_init ffffffff814a9ce0 t net_ns_net_init ffffffff815abc20 t sysctl_net_init ffffffff814ec0c0 t netfilter_net_init ffffffff814ec600 t nf_log_net_init ffffffff8149c010 t sock_inuse_init_net ffffffff814e6610 t netlink_net_init ffffffff814c81e0 t rtnetlink_net_init ffffffff812ecef0 t uevent_net_init ffffffff8149c0f0 t proto_init_net ffffffff814d4140 t dev_proc_net_init ffffffff814d40b0 t dev_mc_net_init ffffffff814b0040 t netdev_init ffffffff8144ab50 t loopback_net_init ffffffff814d5d70 t fib_rules_net_init ffffffff814e0040 t psched_net_init ffffffff814ea8d0 t genl_pernet_init ffffffff815a4b80 t wext_pernet_init ffffffff814acc90 t sysctl_core_net_init ffffffff815279a0 t arp_net_init ffffffff8152c720 t devinet_init_net ffffffff815356c0 t fib_net_init ffffffff814eeae0 t ip_rt_do_proc_init ffffffff8154f050 t xfrm_net_init ffffffff8154a830 t xfrm4_net_init ffffffff814ee490 t sysctl_route_net_init ffffffff814edc20 t rt_genid_init ffffffff814ee440 t ipv4_inetpeer_init ffffffff815317c0 t igmp_net_init ffffffff81518880 t tcp_sk_init ffffffff8151d1d0 t tcp_net_metrics_init ffffffff81526000 t udplite4_proc_init_net ffffffff815294e0 t icmp_sk_init ffffffff81545860 t ipmr_net_init ffffffff8152fb50 t inet_init_net ffffffff8152fc40 t ipv4_mib_init_net ffffffff8151fd60 t raw_init_net ffffffff815171d0 t tcp4_proc_init_net ffffffff815232f0 t udp4_proc_init_net ffffffff8153f630 t ping_v4_proc_init_net ffffffff815418b0 t ip_proc_init_net ffffffff814f4280 t ipv4_frags_init_net ffffffff81557b30 t unix_net_init ffffffff814cf230 t diag_net_init ffffffff81540e40 t ipv4_sysctl_init_net ffffffff8155d500 t inet6_net_init ffffffff81581b50 t icmpv6_sk_init ffffffff81594fb0 t ip6mr_net_init ffffffff815795b0 t ndisc_net_init ffffffff81584a00 t igmp6_net_init ffffffff8157ff50 t raw6_init_net ffffffff8157f5c0 t udplite6_proc_init_net ffffffff81599810 t ipv6_proc_init_net ffffffff81564bf0 t if6_proc_net_init ffffffff8156fd20 t ipv6_inetpeer_init ffffffff8156fdb0 t ip6_route_net_init ffffffff815745d0 t fib6_net_init ffffffff81597180 t xfrm6_net_init ffffffff815991f0 t fib6_rules_net_init ffffffff8156fc90 t ip6_route_net_init_late ffffffff81590f20 t ip6_flowlabel_proc_init ffffffff8156ce50 t ip6addrlbl_net_init ffffffff81565d30 t addrconf_init_net ffffffff81588540 t ipv6_frags_init_net ffffffff8158bb80 t tcpv6_net_init ffffffff8158d1b0 t ping_v6_proc_init_net ffffffff815924c0 t ipv6_sysctl_net_init ffffffff8159dcc0 t packet_net_init ffffffffa014f130 t xt_net_init [x_tables] ffffffffa01788a0 t ip_tables_net_init [ip_tables]
動くメカニズムを図解&実験! Linux超入門 (My Linux・シリーズ)
- 作者: 宗像尚郎/海老原祐太郎
- 出版社/メーカー: CQ出版
- 発売日: 2016/04/15
- メディア: 単行本
- この商品を含むブログ (2件) を見る