ブロックデバイススペシャルファイルの参照
ファイルの読み書きは、FILEのアドレスマップのpageをキャッシュとして行います。ファイルをopenすると、ファイルのdentryを取得し、__dentry_open()がコールされ、struct fileを取得します。この時、f->f_mapping = inode->i_mappingとして、オープンするファイルinodeのアドレスマップをpageキャッシュとなります。パーティション毎のデバイスファイルはそれぞれにinodeを有しているわけで、従ってパーティション毎のf->f_mappingも独立したものになります。
inodeにi_fopコールバックがある場合、f->f_op->openがコールされます。通常はgeneric_file_open()がコールされ、フラグ/サイズのチェックが行われるだけですが、ブロックデスペシャルファイルの場合、blkdev_open()がコールされ、filp->f_mapping = bdev->bd_inode->i_mappingで、ブロックデバイス自身のアドレスマップをが設定されます。
パーティションは同じブロックデバイスで、パーティションのデバイススペシャルファイルのf->f_mappingは、ブロックデバイスのそれとなり、かかるパーティションのpageキャッシュはデバイスのサイズに応じたpageを共有することになります。
ブロックデバイスにバルク的なやり取りで巨大なデータを保存する等、直接デバイススペシャルファイルで読み書きすることで、inodeを介する必要はなく、またかかるinode/データブロック等も必要ないため、高速でデバイス領域全体を実領域として使うことができます。またユーザランドサイドで単純なストリームのバルクデータ管理機能を実装することで、セキュリテをかねた保存デバイスシステムの構築も可能です。
inodeにi_fopコールバックがある場合、f->f_op->openがコールされます。通常はgeneric_file_open()がコールされ、フラグ/サイズのチェックが行われるだけですが、ブロックデスペシャルファイルの場合、blkdev_open()がコールされ、filp->f_mapping = bdev->bd_inode->i_mappingで、ブロックデバイス自身のアドレスマップをが設定されます。
パーティションは同じブロックデバイスで、パーティションのデバイススペシャルファイルのf->f_mappingは、ブロックデバイスのそれとなり、かかるパーティションのpageキャッシュはデバイスのサイズに応じたpageを共有することになります。
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
static const struct file_operations empty_fops = {};
struct inode *inode;
int error;
f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
FMODE_PREAD | FMODE_PWRITE;
if (unlikely(f->f_flags & O_PATH))
f->f_mode = FMODE_PATH;
inode = dentry->d_inode;
if (f->f_mode & FMODE_WRITE) {
error = __get_file_write_access(inode, mnt);
if (error)
goto cleanup_file;
if (!special_file(inode->i_mode))
file_take_write(f);
}
f->f_mapping = inode->i_mapping;
f->f_path.dentry = dentry;
f->f_path.mnt = mnt;
f->f_pos = 0;
file_sb_list_add(f, inode->i_sb);
if (unlikely(f->f_mode & FMODE_PATH)) {
f->f_op = &empty_fops;
return f;
}
f->f_op = fops_get(inode->i_fop);
error = security_dentry_open(f, cred);
if (error)
goto cleanup_all;
error = break_lease(inode, f->f_flags);
if (error)
goto cleanup_all;
if (!open && f->f_op)
open = f->f_op->open;
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
}
if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
i_readcount_inc(inode);
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
if (f->f_flags & O_DIRECT) {
if (!f->f_mapping->a_ops ||
((!f->f_mapping->a_ops->direct_IO) &&
(!f->f_mapping->a_ops->get_xip_mem))) {
fput(f);
f = ERR_PTR(-EINVAL);
}
}
return f;
cleanup_all:
fops_put(f->f_op);
if (f->f_mode & FMODE_WRITE) {
put_write_access(inode);
if (!special_file(inode->i_mode)) {
file_reset_write(f);
mnt_drop_write(mnt);
}
}
file_sb_list_del(f);
f->f_path.dentry = NULL;
f->f_path.mnt = NULL;
cleanup_file:
put_filp(f);
dput(dentry);
mntput(mnt);
return ERR_PTR(error);
}
static int blkdev_open(struct inode * inode, struct file * filp)
{
struct block_device *bdev;
filp->f_flags |= O_LARGEFILE;
if (filp->f_flags & O_NDELAY)
filp->f_mode |= FMODE_NDELAY;
if (filp->f_flags & O_EXCL)
filp->f_mode |= FMODE_EXCL;
if ((filp->f_flags & O_ACCMODE) == 3)
filp->f_mode |= FMODE_WRITE_IOCTL;
bdev = bd_acquire(inode);
if (bdev == NULL)
return -ENOMEM;
filp->f_mapping = bdev->bd_inode->i_mapping;
return blkdev_get(bdev, filp->f_mode, filp);
}
int generic_file_open(struct inode * inode, struct file * filp)
{
if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
return -EOVERFLOW;
return 0;
}
通常ファイルのオープンは、そのファイルのファイルシステムのオペレーションコールバックが、通常のファイル/ディレクトリ/リンクに応じてnode->i_opに設定されますが、そうでないなら init_special_inode()がコールされ、デバイススペシャルファイルの時def_blk_fopsが設定されます。
const struct file_operations def_blk_fops = {
.open = blkdev_open,
.release = blkdev_close,
.llseek = block_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = blkdev_aio_write,
.mmap = generic_file_mmap,
.fsync = blkdev_fsync,
.unlocked_ioctl = block_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = compat_blkdev_ioctl,
#endif
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
};
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = rdev;
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
" inode %s:%lu\n", mode, inode->i_sb->s_id,
inode->i_ino);
}
EXPORT_SYMBOL(init_special_inode);
補足
パーティションのスペシャルファイルのinodeのsizeは、パーティションのサイズで、read()でそのサイズを超えていればエラーとなり、ベースのデバイスファイルでなくパーティションのスペシャルファイルを介して、他のパーティション領域を参照することはできません。ブロックデバイスにバルク的なやり取りで巨大なデータを保存する等、直接デバイススペシャルファイルで読み書きすることで、inodeを介する必要はなく、またかかるinode/データブロック等も必要ないため、高速でデバイス領域全体を実領域として使うことができます。またユーザランドサイドで単純なストリームのバルクデータ管理機能を実装することで、セキュリテをかねた保存デバイスシステムの構築も可能です。






