5

It seems to me that tmpfs is not re-using inode numbers, but instead creates a new inode number via a +1 sequence everytime it needs a free inode.

Do you know how this is implemented / can you pin-point me to some source code where i could check the algorithm that is used in tmpfs ?

I need to understand this in order to bypass a limitation in a caching system that uses the inode number as its cache key (hence leading to rare, but occuring collisions when inodes are re-used too often). tmpfs could save my day if I can prove that it keeps creating unique inode numbers.

Thank you for your help,

Jerome Wagner

Jerome WAGNER
  • 21,986
  • 8
  • 62
  • 77

2 Answers2

7

I won't directly answer your question, so I apologize in advance for that.

The tmpfs idea is good, but I wouldn't have my program depend on a more or less obscure implementation detail for generating keys. Why don't you try another method, such as combining the inode number with some other information? Maybe modification date: it's impossible two files get the same inode number AND modification date at the time of key-generation, unless system date changes.

Cheers!

salezica
  • 74,081
  • 25
  • 105
  • 166
  • I agree that relying on such an impl. detail does not seem reasonable and future proof. The fact is that the key already relies on (inode, mtime) but since the mtime has a 1 second granularity, i have learnt the hard way that the collision does happen. Using filename & filesize in the key would lower the probability of collision too. The best in my opinion would be to delete the cache when the inode is released (using some kind of notification from the kernel). The tmpfs 'hack' could bring a quick&dirty solution to my problem until the real fix is developed & tested. Thanks for the advice – Jerome WAGNER Dec 10 '10 at 23:06
  • Oh well then, sorry for telling you what you already knew and had tested xD – salezica Dec 11 '10 at 00:14
3

The bulk of the tmpfs code is in mm/shmem.c. New inodes are created by

static struct inode *shmem_get_inode(struct super_block *sb, const struct inode *dir,
                                 int mode, dev_t dev, unsigned long flags)

but it delegates almost everything to the generic filesystem code.

In particular, the field i_ino is filled in in fs/inode.c:

/**
 *      new_inode       - obtain an inode
 *      @sb: superblock
 *
 *      Allocates a new inode for given superblock. The default gfp_mask
 *      for allocations related to inode->i_mapping is GFP_HIGHUSER_MOVABLE.
 *      If HIGHMEM pages are unsuitable or it is known that pages allocated
 *      for the page cache are not reclaimable or migratable,
 *      mapping_set_gfp_mask() must be called with suitable flags on the
 *      newly created inode's mapping
 *
 */
struct inode *new_inode(struct super_block *sb)
{
        /*
         * On a 32bit, non LFS stat() call, glibc will generate an EOVERFLOW
         * error if st_ino won't fit in target struct field. Use 32bit counter
         * here to attempt to avoid that.
         */
        static unsigned int last_ino;
        struct inode *inode;

        spin_lock_prefetch(&inode_lock);

        inode = alloc_inode(sb);
        if (inode) {
                spin_lock(&inode_lock);
                __inode_add_to_lists(sb, NULL, inode);
                inode->i_ino = ++last_ino;
                inode->i_state = 0;
                spin_unlock(&inode_lock);
        }
        return inode;
}

And it does indeed just use an incrementing counter (last_ino).

Most other filesystems use information from the on-disk files to later override the i_ino field.

Note that it's perfectly possible for this to wrap all the way around. The kernel also has a "generation" field that gets filled in various ways. mm/shmem.c uses the current time.

wnoise
  • 9,764
  • 37
  • 47