歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix基礎知識 >> j2_inodeCacheSize調優操作和內存DR操作的隱藏危險副作用

j2_inodeCacheSize調優操作和內存DR操作的隱藏危險副作用

日期:2017/3/3 15:21:15   编辑:Unix基礎知識

簡介

大小適中的增強日志文件系統 (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] 個緩存類。iCacheClass 和 iCache 結構是在 /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.

輸出行表明 nInodePerCacheClass 為 0xA8A6,nPagesPerCacheClass 為 0x29D5。

通過 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

輸出表明 maxtotalpg 為 0x29D5,這等於 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

輸出表明 maxtotalpg 比 nPagesPerCacheClass 要少得多。

事實上,它仍然將原始值保留為 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 內存添加後就被耗盡。

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

下面的測試演示了 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,就像前面介紹的那樣。如果 maxtotalpg 和 nPagesPerCacheClass 的值之間出現任何差異,那麼我們可以重新啟動系統,這樣 maxtotalpg 就會恢復到默認值。我們還可以在 kdb 中手動將 maxtotalpg 修改為正確值。

還有另外一種更方便的方法。我們可以先將 j2_inodeCacheSize 增加為一個較大的值,然後將它降低到必要的值。類似的,可以先添加更多的內存,然後將內存刪除到需要的內存量。這樣就可以得到正確的 maxtotalpg 值。
編寫一個程序來修復 maxtotalpg

我們還可以編寫一個程序來自動檢查和修復 maxtotalpg 值(參見 pilefix.c)。

首先,使用 kdb 獲取 iCache 的內存地址:

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

然後,通過 /dev/kmem 界面讀取和對比 maxtotalpg 和 nPagesPerCacheClass 的值。如果 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.c 3 KB 自動檢查和修復 maxtota pilefix.c 7 KB
Copyright © Linux教程網 All Rights Reserved