1

I have a couple of general questions regarding the Linux page cache. As I understand, there are (at least) three methods a block on a block device can be related to a block buffer in page cache:

  1. by the disk device file, e.g., /dev/sda
  2. by the partition device file, e.g., /dev/sda1
  3. by a regular file, e.g., /home/me/hello

suppose the /dev/sda1 contains an typical linux filesystem, such as ext2, which is mounted on / and /home folder is in this filesystem.

And the three methods have different page caches since their address_space object are embedded in different inode objects:

  1. bdev special filesystem's inode, for /dev/sda
  2. bdev special filesystem's inode, for /dev/sda1
  3. ext2 filesystem's inode, for /home/me/hello

Now here are the questions:

  1. I thought super_block object is only allocated for a mounted filesystem, and since bdev is not really mounted anywhere, there is no super_block object for it. Therefore when writeback_inodes() loops over super_block objects and search for dirty inode, page caches from method 1 & 2 above will be missed, hence they require manual sync to disk?

  2. Is it possible that a user can read/write a block on disk that belongs to file /home/me/hello in all three methods listed above, hence make the contents in the three page caches out of sync? I can see in kernel 2.6.11 code there is effort when writing a file in method 3, it waits for a device cache to sync and then proceed, although this device could be either disk device(method 1) or partition device(method 2), but it couldn't be both. And I didn't find similar code in kernel 5.3:

Code to wait for device cache:

static int __block_prepare_write(struct inode *inode, struct page *page,
        unsigned from, unsigned to, get_block_t *get_block)
{
    ...
    unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
    ...
}

void unmap_underlying_metadata(struct block_device *bdev, sector_t block)
{
    ...
    old_bh = __find_get_block_slow(bdev, block, 0);
    if (old_bh) {
        clear_buffer_dirty(old_bh);
        wait_on_buffer(old_bh);
        clear_buffer_req(old_bh);
        __brelse(old_bh);
    }
}

Still new to linux kernel hence the questions might not make much sense.. pointers appreciated!

1
  • OPEN(2) man page talks about O_EXCL flag, I am not sure whether this is related to the question.
    – QnA
    Oct 24, 2019 at 2:07

2 Answers 2

1

It is not normal to mix writes (or reads) to files on a file system with writes to the block device the file system resides on. The latter is only used for tasks like creating partitions or making a file system on a partition.

When the file system is created and mounted, writes to the block device can get the caches out of sync, but that's not the only problem: bypassing the file system and writing directly to the block device is a sure way to corrupt the file system.

Writing to the block device is something only root can do. Linux provides a lot of ways for root to shoot itself in the foot, and the assumption is that you can trust root not to do that.

1
  • thanks, @Johan. I understand it is not a recommended way to do things, the question is whether the kernel has a safety mechanism to prevent inconsistency should users go off track. Given the fact that some (old version) code seem to partially enforce consistency, I wonder why it did not go all the way to deal with more/all cases. And hopefully the answer can help me have a better understanding of the page cache logic.
    – QnA
    Oct 15, 2019 at 15:47
1

Q1 [...] since bdev is not really mounted anywhere, there is no super_block object for it. Therefore when writeback_inodes() loops over super_block objects and search for dirty inode, page caches from method 1 & 2 above will be missed [...]

linux/v5.3/source/fs/block_dev.c:841

struct super_block *blockdev_superblock __read_mostly;
EXPORT_SYMBOL_GPL(blockdev_superblock);

void __init bdev_cache_init(void)
{
    int err;
    static struct vfsmount *bd_mnt;

    bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
            0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
                SLAB_MEM_SPREAD|SLAB_ACCOUNT|SLAB_PANIC),
            init_once);
    err = register_filesystem(&bd_type);
    if (err)
        panic("Cannot register bdev pseudo-fs");
    bd_mnt = kern_mount(&bd_type);
    if (IS_ERR(bd_mnt))
        panic("Cannot create bdev pseudo-fs");
    blockdev_superblock = bd_mnt->mnt_sb;   /* For writeback */
}
1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .