sys_close() を掘ってみる
掘っていく内にナチュラル判明。
スーパブロックなオブジェクトの s_blocks_count 云々って言ってましたが、これってトータルのブロックじゃねぇか。sys_close() 掘っていく内にスーパーブロックなオブジェクトの s_free_blocks_count は更新してるんだけど、、というあたりまで分かった時点で勘違いを疑えよ、と。
とりあえず掘っていった経緯を
まず、sys_close() から。怪しげなのは filp_close() で以下なあたり (一部のみ)
asmlinkage long sys_close(unsigned int fd) { struct file * filp; struct files_struct *files = current->files; struct fdtable *fdt; int retval; spin_lock(&files->file_lock); fdt = files_fdtable(files); if (fd >= fdt->max_fds) goto out_unlock; filp = fdt->fd[fd]; if (!filp) goto out_unlock; rcu_assign_pointer(fdt->fd[fd], NULL); FD_CLR(fd, fdt->close_on_exec); __put_unused_fd(files, fd); spin_unlock(&files->file_lock); retval = filp_close(filp, files);
filp_close() が以下。
int filp_close(struct file *filp, fl_owner_t id) { int retval = 0; if (!file_count(filp)) { printk(KERN_ERR "VFS: Close: file count is 0\n"); return 0; } if (filp->f_op && filp->f_op->flush) retval = filp->f_op->flush(filp, id); dnotify_flush(filp, id); locks_remove_posix(filp, id); fput(filp); return retval; }
これも fput() がアヤシげ、と見て掘る。ここは fs/file_table.c
void fastcall fput(struct file *file) { if (atomic_dec_and_test(&file->f_count)) __fput(file); }
__fput() は直下で定義されている。色々ヤッてますが vfsmount オブジェクトを渡している一番下の mntput() 呼び出しがアヤシいと見て掘る。一番上と
void fastcall __fput(struct file *file) { struct dentry *dentry = file->f_path.dentry; struct vfsmount *mnt = file->f_path.mnt; struct inode *inode = dentry->d_inode;
一番下を引用。
file->f_path.dentry = NULL; file->f_path.mnt = NULL; file_free(file); dput(dentry); mntput(mnt); }
mntput() は include/linux/mount.h にて inline な定義。
static inline void mntput(struct vfsmount *mnt) { if (mnt) { mnt->mnt_expiry_mark = 0; mntput_no_expire(mnt); } }
mntput_no_expire() は fs/namespace.c にて定義されている。
void mntput_no_expire(struct vfsmount *mnt) { repeat: if (atomic_dec_and_lock(&mnt->mnt_count, &vfsmount_lock)) { if (likely(!mnt->mnt_pinned)) { spin_unlock(&vfsmount_lock); __mntput(mnt); return; } atomic_add(mnt->mnt_pinned + 1, &mnt->mnt_count); mnt->mnt_pinned = 0; spin_unlock(&vfsmount_lock); acct_auto_close_mnt(mnt); security_sb_umount_close(mnt); goto repeat; } }
これもいっちゃん最後の __mntput() か。
static inline void __mntput(struct vfsmount *mnt) { struct super_block *sb = mnt->mnt_sb; dput(mnt->mnt_root); free_vfsmnt(mnt); deactivate_super(sb); }
super_block なオブジェクトを渡している deactivate_super() は fs/super.c にて定義されてて以下。
void deactivate_super(struct super_block *s) { struct file_system_type *fs = s->s_type; if (atomic_dec_and_lock(&s->s_active, &sb_lock)) { s->s_count -= S_BIAS-1; spin_unlock(&sb_lock); DQUOT_OFF(s); down_write(&s->s_umount); fs->kill_sb(s); put_filesystem(fs); put_super(s); } }
kill_sb はファイルシステム特有のナニと見て ext3 の kill_sb 属性を grep してみたら super.c にて以下な記述。
static struct file_system_type ext3_fs_type = { .owner = THIS_MODULE, .name = "ext3", .get_sb = ext3_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, };
kill_block_super() は汎用的な手続きらしく fs/super.c にて定義。
void kill_block_super(struct super_block *sb) { struct block_device *bdev = sb->s_bdev; bdev_uevent(bdev, KOBJ_UMOUNT); generic_shutdown_super(sb); sync_blockdev(bdev); close_bdev_excl(bdev); }
で、sb 渡してる generic_shutdown_super() がナニ。fs/super.c にて以下 (一部のみ)。
void generic_shutdown_super(struct super_block *sb) { struct super_operations *sop = sb->s_op; if (sb->s_root) { shrink_dcache_for_umount(sb); fsync_super(sb); lock_super(sb); sb->s_flags &= ~MS_ACTIVE; /* bad name - it should be evict_inodes() */ invalidate_inodes(sb); lock_kernel(); if (sop->write_super && sb->s_dirt) sop->write_super(sb); if (sop->put_super) sop->put_super(sb);
色々見てみたんですが、sop->put_super() が ext3_put_super と見て確認 (一部のみ)。
static void ext3_put_super (struct super_block * sb) { struct ext3_sb_info *sbi = EXT3_SB(sb); struct ext3_super_block *es = sbi->s_es; int i; ext3_xattr_put_super(sb); journal_destroy(sbi->s_journal); if (!(sb->s_flags & MS_RDONLY)) { EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER); es->s_state = cpu_to_le16(sbi->s_mount_state); BUFFER_TRACE(sbi->s_sbh, "marking dirty"); mark_buffer_dirty(sbi->s_sbh); ext3_commit_super(sb, es, 1); }
es 渡してるのがアヤしいという事で ext3_commit_super() 見てみると以下なカンジ。
static void ext3_commit_super (struct super_block * sb, struct ext3_super_block * es, int sync) { struct buffer_head *sbh = EXT3_SB(sb)->s_sbh; if (!sbh) return; es->s_wtime = cpu_to_le32(get_seconds()); es->s_free_blocks_count = cpu_to_le32(ext3_count_free_blocks(sb)); es->s_free_inodes_count = cpu_to_le32(ext3_count_free_inodes(sb)); BUFFER_TRACE(sbh, "marking dirty"); mark_buffer_dirty(sbh); if (sync) sync_dirty_buffer(sbh); }
ここで s_free_blocks_count 属性とかをナニ。ここまで掘れてたんですが、まだナチュラルに気づかず。おかしいな、と言いつつ 3rd Edition を色々見つつ意識を失なってたりしてた訳ッス。
これは
もっかい statfs() のナニを見てみた方が良いな。