<--files ^--VFS--^ continued-->

How to write a Linux VFS filesystem module - dentries

March 8, 2004

Dentries

A dentry is a directory entry, representing what we see as a file, directory or symbolic link in a given directory. These dentry objects are kernel creations, since in a filesystem they are really represented by an inode, with one or more links to it. Files, then, are records (kept by the operating system), that point to dentry objects, which in turn refer to inodes. Multiple file objects can refer to the same dentry, and multiple dentry objects can refer to a given inode.

Another way to look at it is that programmers are concerned with file objects (this is what the operating system returns when you open a file, for instance); users are concerned with dentry objects (they "see" them whenever they type ls to see their files and directories (though they don't really "see" them, since they are created by the kernel when talking to VFS modules); and VFS modules are concerned with inodes, since that's their "unit" for working within their filesystems.

VFS maintains a dentry cache, where it keeps the structures shown below in memory for re-use. This is so oft-accessed files are quickly available instead of having to re-read their info from the filesystem each time, a time-consuming process.

Here is the struct dentry that VFS uses with our modules:

struct dentry {
        atomic_t d_count;
        unsigned int d_flags;
        struct inode  * d_inode;        /* Where the name belongs to - NULL is negative */
        struct dentry * d_parent;       /* parent directory */
        struct list_head d_hash;        /* lookup hash list */
        struct list_head d_lru;         /* d_count = 0 LRU list */
        struct list_head d_child;       /* child of parent list */
        struct list_head d_subdirs;     /* our children */
        struct list_head d_alias;       /* inode alias list */
        int d_mounted;
        struct qstr d_name;
        unsigned long d_time;           /* used by d_revalidate */
        struct dentry_operations  *d_op;
        struct super_block * d_sb;      /* The root of the dentry tree */
        unsigned long d_vfs_flags;
        void * d_fsdata;                /* fs-specific data */
        void * d_extra_attributes;      /* TUX-specific data */
        unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
};
None of this is familiar, because the VFS modules don't handle the dentry structures directly; they're usually handed off to the helper functions that we saw back in the inode section. We'll take a look at those now.

d_add()

Here's the code in /usr/src/linux/include/linux/dcache.h:

static __inline__ void d_add(struct dentry * entry, struct inode * inode)
{
        d_instantiate(entry, inode);
        d_rehash(entry);
}
Lovely. We haven't learned anything, really, except that it calls two other functions. Let's look at those.

d_instantiate()

void d_instantiate(struct dentry *entry, struct inode * inode)
{
        if (!list_empty(&entry->d_alias)) BUG();
        spin_lock(&dcache_lock);
        if (inode)
                list_add(&entry->d_alias, &inode->i_dentry);
        entry->d_inode = inode;
        spin_unlock(&dcache_lock);
}
All these does is add the struct dentry to the dcache, which is pulled from inode->i_dentry. I'm not sure how i_dentry gets initialized; I looked in our ramfs_get_inode() function, in the new_inode() function it calls, and even in the get_empty_node() function that THAT calls. Interesting. Anyway, for now we'll just care that this is how a dentry gets added to the dcache.

d_rehash()

void d_rehash(struct dentry * entry)
{
        struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash);
        if (!list_empty(&entry->d_hash)) BUG();
        spin_lock(&dcache_lock);
        list_add(&entry->d_hash, list);
        spin_unlock(&dcache_lock);
}
It looks like dentry objects are not only kept in a linked list (doubly linked list, if you look at the code), but also in a hash for easy retrieval. It doesn't really have anything to do with us, as this is all used by the kernel and VFS to handle the filesystems better.

dget()

static __inline__ struct dentry * dget(struct dentry *dentry)
{
        if (dentry) {
                if (!atomic_read(&dentry->d_count))
                        BUG();
                atomic_inc(&dentry->d_count);
        }
        return dentry;
}
Pretty straightforward, this function "gets" the entry by incrementing a counter on how many users there are. This is all done atomically, so it's threadsafe.

dput()

void dput(struct dentry *dentry)
{
        if (!dentry)
                return;

repeat:
        if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
                return;

        /* dput on a free dentry? */
        if (!list_empty(&dentry->d_lru))
                BUG();
        /*
         * AV: ->d_delete() is _NOT_ allowed to block now.
         */
        if (dentry->d_op && dentry->d_op->d_delete) {
                if (dentry->d_op->d_delete(dentry))
                        goto unhash_it;
        }
        }
        /* Unreachable? Get rid of it */
        if (list_empty(&dentry->d_hash))
                goto kill_it;
        list_add(&dentry->d_lru, &dentry_unused);
        dentry_stat.nr_unused++;
        spin_unlock(&dcache_lock);
        return;

unhash_it:
        list_del_init(&dentry->d_hash);

kill_it: {
                struct dentry *parent;
                list_del(&dentry->d_child);
                /* drops the lock, at that point nobody can reach this dentry */
                dentry_iput(dentry);
                parent = dentry->d_parent;
                d_free(dentry);
                if (dentry == parent)
                        return;
                dentry = parent;
                goto repeat;
        }
}
Wow, that's bigger than usual. This block of goto-containing code (not that there's anything wrong with that) takes care of decrementing the usage counter, recycling the dentry structure if necessary, etc. Important to us when used as we saw in ramfs_unlink(), but otherwise we don't care about the magic behind it.

d_unhashed()

static __inline__ int d_unhashed(struct dentry *dentry)
{
        return list_empty(&dentry->d_hash);
}
Seen in ramfs_positive(), this function simply says whether the given dentry is hashed or not.
<--files ^--VFS--^ continued-->
©2002-2018 Wayne Pearson