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
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
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]