vmcoreの解析メモ (NFSv4編)

vmcoreの解析に関するメモは、あまり公開されていないので、今回NFS関連で役に立つかもしれない方法を紹介していきます。

まず、モジュールをロードしておきます。

crash> mod -s nfs
crash> mod -s nfsv4

これをしないと、nfs_server構造体をチェックしたいときなど、次のようなエラーが発生します。
No struct type named nfs_server.

次にsuper_blockからnfsのポインタを探していきます。ここでは、nfs_serverとnfs_clientのアドレスをチェックしてみます。
マクロに設定しておくと、使い回しができるのでちょっと便利です。

crash> mount
MOUNT SUPERBLK TYPE DEVNAME DIRNAME
...
ffff88007b817200 ffff880035a83000 nfs4 192.168.122.135:/tmp /mnt

crash> cat ~/macro/nfs-pointers
define superblk-to-nfs-pointers
set $sb = (struct super_block *)$arg0
set $nfs_server = (struct nfs_server *)($sb->s_fs_info)
set $nfs_client = $nfs_server->nfs_client
print $sb
print $nfs_server
print $nfs_client
end
crash> source /root/macro/nfs-pointers
Redefine command "superblk-to-nfs-pointers"? (y or n) [answered Y; input not from terminal]
crash> superblk-to-nfs-pointers 0xffff880035a83000
$16 = (struct super_block *) 0xffff880035a83000
$17 = (struct nfs_server *) 0xffff880035a84800
$18 = (struct nfs_client *) 0xffff88007a313c00

 nfs_clientのアドレスがわかったので、実際にどんな値が入っているか確認することができます。

crash> struct nfs_client 0xffff88007a313c00
struct nfs_client {
cl_count = {
counter = 0x1
},
... (長いので省略します。)
cl_hostname = 0xffff88007a3076d0 "192.168.122.135",
cl_rpcclient = 0xffff880035db5400,
rpc_ops = 0xffffffffa0636160 <nfs_v4_clientops>,
cl_proto = 0x6,
cl_nfs_mod = 0xffffffffa065fdc0 <nfs_v4>,
...
cl_serverowner = 0xffff880035a87000,
cl_serverscope = 0xffff880035a85000,
cl_implid = 0xffff880035e96000,
cl_sp4_flags = 0x0,
cl_ipaddr = "192.168.122.225\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
fscache = 0xffff880035a09058,
cl_net = 0xffffffff81a44000 <init_net>
}

cl_rpcclientのアドレスがわかったので、rpc_clntの値も確認できます。

crash> struct rpc_clnt 0xffff880035db5400
struct rpc_clnt {
cl_count = {
counter = 0x2
},
cl_clid = 0x1,
cl_clients = {
next = 0xffff88007bd93208,
...
cl_xprt = 0xffff880035a81800,

...

また、cl_xprtのアドレスがわかったので、rpc_xprtの値も確認できます。「どうして?」って思われる方もいるかもしれないので、関連するソースコードを紹介します。

 include/linux/sunrpc/clnt.h:
...
struct rpc_clnt {
atomic_t cl_count; /* Number of references */
unsigned int cl_clid; /* client id */
struct list_head cl_clients; /* Global list of clients */
struct list_head cl_tasks; /* List of tasks */
spinlock_t cl_lock; /* spinlock */
struct rpc_xprt __rcu * cl_xprt; /* transport */

crash> struct rpc_xprt 0xffff880035a81800
struct rpc_xprt {
count = {
counter = 0x2
},
ops = 0xffffffffa0330300,
timeout = 0xffffffffa0325fa0,
...
state = 0x12,
...
xprt_net = 0xffffffff81a44000 <init_net>,
servername = 0xffff88007a3071a0 "192.168.122.135",
address_strings = {0xffff88007a3079b0 "192.168.122.135", 0xffff88007a0fde60 "2049", 0xffffffffa0327fe8 "tcp", 0xffff88007a3076f0 "c0a87a87", 0xffff88007a0fde58 " 801", 0xffffffffa0327fe8 "tcp"},
debugfs = 0xffff880069800f00
}
 

例えば、 stateのビットを確認すると接続されているかどうかチェックすることもできます。

crash> eval -b 0x12|grep bits
bits set: 4 1

該当するソースコードは、こちらになります。

include/linux/sunrpc/xprt.h:
...
#define XPRT_LOCKED (0)
#define XPRT_CONNECTED (1)
#define XPRT_CONNECTING (2)
#define XPRT_CLOSE_WAIT (3)
#define XPRT_BOUND (4)
#define XPRT_BINDING (5)
#define XPRT_CLOSING (6)
#define XPRT_CONNECTION_ABORT (7)
#define XPRT_CONNECTION_CLOSE (8)
#define XPRT_CONGESTED (9)
#define XPRT_CONNECTION_REUSE (10)

static inline void xprt_set_connected(struct rpc_xprt *xprt)
{
set_bit(XPRT_CONNECTED, &xprt->state);
}

つまり、bit 1がセットされているので、接続されていることがわかります。