Skip to content

Commit

Permalink
aufs: i_nlink 1/2, protect aufs inode i_nlink
Browse files Browse the repository at this point in the history
By the commit in linux-v3.3-rc1
        7ada4db 2012-01-06 vfs: count unlinked inodes
vfs:__destroy_inode() became available to produce a warning about
sb->s_remove_count.
Basically inode->i_nlink should be referenced by anytime, and
protected by inode_lock() or something in changing-time.
In aufs, Artur Piechocki found a problem and it is necessary to be
protected by another lock.
The problem is a warning produced by VFS:__destroy_inode() about
superblock->s_remove_count. I am not sure whether the warning appears
since linux-v3.3 or not. Some other recent (much later than v3.3)
changes in mainline MAY be related to the lifetime of inode or its
link count. On my test environment, the warning never appeared.

Here aufs introduces a spinlock dedicated to i_nlink only.
In aufs, every user of i_nlink and VFS functions for it should call
this new function regardless the inode is aufs or not.

Reported by Artur Piechocki on github.

See-also: sfjro/aufs-standalone#44
Signed-off-by: J. R. Okajima <[email protected]>
  • Loading branch information
sfjro committed Nov 27, 2024
1 parent 9fb963b commit 7f8f9df
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 14 deletions.
1 change: 1 addition & 0 deletions fs/aufs/iinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ void au_icntnr_init_once(void *_c)
spin_lock_init(&iinfo->ii_generation.ig_spin);
au_rw_init(&iinfo->ii_rwsem);
inode_init_once(&c->vfs_inode);
spin_lock_init(&c->nlink_spin);
}

void au_hinode_init(struct au_hinode *hinode)
Expand Down
20 changes: 20 additions & 0 deletions fs/aufs/inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifdef __KERNEL__

#include <linux/fsnotify.h>
#include "fstype.h"
#include "rwsem.h"

struct vfsmount;
Expand Down Expand Up @@ -67,6 +68,7 @@ struct au_iinfo {
struct au_icntnr {
struct au_iinfo iinfo;
struct inode vfs_inode;
spinlock_t nlink_spin; /* protects vfs_inode.i_nlink */
struct hlist_bl_node plink;
struct rcu_head rcu;
} ____cacheline_aligned_in_smp;
Expand Down Expand Up @@ -112,6 +114,24 @@ static inline struct au_iinfo *au_ii(struct inode *inode)
return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
}

static inline void au_nlink_lock(struct inode *inode)
{
spinlock_t *spin;

AuDebugOn(!au_test_aufs(inode->i_sb));
AuDebugOn(is_bad_inode(inode));
spin = &(container_of(inode, struct au_icntnr, vfs_inode)->nlink_spin);
spin_lock(spin);
}

static inline void au_nlink_unlock(struct inode *inode)
{
spinlock_t *spin;

spin = &(container_of(inode, struct au_icntnr, vfs_inode)->nlink_spin);
spin_unlock(spin);
}

/* ---------------------------------------------------------------------- */

/* inode.c */
Expand Down
46 changes: 46 additions & 0 deletions fs/aufs/vfsub.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,52 @@ int vfsub_sync_filesystem(struct super_block *h_sb)

/* ---------------------------------------------------------------------- */

unsigned int vfsub_inode_nlink_aufs(struct inode *inode)
{
unsigned int nlink;

au_nlink_lock(inode);
nlink = inode->i_nlink;
au_nlink_unlock(inode);

return nlink;
}

void vfsub_inc_nlink(struct inode *inode)
{
au_nlink_lock(inode);
inc_nlink(inode);
au_nlink_unlock(inode);
}

void vfsub_drop_nlink(struct inode *inode)
{
au_nlink_lock(inode);
AuDebugOn(!inode->i_nlink);
drop_nlink(inode);
au_nlink_unlock(inode);
}

void vfsub_clear_nlink(struct inode *inode)
{
au_nlink_lock(inode);
AuDebugOn(!inode->i_nlink);
clear_nlink(inode);
au_nlink_unlock(inode);
}

void vfsub_set_nlink(struct inode *inode, unsigned int nlink)
{
/*
* stop setting the value equal to the current one, in order to stop
* a useless warning from vfs:destroy_inode() about sb->s_remove_count.
*/
au_nlink_lock(inode);
if (nlink != inode->i_nlink)
set_nlink(inode, nlink);
au_nlink_unlock(inode);
}

int vfsub_update_h_iattr(struct path *h_path, int *did)
{
int err;
Expand Down
51 changes: 37 additions & 14 deletions fs/aufs/vfsub.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/posix_acl.h>
#include <linux/xattr.h>
#include "debug.h"
#include "fstype.h"

/* copied from linux/fs/internal.h */
/* todo: BAD approach!! */
Expand Down Expand Up @@ -44,28 +45,50 @@ enum {

/* ---------------------------------------------------------------------- */

static inline void au_set_nlink(struct inode *inode, unsigned int nlink)
unsigned int vfsub_inode_nlink_aufs(struct inode *inode);

enum au_inode_type {
AU_I_AUFS,
AU_I_BRANCH,
AU_I_UNKNOWN
};

static inline unsigned int vfsub_inode_nlink(struct inode *inode,
enum au_inode_type type)
{
/*
* stop setting the value equal to the current one, in order to stop
* a useless warning from vfs:destroy_inode() about sb->s_remove_count.
*/
if (nlink != inode->i_nlink)
set_nlink(inode, nlink);
unsigned int nlink;

switch (type) {
case AU_I_AUFS:
nlink = vfsub_inode_nlink_aufs(inode);
break;
case AU_I_BRANCH: /* aufs cannot be a branch of another aufs mount */
AuDebugOn(au_test_aufs(inode->i_sb));
nlink = inode->i_nlink;
break;
case AU_I_UNKNOWN:
if (au_test_aufs(inode->i_sb))
nlink = vfsub_inode_nlink_aufs(inode);
else
nlink = inode->i_nlink;
break;
};

return nlink;
}

static inline void au_init_nlink(struct inode *inode, unsigned int nlink)
void vfsub_inc_nlink(struct inode *inode);
void vfsub_drop_nlink(struct inode *inode);
void vfsub_clear_nlink(struct inode *inode);
void vfsub_set_nlink(struct inode *inode, unsigned int nlink);

static inline void vfsub_inode_nlink_init(struct inode *inode,
unsigned int nlink)
{
/* to ignore sb->s_remove_count, do not use set_nlink() */
inode->__i_nlink = nlink;
}

static inline void vfsub_drop_nlink(struct inode *inode)
{
AuDebugOn(!inode->i_nlink);
drop_nlink(inode);
}

static inline void vfsub_dead_dir(struct inode *inode)
{
AuDebugOn(!S_ISDIR(inode->i_mode));
Expand Down

0 comments on commit 7f8f9df

Please sign in to comment.