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);
}

面白いんですが、ちょっとそろそろ限界。