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!

  • OPEN(2) man page talks about O_EXCL flag, I am not sure whether this is related to the question. – QnA Oct 24 '19 at 2:07
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.

  • 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 '19 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 */
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.