dentryの生成と初期化を追う

ファイルシステムext3

  • どこのレイヤーを処理してるのかを見失わないように
    • dentry
    • inodeオブジェクト
    • super_block
    • ext3_*
struct inode_operations ext3_dir_inode_operations = {
	/* ... */
	.lookup		= ext3_lookup,
	/* ... */
}
  • lookup ... dentry操作の抽象化
    • iget ... inode操作の抽象化 ... ext3層でinode操作
  • inode->i_op->lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
    • reallookup
      • lookup ---> ext3_lookup
      • iget .. inodeオブジェクト割当、super_blockのread_inodeでディスクinode情報で初期化
        • iget_locked ...
            • get_new_inode_fast
              • alloc_inode
          • ifind_fast ... スピンロック,ディスクの待ち合わせ?
            • find_inode_fast ... inodeオブジェクトテーブルの探索
  • ext3_read_inode ... ディスクinodeの情報を読み取り、inodeオブジェクトにセットする
  • [fs/namei.c] real_lookup
/*
 * This is called when everything else fails, and we actually have
 * to go to the low-level filesystem to find out what we should do..
 *
 * We get the directory semaphore, and after getting that we also
 * make sure that nobody added the entry to the dcache in the meantime..
 * SMP-safe
 */
static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
	struct dentry * result;
	struct inode *dir = parent->d_inode; // 親dentryのinode .. ディレクトリ

        // セマフォ
	down(&dir->i_sem);
	/*
	 * First re-do the cached lookup just in case it was created
	 * while we waited for the directory semaphore..
	 *
	 * FIXME! This could use version numbering or similar to
	 * avoid unnecessary cache lookups.
	 *
	 * The "dcache_lock" is purely to protect the RCU list walker
	 * from concurrent renames at this point (we mustn't get false
	 * negatives from the RCU list walk here, unlike the optimistic
	 * fast walk).
	 *
	 * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
	 */
	result = d_lookup(parent, name);
	if (!result) {
                // dentryの割当
		struct dentry * dentry = d_alloc(parent, name);
		result = ERR_PTR(-ENOMEM);
		if (dentry) {
                        //inode探索のコストはファイルシステムに依る
                        // fs/ext3/namei.c
			result = dir->i_op->lookup(dir, dentry, nd);
			if (result)
				dput(dentry);
			else
				result = dentry;
		}
		up(&dir->i_sem);
		return result;
	}

	/*
	 * Uhhuh! Nasty case: the cache was re-populated while
	 * we waited on the semaphore. Need to revalidate.
	 */
	up(&dir->i_sem);
	if (result->d_op && result->d_op->d_revalidate) {
		if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) {
			dput(result);
			result = ERR_PTR(-ENOENT);
		}
	}
	return result;
}
// [fs/namei.c] real_lookup -> inode->i_op->lookup 
static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
	struct inode * inode;
	struct ext3_dir_entry_2 * de; // ext3のディレクトリエントリ //新しいバージョン?
	struct buffer_head * bh;

        // dentry->d_name -> struct qstr ( 長さとハッシュを保持しているので 探索、比較が速い文字列として扱える?)
	if (dentry->d_name.len > EXT3_NAME_LEN)
		return ERR_PTR(-ENAMETOOLONG);

	bh = ext3_find_entry(dentry, &de); // ディレクトリエントリの探索 -> bufferから探索
	inode = NULL;
	if (bh) {
               unsigned long ino = le32_to_cpu(de->inode); // inode番号
		brelse (bh);
                // ディレクトリエントリのinode番号からinodeの取得 -> 必要であればディスクinodeから読み込む
		inode = iget(dir->i_sb, ino);

		if (!inode)
			return ERR_PTR(-EACCES);
	}
	if (inode)
            //-> d_add -> d_instantiate -> dentry->d_inode = inode する
            //-> d_rehash -> ハッシュ?
            return d_splice_alias(inode, dentry); 
        
	d_add(dentry, inode);
	return NULL;
}


// dirは親ディレクトリのinodeオブジェクト
// dentryは親ディレクトリのdentry
static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
		inode = iget(dir->i_sb, ino);
}

//iget .. inodeオブジェクト割当、super_blockのread_inodeでディスクinode情報で初期化
1389 static inline struct inode *iget(struct super_block *sb, unsigned long ino)
1390 {
1391         struct inode *inode = iget_locked(sb, ino);
1392 
1393         if (inode && (inode->i_state & I_NEW)) {
		     // inodeオブジェクトの状態がI_NEW ... inodeオブジェクトが割当済み、ディスクのinodeの情報で初期化されてない
1394                 sb->s_op->read_inode(inode); /// ext3_read_inode .. ディスクのinode読み取り、初期化
1395                 unlock_new_inode(inode);
1396         }
1397 
1398         return inode;
1399 }

//// static struct hlist_head *inode_hashtable

 932 struct inode *iget_locked(struct super_block *sb, unsigned long ino)
 933 {
 934         struct hlist_head *head = inode_hashtable + hash(sb, ino);
 935         struct inode *inode;
 936 
 937         inode = ifind_fast(sb, head, ino);
 938         if (inode)
 939                 return inode;
 940         /*
 941          * get_new_inode_fast() will do the right thing, re-trying the search
 942          * in case it had to block at any point.
 943          */
 944         return get_new_inode_fast(sb, head, ino);
 945 }
 
 // ifind_fast ... スピンロック,ディスクの待ち合わせ?
 805 static inline struct inode *ifind_fast(struct super_block *sb,
 806                 struct hlist_head *head, unsigned long ino)
 807 {
 808         struct inode *inode;
 809 
 810         spin_lock(&inode_lock);
 811         inode = find_inode_fast(sb, head, ino);
 812         if (inode) {
 813                 __iget(inode);
 814                 spin_unlock(&inode_lock);
 815                 wait_on_inode(inode);
 816                 return inode;
 817         }
 818         spin_unlock(&inode_lock);
 819         return NULL;
 820 }

 221 /*
 222  * inode_lock must be held
 223  */
 224 void __iget(struct inode * inode)
 225 {
 226         if (atomic_read(&inode->i_count)) {
 227                 atomic_inc(&inode->i_count);
 228                 return;
 229         }
 230         atomic_inc(&inode->i_count);
 231         if (!(inode->i_state & (I_DIRTY|I_LOCK)))
 232                 list_move(&inode->i_list, &inode_in_use);
 233         inodes_stat.nr_unused--;
 234 }

 // find_inode_fast ... inodeオブジェクトテーブルの探索
 524 /*
 525  * find_inode_fast is the fast path version of find_inode, see the comment at
 526  * iget_locked for details.
 527  */
 528 static struct inode * find_inode_fast(struct super_block * sb, struct hlist_head *head, unsigned long ino)
 529 {
 530         struct hlist_node *node;
 531         struct inode * inode = NULL;
 532 
 533 repeat:
             // リスト探索
 534         hlist_for_each (node, head) {
 535                 inode = hlist_entry(node, struct inode, i_hash);
 536                 if (inode->i_ino != ino)
 537                         continue;
 538                 if (inode->i_sb != sb)
 539                         continue;
 540                 if (inode->i_state & (I_FREEING|I_CLEAR)) {
 541                         __wait_on_freeing_inode(inode);
 542                         goto repeat;
 543                 }
 544                 break;
 545         }
 546         return node ? inode : NULL;
 547 }

// caller: iget_locked
/*
 * get_new_inode_fast is the fast path version of get_new_inode, see the
 * comment at iget_locked for details.
 */
static struct inode * get_new_inode_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino)
{
	struct inode * inode;
	// 割当
	inode = alloc_inode(sb);
	if (inode) {
		struct inode * old;

		spin_lock(&inode_lock);
		/* We released the lock, so.. */
		old = find_inode_fast(sb, head, ino);
		if (!old) {
			inode->i_ino = ino;
			inodes_stat.nr_inodes++;
			list_add(&inode->i_list, &inode_in_use);
			hlist_add_head(&inode->i_hash, head);

			// I_NEW ... アロケート済み、初期化されてない
			inode->i_state = I_LOCK|I_NEW;
			spin_unlock(&inode_lock);

			/* Return the locked inode with I_NEW set, the
			 * caller is responsible for filling in the contents
			 */
			return inode;
		}

		/*
		 * Uhhuh, somebody else created the same inode under
		 * us. Use the old inode instead of the one we just
		 * allocated.
		 */
		__iget(old);
		spin_unlock(&inode_lock);
		destroy_inode(inode);
		inode = old;
		wait_on_inode(inode);
	}
	return inode;
}

// inodeオブジェクトの割当
static struct inode *alloc_inode(struct super_block *sb)
{
	static struct address_space_operations empty_aops;
	static struct inode_operations empty_iops;
	static struct file_operations empty_fops;
	struct inode *inode;

	if (sb->s_op->alloc_inode)
		inode = sb->s_op->alloc_inode(sb);
	else
		inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);

	if (inode) {

		//inodeの初期化
		//ファイルシステムに依存しないパラメータの初期化
		struct address_space * const mapping = &inode->i_data;

		inode->i_sb = sb;
		inode->i_blkbits = sb->s_blocksize_bits;
		inode->i_flags = 0;
		atomic_set(&inode->i_count, 1);
		inode->i_sock = 0;
		inode->i_op = &empty_iops;
		inode->i_fop = &empty_fops;
		inode->i_nlink = 1;
		atomic_set(&inode->i_writecount, 0);
		inode->i_size = 0;
		inode->i_blocks = 0;
		inode->i_bytes = 0;
		inode->i_generation = 0;
#ifdef CONFIG_QUOTA
		memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
#endif
		inode->i_pipe = NULL;
		inode->i_bdev = NULL;
		inode->i_cdev = NULL;
		inode->i_rdev = 0;
		inode->i_security = NULL;
		inode->dirtied_when = 0;
		if (security_inode_alloc(inode)) {
			if (inode->i_sb->s_op->destroy_inode)
				inode->i_sb->s_op->destroy_inode(inode);
			else
				kmem_cache_free(inode_cachep, (inode));
			return NULL;
		}

		mapping->a_ops = &empty_aops;
 		mapping->host = inode;
		mapping->flags = 0;
		mapping_set_gfp_mask(mapping, GFP_HIGHUSER);
		mapping->assoc_mapping = NULL;
		mapping->backing_dev_info = &default_backing_dev_info;

		/*
		 * If the block_device provides a backing_dev_info for client
		 * inodes then use that.  Otherwise the inode share the bdev's
		 * backing_dev_info.
		 */
		if (sb->s_bdev) {
			struct backing_dev_info *bdi;

			bdi = sb->s_bdev->bd_inode_backing_dev_info;
			if (!bdi)
				bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
			mapping->backing_dev_info = bdi;
		}
		memset(&inode->u, 0, sizeof(inode->u));
		inode->i_mapping = mapping;
	}
	return inode;
}