j2_inodeCacheSize 调优操作和内存 DR 操作的隐藏危险副作用

本文将揭示 j2_inodeCacheSize 调优操作和内存 DR(dynamic reconfiguration,动态重新配置)操作的隐藏的危险副作用。这种有害的副作用可能导致严重的后果,包括 inode 缓存耗尽、文件系统损坏和系统故障。我们将演示并解释这种副作用是如何出现的以及如何预防它。我们还会介绍一些检测和修复这类问题的方法。

Wu Jian Jun, 咨询软件工程师, IBM

/developerworks/i/authors/wjj_64x80.jpgWu Jian Jun 是一名软件工程师,过去五年一直效力于 IBM 公司,此外,他还关注 IBM AIX 开发支持。他从中国浙江大学获得了计算机科学博士学位。



2013 年 9 月 16 日

简介

大小适中的增强日志文件系统 (JFS2) inode 缓存对实现 IBM® AIX® 系统的高性能和稳定性至关重要。通常用户会通过调优 j2_inodeCacheSize 来控制 inode 缓存的最大内存使用。inode 缓存大小也可通过内存动态重新配置 (DR) 操作进行更改。在 AIX 6.1(6100-04 以上)和 AIX 7.1 中,有一种隐藏的副作用,在执行 j2_inodeCacheSize 调优操作或动态逻辑分区 (DLPAR) 内存操作之后,inode 缓存类的最大堆大小只能下调。在本文中,我们将演示这种副作用是如何导致 inode 缓存耗尽的,还会介绍一些处理这类问题的方法。


JFS2 inode 缓存

inode 是 JFS2 的基础结构。每个 inode 拥有一个磁盘上 512 字节 的数据结构。当 inode 在内存中工作时,JFS2 跟踪的不仅仅是磁盘上的字段。核内 inode(包括磁盘上部分和工作部分)目前大约为 1 KB。AIX 内核会缓存所有这些数据来提高性能。

为了预防多个处理器争用,inode 缓存拆分为多个缓存类。AIX 内核为每个处理器创建了两个缓存类,还创建了另外一个缓存类,也就是说,在系统初始化时会创建总计 [n(处理器数)* 2 + 1] 个缓存类。iCacheClassiCache 结构是在 /usr/include/j2/j2_inode.h 中定义的:

typedef struct iCacheClass {
        MUTEXLOCK_T cc_lock;
        int32 cc_nInode;                    /* # of inode in cacheList  */
        CDLL_HEADER(inode) cc_cacheList; /* cacheList header */
        struct pile *cc_pile;             /* inode pile */
        boolean_t pileFull;               /* pile is full */
} iCacheClass_t;

struct iCache {
        int32 nInode;                 /* # of in-memory inode */
        int16 nCacheClass;            /* # of cacheClass */
        struct iCacheClass *cacheTable;
        int32 nInodePerCacheClass;    /* # of inode per cacheClass */
        int32 nHashClass;             /* # of hashClass - 1 */
        int32 nNewHashClass;          /* # of hashClass - 1 */
        int32 nInodePerHashClass;     /* # of inode per hashClass */
        struct iHashClass **hashTable;
        int32 nPagesPerCacheClass; /* # of pile pages per cacheClass */
        int32 nMaxInode;            /* nInode at initialization time */
};

为每个缓存类提供了一个用于 inode 分配的堆:

struct pile {
        eye_catch_t pile_eyec;         /* 8: pile eye-catcher */
        uint32_t flags;             /* 4: guarded by pile_lock */
        uint16_t obj_size;          /* 2: opaque object size */
        uint16_t align;             /* 2: object align (offset mask) */
        uint16_t slab_size;         /* 2: alloc slab size in pages */
        ......
        uint64_t max_total_pages;   /* 8: max total pages, ideally */
        uint64_t min_total_pages;
        uint64_t cur_total_pages;   /* 8: real world value */
        ......      
};

可为一个堆配置最大页数。这个 max_total_pages 字段确定分配多少个 inode 才能将一个堆装满,并开始从缓存列表回收 inode。可强制性缩小堆,也可以扩大它。此过程在内存 DR 和 j2_inodeCacheSize 调节期间执行。


调节 j2_inodeCacheSize

inode 缓存大小可通过使用 ioo 命令更改 j2_inodeCacheSize tunable 来调节。在 AIX 6.1 中,该值默认为 400,在 AIX 7.1 中默认为 200。该值没有明确表明将使用的缓存量,而只是一个缩放比例系数。它可与主要内存的大小结合使用,以确定 inode 缓存的最大内存使用量。目前的公式为:

(inode 缓存内存)=(系统内存)*(j2_inodeCacheSize)/4000

我们可运行以下命令来显示 j2_inodeCacheSize 的当前值:

#ioo -a |grep j2_inodeCacheSize
j2_inodeCacheSize = 400

可使用 kdb 命令获取 inode 缓存的详细信息:

(0)> i2 -c
iCache:
  nInode:         0xB3306 (733958)
  nMaxInode:      0xB3306 (733958)
  nCacheClass:    17
  nHashClass:     0xFFFF (65535)
  nNewHashClass:  0xFFFF (65535)
  cacheTable:     0xF10001003B4FC000
  hashTable:      0xF10001003B54B000

Cache table:
   CLASS      LOCK    INODES    CACHELIST.HEAD              PILE  FULL
       0         0       260  F10001003FD92080  F10001003B502300  0
       1         0       273  F10001003D4A2880  F10001003B502600  0
       ……
      16         0       260  F10001003FF17880  F10001003B503400  0

(0)> dw iCache 12
iCache+000000: 000B3306 00110000 F1000100 3B4FC000  ..3.........;O..
iCache+000010: 0000A8A6 0000FFFF 0000FFFF 00000010  ................
iCache+000020: F1000100 3B54B000 000029D5 000B3306  ....;T....)...3.

输出行表明 nInodePerCacheClass0xA8A6nPagesPerCacheClass0x29D5

通过 kdb 命令可以检查每个缓存类的堆:

(0)> pile F10001003B502300
name........iCache
prev........0xF100010034832800 next........0xF10001003B502600
eyec........0x4C465361 objectsize..0x0400     align.......0x007F
slabsize....0x0010     intpri......0x000B
flags.......0x00000026 SLAB_PINNED ZEROED PROTECTED
pa_slabs....0x0000
paq_next....0x0000000000000000 paq_prev....0x0000000000000000
pa_flags....0x00000000
maxtotalpg..0x00000000000029D5                mintotalpg..0x0000000000000000
curtotalpg..0x0000000000000060

输出表明 maxtotalpg0x29D5,这等于 nPagesPerCacheClass

现在,让我们使用 ioo 命令降低 j2_inodeCacheSize 的当前值:

#ioo -o j2_inodeCacheSize=300
Setting j2_inodeCacheSize to 300

(0)> i2 -c
iCache:
  nInode:         0x86609 (550409)
  nMaxInode:      0xB3306 (733958)
  nCacheClass:    17
  nHashClass:     0xFFFF (65535)
  nNewHashClass:  0xFFFF (65535)
  cacheTable:     0xF10001003B4FC000
  hashTable:      0xF10001003B54B000

Cache table:
   CLASS      LOCK    INODES    CACHELIST.HEAD              PILE  FULL
       0         0       271  F10001003FF2C480  F10001003B502300  0
       1         0       282  F10001003D4A5880  F10001003B502600  0
       ……
      16         0       272  F10001003FF1C480  F10001003B503400  0

输出表明 nInode 已降低到 0x86609

(0)> dw iCache 12
iCache+000000: 00086609 00110000 F1000100 3B4FC000  ..f.........;O..
iCache+000010: 00007E79 0000FFFF 0000FFFF 00000010  ..~y............
iCache+000020: F1000100 3B54B000 00001F5F 000B3306  ....;T....._..3.

输出表明 nPagesPerCacheClass 已降低到 0x1F5F

(0)> pile F10001003B502300
name........iCache
……
maxtotalpg..0x0000000000001F5F                mintotalpg..0x0000000000000000
curtotalpg..0x0000000000000060

输出表明 maxtotalpg 也已降低到 0x1F5F,这等于 nPagesPerCacheClass

现在让我们使用 ioo 命令增加 j2_inodeCacheSize 的当前值:

#ioo -o j2_inodeCacheSize=500Setting j2_inodeCacheSize to 500

(0)> i2 -c
iCache:
  nInode:         0xE0003 (917507)
  nMaxInode:      0xE0003 (917507)
  nCacheClass:    17
  nHashClass:     0xFFFF (65535)
  nNewHashClass:  0xFFFF (65535)
  cacheTable:     0xF10001003B4FC000
  hashTable:      0xF10001003B54B000

Cache table:
   CLASS      LOCK    INODES    CACHELIST.HEAD              PILE  FULL
       0         0      1628  F10001004339D080  F10001003B502300  0
       1         0      1640  F100010042B78480  F10001003B502600  0
        ……
      16         0      1629  F10001004338D080  F10001003B503400  0

输出表明 nInode 已增加到了 0xE0003

(0)> dw iCache 12
iCache+000000: 000E0003 00110000 F1000100 3B4FC000  ............;O..
iCache+000010: 0000D2D3 0000FFFF 0000FFFF 00000010  ................
iCache+000020: F1000100 3B54B000 0000344B 000E0003  ....;T....4K....

输出表明 nPagesPerCacheClass 已增加到了 0x344B

(0)> pile F10001003B502800
name........iCache
……
maxtotalpg..0x0000000000001F5F                mintotalpg..0x0000000000000000
curtotalpg..0x00000000000001B0

输出表明 maxtotalpgnPagesPerCacheClass 要少得多。

事实上,它仍然将原始值保留为 0x1F5F


内存 DR 操作

执行内存 DR 操作后,我们可使用相同的方法检查 maxtotalpg 值。然后我们会发现,从 LPAR 删除一些内存后,maxtotalpg 降低了,但在向 LPAR 添加一些内存后,它绝不会增加。


跟踪日志和报告

跟踪日志可帮助我们进一步理解这一问题。

降低 inodeCacheSize 的跟踪日志

#ioo -a | grep j2_inodeCacheSize
             j2_inodeCacheSize = 500

#trace -anl -C all -T100M -L200M -K vmm -o trace.raw; 
ioo -o j2_inodeCacheSize=300; trcstop
Setting j2_inodeCacheSize to 300

#trcrpt -C all -o trc.out trace.raw
4DC ioo 0
 2228422 21758033 close 
0.122932631 2 
pile_config_max: pile= F10001003B502300, pflags=0026, origmax=344B, newmax=1F5F: rc=0000

我们可从跟踪日志中发现,ioo 命令调用了 pile_config_max() 函数来降低 maxtotalpg 值,

增加 inodeCacheSize 的跟踪日志

#ioo -a | grep j2_inodeCacheSize
             j2_inodeCacheSize = 300

#trace -anl -C all -T100M -L200M -K vmm -o trace.raw;
 ioo -o j2_inodeCacheSize=400; trcstop
Setting j2_inodeCacheSize to 400

#trcrpt -C all -o trc.out trace.raw#grep pile_config_max trc.out

从跟踪日志中我们可以发现,在增加 j2_inodeCacheSize 时,未调用 pile_config_max() 函数,这就是 maxtotalpg 原封未动的原因。

执行内存 DR 操作可获得类似的跟踪日志:删除内存时调用了 pile_config_max() 函数,但增加内存时未调用它。


inode 缓存耗尽

maxtotalpg 只能降低的事实意味着,在偶然降低 j2_inodeCacheSize 或执行 DLPAR 内存删除后,inode 缓存可能被耗尽,甚至在一次 j2_inodeCacheSize 增加或 DLPAR 内存添加后就被耗尽。

下面的测试演示了 inode 缓存耗尽情况。

# ioo -o j2_inodeCacheSize=50
Setting j2_inodeCacheSize to 50
# ioo -o j2_inodeCacheSize=400
Setting j2_inodeCacheSize to 400
#kdb
(0)> i2 -c
iCache:
  nInode:         0xB3306 (733958)
  nMaxInode:      0xB3306 (733958)
  nCacheClass:    17
  nHashClass:     0xFFFF (65535)
  nNewHashClass:  0xFFFF (65535)
  cacheTable:     0xF10001003A70F000
  hashTable:      0xF10001003B57D000

Cache table:
   CLASS      LOCK    INODES    CACHELIST.HEAD              PILE  FULL
       0         0       282  F10001003FAFA080  F10001003B50D300  0
       1         0       280  F10001003D4B4480  F10001003B50D600  0
    ……
      16         0       281  F10001003FAEA080  F10001003B510500  0

(0)> dw iCache 12
iCache+000000: 000B3306 00110000 F1000100 3A70F000  ..3.........:p..
iCache+000010: 0000A8A6 0000FFFF 0000FFFF 00000010  ................
iCache+000020: F1000100 3B57D000 000029D5 000B3306  ....;W....)...3.

(0)> pile F10001003B50D300
name........iCache
……
maxtotalpg..0x0000000000000539                mintotalpg..0x0000000000000000
curtotalpg..0x0000000000000060

然后,我们编写了一个程序来打开许多文件(参见 openfile.c)。

#./openfile 1000 100 /home/testdir

以下错误消息被显示:

open 90 failed Resource temporarily unavailable

从 kdb 中可以发现,所有堆都已装满,所有缓存列表都是空的,也就是说 inode 缓存已被耗尽:

(0)> i2 -c
iCache:
  nInode:         0xB3306 (733958)
  nMaxInode:      0xB3306 (733958)
  nCacheClass:    17
  nHashClass:     0xFFFF (65535)
  nNewHashClass:  0xFFFF (65535)
  cacheTable:     0xF10001003A70F000
  hashTable:      0xF10001003B57D000

Cache table:
   CLASS      LOCK    INODES    CACHELIST.HEAD         PILEFULL
       0         0         0  F10001003A70F010  F10001003B50D300  1
       1         0         0  F10001003A70F040  F10001003B50D600  1
       ……
      16         0         0  F10001003A70F310  F10001003B510500  1

curtotalpg 的值与 maxtotalpg 的值几乎一样:

(0)> pile F10001003B510300 | grep totalpg
maxtotalpg..0x0000000000000539                mintotalpg..0x0000000000000000
curtotalpg..0x0000000000000530

因为 inode 缓存已被耗尽,所以我们无法打开任何新文件,无法启动任何新进程。此外,没有用户能够登录到系统中。而且根据系统配置,缓存耗尽可能导致更严重的后果,比如文件系统损坏,甚至是系统故障。


检测和修复错误的 maxtotalpg

可通过一些方法检测和修复 inode 缓存堆的错误 maxtotalpg 值。

使用 kdb

首先,我们可以使用 kdb 并运行 pile 命令来检查 maxtotalpg 是否等于 nPagesPerCacheClass,就像前面介绍的那样。如果 maxtotalpgnPagesPerCacheClass 的值之间出现任何差异,那么我们可以重新启动系统,这样 maxtotalpg 就会恢复到默认值。我们还可以在 kdb 中手动将 maxtotalpg 修改为正确值。

还有另外一种更方便的方法。我们可以先将 j2_inodeCacheSize 增加为一个较大的值,然后将它降低到必要的值。类似的,可以先添加更多的内存,然后将内存删除到需要的内存量。这样就可以得到正确的 maxtotalpg 值。

编写一个程序来修复 maxtotalpg

我们还可以编写一个程序来自动检查和修复 maxtotalpg 值(参见 pilefix.c)。

首先,使用 kdb 获取 iCache 的内存地址:

(0)> ns
Symbolic name translation off
(0)> dd iCache
0285C158

然后,通过 /dev/kmem 界面读取和对比 maxtotalpgnPagesPerCacheClass 的值。如果 maxtotalpg 的值不同于 nPagesPerCacheClass 的值,那么我们可写回正确的值:

open("/dev/kmem", O_RDWR, 0);
kread((unsigned long long )icachep, (char *)&icache, sizeof(icache));
ccp = (iCacheClass_t *)icache.cacheTable;        
for (i=0; i<icache.nCacheClass; i++, ccp++){
    kread((unsigned long long )ccp, (char *)&cc, sizeof(iCacheClass_t));
    kread((unsigned long long )pmaxtp, (char *)&max_total_pages, 8);
    if(icache.nPagesPerCacheClass != max_total_pages){
         max_total_pages = icache.nPagesPerCacheClass;
         kwrite((unsigned long long)pmaxtp, (char *)&max_total_pages, 8);
    }
}

结束语

在 AIX 6.1(高于 6100-04 的版本)和 AIX 7.1 中,在执行 j2_inodeCacheSize 调优操作或 DLPAR 内存操作后,inode 缓存类的最大堆大小只能降低。IBM 最近提供了一份特许程序分析师报告 (APAR),表明 IV41462 可以避免此问题。如果 AIX 系统没有应用 IV41462,那么可以使用本文中介绍的方法来避免 inode 缓存耗尽。


下载

描述名字大小
要打开很多文件的程序openfile.c3 KB
自动检查和修复 maxtotapilefix.c7 KB

参考资料

学习

  • IV41462:提供了 IV41462 的详细信息。
  • mem 和 kmem:提供了更多与 memkmem 特殊文件相关的信息。
  • ioo 命令:提供了更多与 ioo 命令相关的信息。
  • 故障排除:JFS2 索引节点引起的进程挂起:本文为您演示了一种因为设置 AIX JFS2 文件系统的访问控制列表(ACL)而产生的特殊索引节点,该节点的某些 ACL 属性缺失会导致访问它的进程挂起,从而引起整体系统性能的下降。
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
  • IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=945156
ArticleTitle=j2_inodeCacheSize 调优操作和内存 DR 操作的隐藏危险副作用
publish-date=09162013