GlusterFSのdisperseを試す(その3)

この間の続き。やっと解決。
FreeBSDとLinuxのmajor/minor/makedev関数の違い、statが通常ファイルに対して返すrdev値の違いによる問題。

[2016-01-15 01:01:54.466105] W [ec-combine.c:76:ec_iatt_combine] 0-gv0-disperse-0: Failed to combine iatt (inode: 11735403145588290470-11735403145588290470, links: 1-1, uid: 0-0, gid: 0-0, rdev: 322123661424-571232551144, size: 148176896-148209664, mode: 100644-100644)

sizeはとりあえずいい。healなんだから、大きさが違うこともある。
でもrdevとやらは良くない匂いがする。

rdevはそもそもキャラクタデバイスなんかを表す番号のはず。
エラーログにある数字は一体なんなのか。

(gdb) printf "%16llx\n%16llx\n", 322123661424, 571232551144

      4b00110070
      85001d00e8

ますます怪しい。
この値を作っているのはlibglusterfs/src/iatt.hの以下の部分。

static inline int
iatt_from_stat (struct iatt *iatt, struct stat *stat)
{
        /* snip */
        iatt->ia_rdev       = ia_makedev (major (stat->st_rdev),
                                          minor (stat->st_rdev));

これはおなじみstruct statから、glusterfsの内部で使うstruct iattに変換している模様。
ではia_makedevmajorminorはどこにあるのか。
ia_makedevはすぐ近く(同じiatt.h)に。

ia_makedev (uint32_t ia_maj, uint32_t ia_min)
{
        return ((((uint64_t) ia_maj) << 32) | ia_min);
}

majorminorは、FreeBSDの場合は/usr/include/sys/types.hにある。

#define major(x)        ((int)(((u_int)(x) >> 8)&0xff)) /* major number */
#define minor(x)        ((int)((x)&0xffff00ff))         /* minor number */
#define makedev(x,y)    ((dev_t)(((x) << 8) | (y)))     /* create dev_t */

ずいぶんと面白い定義になってるけど、きっと"歴史的経緯"というやつだろう。
で、こいつらのせいでrdevは本来の値から変な変形を受けている。
struct statからstruct iattが作られたのなら、こうなるはずだ。

stat側: 11223344
iatt側: 3311220044

これに先の値を当てはめて元に戻してみる。

iatt側1: 4b00110070
stat側1: 00114b70 (=1133424)
iatt側2: 85001d00e8
stat側2: 001d85e8 (=1934824)

それっぽいので、このrdev値をもつファイルを探してみよう。

# ssh root@10.0.0.1 "find /dummy-root/glusterfs-data | xargs -n 1 stat -f '%r %R'" > /tmp/stats-1.txt
# ssh root@10.0.0.2 "find /dummy-root/glusterfs-data | xargs -n 1 stat -f '%r %R'" > /tmp/stats-2.txt
# ssh root@10.0.0.3 "find /dummy-root/glusterfs-data | xargs -n 1 stat -f '%r %R'" > /tmp/stats-3.txt
# grep -E "1133424|1934824" /tmp/statall*
/tmp/statall2.txt:1133424 /dummy-root/glusterfs-data/brick/.glusterfs/ae/93/ae930c59-e4ab-4142-a2dc-86fbeccab3a6
/tmp/statall2.txt:1133424 /dummy-root/glusterfs-data/brick/random.dat
/tmp/statall3.txt:1934824 /dummy-root/glusterfs-data/brick/.glusterfs/ae/93/ae930c59-e4ab-4142-a2dc-86fbeccab3a6
/tmp/statall3.txt:1934824 /dummy-root/glusterfs-data/brick/random.dat

出てきた。

ところで、rdevってregular fileに付くの……?

# stat -f '%r %N' / /COPYRIGHT /dummy-root
4294967295 /
4294967295 /COPYRIGHT
120 /dummy-root

/はZFS。UFSだとregular fileもrdevは有効な値になる……?

ちょっとバージョンは違うけどFreeBSD 9.0のソースコードより、
sys/ufs/ufs/dinode.h:

/*                                                              
 * The di_db fields may be overlaid with other information for  
 * file types that do not have associated disk storage. Block   
 * and character devices overlay the first data block with their
 * dev_t value. Short symbolic links place their path in the    
 * di_db area.                                                  
 */
#define di_rdev di_db[0]

意訳するとdi_dbフィールドはディスク領域を割り当てないようなファイルタイプについては他の情報が載ってる場合があるよ
デバイス系だとdev_t番号、すなわちrdevの値。ディスク領域を割り当てるようなファイル、つまりregular fileの場合、ここは本来Direct disk blocksなので、きっと割り当てブロックの情報が入るのであろう。
つまり、regular fileに対して実行したstatのst_rdevは読んではいけない。

POSIXのstatの定義はどうなっているか。
stat - get file status
sys/stat.h

はっきりとは書かれていないが、構造体定義にst_rdevDevice ID (if file is character or block special).と書かれているので、character/block device以外については不定と考えるべきだろう。少なくとも、いつも同じ値を返す義理はない。

となればglusterfsのバグ。さくっと直して続けてみる。

[2016-01-16 01:27:35.331128] W [ec-common.c:162:ec_check_status] 0-gv0-disperse-0: Operation failed on some subvolumes (up=7, mask=5, remaining=0, good=5, bad=2)
[2016-01-16 01:27:35.331231] I [ec-heal.c:546:ec_heal_init] 0-ec: Healing '(null)', gfid db8166d5-06be-4d04-bbf1-de9f2b911ccb
[2016-01-16 01:27:35.331278] E [ec-helpers.c:473:ec_loc_parent] 0-gv0-disperse-0: Parent inode missing for loc_t
[2016-01-16 01:27:35.331319] W [ec-common.c:121:ec_heal_report] 0-gv0-disperse-0: Heal failed (error 5)

これはどうやらディレクトリがHealできていないのが原因らしい。

client# ls -al /glusterfs

もしくは、実運用などでまとめてHealするなら、 client# find /glusterfs -exec stat {}\; >/dev/null
でできる。

これでエラーもおさまった。
先の件もバグに登録済み。

あとはESXiから使う話に戻ろう。