bd_acquire() 手続き
3rd Edition の 14.5 に詳細な解説あり。懇切丁寧な解説でとても良いのですが bdev_lock でヒッカカッてたりします。
bdev_lock の定義は block_dev.c で以下。
static spinlock_t bdev_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
この際、cacheline_aligned はスルーとして、この bdev_lock はカーネル上で唯一なインスタンス (って言い方も微妙ですが) なはずなので、そーゆー理解で良いのかな。
このあたりを掘り下げると色々あるのでしょうが
- ソースファイルなオブジェクトとしての lock
- 構造体オブジェクトとしての lock
があるのかどうか。
3rd Edition 参考に
がつっとメモを。
bdev = inode->i_bdev; if (bdev && igrab(bdev->bd_inode)) {
ここは bdev_acquire(inode) の a. に当たります。bdev が NULL でない場合はスデに open 済みとの事。このあたりの意図が楽に分かるのが良いですね。で、igrab() 手続きの以下の条件ですが
if (!(inode->i_state & I_FREEING))
これも 3rd Edition によれば_i ノードオブジェクトの解放処理が行なわれている状態_との事なので状態として
- 解放済み
- だけど解放中
なのであれば NULL を返却するのか。このあたりの制御がなかなかキビシい感じ。else なブロック (上記の条件を満たす場合?) のコメントが以下。
* Handle the case where s_op->clear_inode is not been * called yet, and somebody is calling igrab * while the inode is getting freed.
成程ね、としか言いようが無いな。で、__iget() 手続きですが以下な定義。
/* * inode_lock must be held */ void __iget(struct inode * inode) { if (atomic_read(&inode->i_count)) { atomic_inc(&inode->i_count); return; } atomic_inc(&inode->i_count); if (!(inode->i_state & (I_DIRTY|I_LOCK))) list_move(&inode->i_list, &inode_in_use); inodes_stat.nr_unused--; }
3rd Edition によれば bd_inode の利用カウンタ増分、とある。上記なコードの意図がぱっと見分からんかったのですが、
if (atomic_read(&inode->i_count)) {
が偽 (atomic_read() な呼び出しの戻りが 0) な場合って単純に参照カウントが 0 だった、という事?
ってか inode 構造体の i_count 属性は atomic_t との事で、あるいは atomic_read() の定義は include/asm-i386/atomic.h にて以下。
#define atomic_read(v) ((v)->counter)
む、counter って何だば、と言いつつ atomic_t な定義を確認。include/asm-i386/atomic.h で以下。
typedef struct { volatile int counter; } atomic_t;
なるほど。参照カウンタが 0 じゃなければ増分して戻るんですが、0 な場合は増分して以下な処理を実行するのか。
if (!(inode->i_state & (I_DIRTY|I_LOCK))) list_move(&inode->i_list, &inode_in_use); inodes_stat.nr_unused--;
inode のステイトに関する bitmap な定義が以下な模様。include/linux/fs.h にて定義されている。
/* Inode state bits. Protected by inode_lock. */ #define I_DIRTY_SYNC 1 /* Not dirty enough for O_DATASYNC */ #define I_DIRTY_DATASYNC 2 /* Data-related inode changes pending */ #define I_DIRTY_PAGES 4 /* Data-related inode changes pending */ #define __I_LOCK 3 #define I_LOCK (1 << __I_LOCK) #define I_FREEING 16 #define I_CLEAR 32 #define I_NEW 64 #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
そろそろ寝ないと、と言いつつ list_move() も掘ってみる。
/** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add(list, head); }
__list_del() は以下
/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; }
ぱっと見、list_move() はそのまんま
- 現在居るリストから要素を除いて
- head が指すリストの先頭にその要素を追加
という事らしい。list_add の理解が若干微妙。定義がこんなカンジ。
/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }
面白いんですが、ちょっとそろそろ限界。