Search code examples
cmemory-managementext4journaling

How to know which address space a buffer head is mapped to?


In the jbd2 source code, any modification in the File System is mapped into a handle_t structure (per process) that later is used to map the buffer_head to the transaction_t which this handle is going to be part of.

As far as I could understand, when a modification to a given buffer_head is needed, then a call to do_get_write_access() is going to map this buffer_head to the transaction that the handle_t is being part of. However, when this handle_t is used to map the buffer_head to the transaction_t, the reciprocal mapping is lost, that is, I cannot track back to which handle_t this buffer_head belonged.

The thing is that, during the jbd2_journal_commit_transaction() (commit phase 2b in commit function) I want to find a way to walk through these buffer_heads and be able to classify them if they are related to an inode, or to a metadata, or to a inode bitmap block, or an data bitmap block, for example. Furthermore, at this point in the source code, the buffer_heads seems to be opaque, where they are simply sent to the storage.

UPDATE 1:

What I have tried so far was this, in the jbd2_journal_commit_transaction() function, in the commit phase 2b.

struct journal_head *jh;
...
jh = commit_transaction->t_buffers;
if(jh->b_jlist == BJ_Metadata) {
    struct buffer_head *bh_p = NULL;
    bh_p = jh2bh(jh);
    if(!bh_p) printk(KERN_DEBUG "Null ptr in bh_p\n");
    else {
        struct address_space *as_p = NULL;
        if((as_p = bh_p->b_assoc_map) == NULL)
            printk(KERN_DEBUG "Null ptr in as_p\n");
        else {
            struct inode *i_p = NULL;
            if(i_p) printk(KERN_DEBUG "Inode is %lu\n", i_p->i_ino);
        }
    }
}

It is not working, it is giving NULL ptr in the as_p, that is, there is no b_assoc_map set for this buffer_head. But, I have no idea what is the b_assoc_map.

UPDATE 2:

I am trying to get the information from the handle_t structure at ext4_mark_iloc_dirty. handle_t->h_type has the information I need. However, when I try to compare this value, a NULL pointer is causing a kernel warning. I thought this structure is unique per process, but seems like it is having some race condition, I don't know clearly yet.


Solution

  • After looking through all the source code path related to this issue, I conclude that there is no way to do it without changing anything.

    Basically, the handle_t structure has the information about the transaction. Later, when some modification is going to be done in a given buffer_head, the jbd2_journal_get_write_access(handle, bh) is called to get the write access to the specified buffer.

    Inside jbd2_journal_get_write_access the journal_head structure is created, and then it is going to point to this buffer_head, however, at this point there is no relation between handle_t.

    Next step, after returning from jbd2_journal_add_journal_head, a call to do_get_write_access(handle, bh) is made, and here the journal_head is initialized with the information passed by the handle_t.

    After this step, where the handle_t is used to initialize the journal_head, then the handle_t is not necessary anymore.

    Up to here, everything is initialized, now we can move to the commit point.

    In jbd2_journal_commit_transaction, at commit phase 2b the buffer_heads belonging to the committing transaction are going to be iterated, and committed.

    Because the only information attached to the buffer_head is the journal_head, and the journal_head does not contain the necessary information to distinguish what kind of buffer_head is it, then I conclude that it is not possible to reach what I want without modifying the source code.

    My solution was to add a new member to store the inode number in the handle_t, and also in journal_head structure. So, when the do_get_write_access() call is made, I can filter the operation like this:

    if(handle->h_ino)
        jh->b_ino = handle->h_ino;
    

    So, I had to modify handle_t to transport the inode number to journal_head, and at commit time I can get the required information that I want.