Commit ee166183 authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: dinfo core



The structure is very similar to aufs inode info (in previous commit).
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 786c90cb
# Copyright (C) 2005-2019 Junjiro R. Okajima
Lookup in a Branch
----------------------------------------------------------------------
Since aufs has a character of sub-VFS (see Introduction), it operates
lookup for branches as VFS does. It may be a heavy work. But almost all
lookup operation in aufs is the simplest case, ie. lookup only an entry
directly connected to its parent. Digging down the directory hierarchy
is unnecessary. VFS has a function lookup_one_len() for that use, and
aufs calls it.
When a branch is a remote filesystem, aufs basically relies upon its
->d_revalidate(), also aufs forces the hardest revalidate tests for
them.
For d_revalidate, aufs implements three levels of revalidate tests. See
"Revalidate Dentry and UDBA" in detail.
......@@ -10,6 +10,7 @@ ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
obj-$(CONFIG_AUFS_FS) += aufs.o
aufs-y := module.o super.o \
dinfo.o \
iinfo.o
# all are boolean
......
......@@ -22,6 +22,8 @@
#include "debug.h"
#include "dcsub.h"
#include "dentry.h"
#include "fstype.h"
#include "inode.h"
#include "module.h"
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* sub-routines for dentry cache
*/
#ifndef __AUFS_DCSUB_H__
#define __AUFS_DCSUB_H__
#ifdef __KERNEL__
#include <linux/dcache.h>
/*
* by the commit
* 360f547 2015-01-25 dcache: let the dentry count go down to zero without
* taking d_lock
* the type of d_lockref.count became int, but the inlined function d_count()
* still returns unsigned int.
* I don't know why. Maybe it is for every d_count() users?
* Anyway au_dcount() lives on.
*/
static inline int au_dcount(struct dentry *d)
{
return (int)d_count(d);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_DCSUB_H__ */
......@@ -97,3 +97,87 @@ void au_dpri_inode(struct inode *inode)
for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; bindex++)
do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode);
}
void au_dpri_dalias(struct inode *inode)
{
struct dentry *d;
spin_lock(&inode->i_lock);
hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias)
au_dpri_dentry(d);
spin_unlock(&inode->i_lock);
}
static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
{
if (!dentry || IS_ERR(dentry)) {
dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
return -1;
}
/* do not call dget_parent() here */
/* note: access d_xxx without d_lock */
dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n",
bindex, dentry, dentry,
dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
au_dcount(dentry), dentry->d_flags,
d_unhashed(dentry) ? "un" : "");
do_pri_inode(bindex, d_inode(dentry));
return 0;
}
void au_dpri_dentry(struct dentry *dentry)
{
struct au_dinfo *dinfo;
aufs_bindex_t bindex;
int err;
err = do_pri_dentry(-1, dentry);
if (err || !au_test_aufs(dentry->d_sb))
return;
dinfo = au_di(dentry);
if (!dinfo)
return;
dpri("d-1: btop %d, bbot %d\n",
dinfo->di_btop, dinfo->di_bbot);
if (dinfo->di_btop < 0)
return;
for (bindex = dinfo->di_btop; bindex <= dinfo->di_bbot; bindex++)
do_pri_dentry(bindex, au_hdentry(dinfo, bindex)->hd_dentry);
}
/* ---------------------------------------------------------------------- */
void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line)
{
struct inode *h_inode, *inode = d_inode(dentry);
struct dentry *h_dentry;
aufs_bindex_t bindex, bbot, bi;
if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */)
return;
bbot = au_dbbot(dentry);
bi = au_ibbot(inode);
if (bi < bbot)
bbot = bi;
bindex = au_dbtop(dentry);
bi = au_ibtop(inode);
if (bi > bindex)
bindex = bi;
for (; bindex <= bbot; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
h_inode = au_h_iptr(inode, bindex);
if (unlikely(h_inode != d_inode(h_dentry))) {
au_debug_on();
AuDbg("b%d, %s:%d\n", bindex, func, line);
AuDbgDentry(dentry);
AuDbgInode(inode);
au_debug_off();
BUG();
}
}
}
......@@ -60,6 +60,11 @@ extern struct mutex au_dbg_mtx;
extern char *au_plevel;
struct inode;
void au_dpri_inode(struct inode *inode);
void au_dpri_dalias(struct inode *inode);
void au_dpri_dentry(struct dentry *dentry);
#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__)
void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line);
#define AuDbgInode(i) do { \
mutex_lock(&au_dbg_mtx); \
......@@ -67,8 +72,26 @@ void au_dpri_inode(struct inode *inode);
au_dpri_inode(i); \
mutex_unlock(&au_dbg_mtx); \
} while (0)
#define AuDbgDAlias(i) do { \
mutex_lock(&au_dbg_mtx); \
AuDbg(#i "\n"); \
au_dpri_dalias(i); \
mutex_unlock(&au_dbg_mtx); \
} while (0)
#define AuDbgDentry(d) do { \
mutex_lock(&au_dbg_mtx); \
AuDbg(#d "\n"); \
au_dpri_dentry(d); \
mutex_unlock(&au_dbg_mtx); \
} while (0)
#else
AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry)
#define AuDbgInode(i) do {} while (0)
#define AuDbgDAlias(i) do {} while (0)
#define AuDbgDentry(d) do {} while (0)
#endif /* CONFIG_AUFS_DEBUG */
#endif /* __KERNEL__ */
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* lookup and dentry operations
*/
#ifndef __AUFS_DENTRY_H__
#define __AUFS_DENTRY_H__
#ifdef __KERNEL__
#include <linux/dcache.h>
#include "rwsem.h"
struct au_hdentry {
struct dentry *hd_dentry;
};
struct au_dinfo {
struct au_rwsem di_rwsem;
aufs_bindex_t di_btop, di_bbot;
struct au_hdentry *di_hdentry;
struct rcu_head rcu;
} ____cacheline_aligned_in_smp;
/* ---------------------------------------------------------------------- */
/* dinfo.c */
void au_di_init_once(void *_di);
struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc);
void au_di_free(struct au_dinfo *dinfo);
int au_di_init(struct dentry *dentry);
void au_di_fin(struct dentry *dentry);
void di_read_lock(struct dentry *d, int flags, unsigned int lsc);
void di_read_unlock(struct dentry *d, int flags);
void di_downgrade_lock(struct dentry *d, int flags);
void di_write_lock(struct dentry *d, unsigned int lsc);
void di_write_unlock(struct dentry *d);
struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex);
void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_dentry);
void au_update_dbtop(struct dentry *dentry);
void au_update_dbbot(struct dentry *dentry);
/* ---------------------------------------------------------------------- */
static inline struct au_dinfo *au_di(struct dentry *dentry)
{
return dentry->d_fsdata;
}
/* ---------------------------------------------------------------------- */
/* lock subclass for dinfo */
enum {
AuLsc_DI_CHILD, /* child first */
AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */
AuLsc_DI_CHILD3, /* copyup dirs */
AuLsc_DI_PARENT,
AuLsc_DI_PARENT2,
AuLsc_DI_PARENT3,
AuLsc_DI_TMP /* temp for replacing dinfo */
};
/*
* di_read_lock_child, di_write_lock_child,
* di_read_lock_child2, di_write_lock_child2,
* di_read_lock_child3, di_write_lock_child3,
* di_read_lock_parent, di_write_lock_parent,
* di_read_lock_parent2, di_write_lock_parent2,
* di_read_lock_parent3, di_write_lock_parent3,
*/
#define AuReadLockFunc(name, lsc) \
static inline void di_read_lock_##name(struct dentry *d, int flags) \
{ di_read_lock(d, flags, AuLsc_DI_##lsc); }
#define AuWriteLockFunc(name, lsc) \
static inline void di_write_lock_##name(struct dentry *d) \
{ di_write_lock(d, AuLsc_DI_##lsc); }
#define AuRWLockFuncs(name, lsc) \
AuReadLockFunc(name, lsc) \
AuWriteLockFunc(name, lsc)
AuRWLockFuncs(child, CHILD);
AuRWLockFuncs(child2, CHILD2);
AuRWLockFuncs(child3, CHILD3);
AuRWLockFuncs(parent, PARENT);
AuRWLockFuncs(parent2, PARENT2);
AuRWLockFuncs(parent3, PARENT3);
#undef AuReadLockFunc
#undef AuWriteLockFunc
#undef AuRWLockFuncs
#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem)
#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem)
#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem)
/* ---------------------------------------------------------------------- */
static inline void au_h_dentry_init(struct au_hdentry *hdentry)
{
hdentry->hd_dentry = NULL;
}
static inline struct au_hdentry *au_hdentry(struct au_dinfo *di,
aufs_bindex_t bindex)
{
return di->di_hdentry + bindex;
}
static inline void au_hdput(struct au_hdentry *hd)
{
if (hd)
dput(hd->hd_dentry);
}
static inline aufs_bindex_t au_dbtop(struct dentry *dentry)
{
DiMustAnyLock(dentry);
return au_di(dentry)->di_btop;
}
static inline aufs_bindex_t au_dbbot(struct dentry *dentry)
{
DiMustAnyLock(dentry);
return au_di(dentry)->di_bbot;
}
/* todo: hard/soft set? */
static inline void au_set_dbtop(struct dentry *dentry, aufs_bindex_t bindex)
{
DiMustWriteLock(dentry);
au_di(dentry)->di_btop = bindex;
}
static inline void au_set_dbbot(struct dentry *dentry, aufs_bindex_t bindex)
{
DiMustWriteLock(dentry);
au_di(dentry)->di_bbot = bindex;
}
#endif /* __KERNEL__ */
#endif /* __AUFS_DENTRY_H__ */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* dentry private data
*/
#include "aufs.h"
void au_di_init_once(void *_dinfo)
{
struct au_dinfo *dinfo = _dinfo;
au_rw_init(&dinfo->di_rwsem);
}
struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc)
{
struct au_dinfo *dinfo;
int nbr;
dinfo = au_cache_alloc_dinfo();
if (unlikely(!dinfo))
goto out;
nbr = 1; /* re-commit later */
dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS);
if (dinfo->di_hdentry) {
au_rw_write_lock_nested(&dinfo->di_rwsem, lsc);
dinfo->di_btop = -1;
dinfo->di_bbot = -1;
goto out;
}
au_cache_free_dinfo(dinfo);
dinfo = NULL;
out:
return dinfo;
}
void au_di_free(struct au_dinfo *dinfo)
{
struct au_hdentry *p;
aufs_bindex_t bbot, bindex;
/* dentry may not be revalidated */
bindex = dinfo->di_btop;
if (bindex >= 0) {
bbot = dinfo->di_bbot;
p = au_hdentry(dinfo, bindex);
while (bindex++ <= bbot)
au_hdput(p++);
}
au_kfree_try_rcu(dinfo->di_hdentry);
au_cache_free_dinfo(dinfo);
}
int au_di_init(struct dentry *dentry)
{
int err;
struct super_block *sb;
struct au_dinfo *dinfo;
err = 0;
sb = dentry->d_sb;
dinfo = au_di_alloc(sb, AuLsc_DI_CHILD);
if (dinfo)
dentry->d_fsdata = dinfo;
else
err = -ENOMEM;
return err;
}
void au_di_fin(struct dentry *dentry)
{
struct au_dinfo *dinfo;
dinfo = au_di(dentry);
AuRwDestroy(&dinfo->di_rwsem);
au_di_free(dinfo);
}
/* ---------------------------------------------------------------------- */
static void do_ii_write_lock(struct inode *inode, unsigned int lsc)
{
switch (lsc) {
case AuLsc_DI_CHILD:
ii_write_lock_child(inode);
break;
case AuLsc_DI_CHILD2:
ii_write_lock_child2(inode);
break;
case AuLsc_DI_CHILD3:
ii_write_lock_child3(inode);
break;
case AuLsc_DI_PARENT:
ii_write_lock_parent(inode);
break;
case AuLsc_DI_PARENT2:
ii_write_lock_parent2(inode);
break;
case AuLsc_DI_PARENT3:
ii_write_lock_parent3(inode);
break;
default:
BUG();
}
}
static void do_ii_read_lock(struct inode *inode, unsigned int lsc)
{
switch (lsc) {
case AuLsc_DI_CHILD:
ii_read_lock_child(inode);
break;
case AuLsc_DI_CHILD2:
ii_read_lock_child2(inode);
break;
case AuLsc_DI_CHILD3:
ii_read_lock_child3(inode);
break;
case AuLsc_DI_PARENT:
ii_read_lock_parent(inode);
break;
case AuLsc_DI_PARENT2:
ii_read_lock_parent2(inode);
break;
case AuLsc_DI_PARENT3:
ii_read_lock_parent3(inode);
break;
default:
BUG();
}
}
void di_read_lock(struct dentry *d, int flags, unsigned int lsc)
{
struct inode *inode;
au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc);
if (d_really_is_positive(d)) {
inode = d_inode(d);
if (au_ftest_lock(flags, IW))
do_ii_write_lock(inode, lsc);
else if (au_ftest_lock(flags, IR))
do_ii_read_lock(inode, lsc);
}
}
void di_read_unlock(struct dentry *d, int flags)
{
struct inode *inode;
if (d_really_is_positive(d)) {
inode = d_inode(d);
if (au_ftest_lock(flags, IW)) {
au_dbg_verify_dinode(d);
ii_write_unlock(inode);
} else if (au_ftest_lock(flags, IR)) {
au_dbg_verify_dinode(d);
ii_read_unlock(inode);
}
}
au_rw_read_unlock(&au_di(d)->di_rwsem);
}
void di_downgrade_lock(struct dentry *d, int flags)
{
if (d_really_is_positive(d) && au_ftest_lock(flags, IR))
ii_downgrade_lock(d_inode(d));
au_rw_dgrade_lock(&au_di(d)->di_rwsem);
}
void di_write_lock(struct dentry *d, unsigned int lsc)
{
au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc);
if (d_really_is_positive(d))
do_ii_write_lock(d_inode(d), lsc);
}
void di_write_unlock(struct dentry *d)
{
au_dbg_verify_dinode(d);
if (d_really_is_positive(d))
ii_write_unlock(d_inode(d));
au_rw_write_unlock(&au_di(d)->di_rwsem);
}
/* ---------------------------------------------------------------------- */
struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex)
{
struct dentry *d;
DiMustAnyLock(dentry);
if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry))
return NULL;
AuDebugOn(bindex < 0);
d = au_hdentry(au_di(dentry), bindex)->hd_dentry;
AuDebugOn(d && au_dcount(d) <= 0);
return d;
}
/* ---------------------------------------------------------------------- */
void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_dentry)
{
struct au_dinfo *dinfo;
struct au_hdentry *hd;
DiMustWriteLock(dentry);
dinfo = au_di(dentry);
hd = au_hdentry(dinfo, bindex);
au_hdput(hd);
hd->hd_dentry = h_dentry;
}
void au_update_dbtop(struct dentry *dentry)
{
aufs_bindex_t bindex, bbot;
struct dentry *h_dentry;
bbot = au_dbbot(dentry);
for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
if (d_is_positive(h_dentry)) {
au_set_dbtop(dentry, bindex);
return;
}
au_set_h_dptr(dentry, bindex, NULL);
}
}
void au_update_dbbot(struct dentry *dentry)
{
aufs_bindex_t bindex, btop;
struct dentry *h_dentry;
btop = au_dbtop(dentry);
for (bindex = au_dbbot(dentry); bindex >= btop; bindex--) {
h_dentry = au_h_dptr(dentry, bindex);
if (!h_dentry)
continue;
if (d_is_positive(h_dentry)) {
au_set_dbbot(dentry, bindex);
return;
}
au_set_h_dptr(dentry, bindex, NULL);
}
}
......@@ -88,9 +88,11 @@ static void au_cache_fin(void)
static int __init au_cache_init(void)
{
/* SLAB_DESTROY_BY_RCU */
au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr,
au_icntnr_init_once);
au_cache[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once);
if (au_cache[AuCache_DINFO])
/* SLAB_DESTROY_BY_RCU */
au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr,
au_icntnr_init_once);
if (au_cache[AuCache_ICNTNR])
return 0;
......
......@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include "debug.h"
#include "dentry.h"
#include "inode.h"
void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink);
......@@ -80,6 +81,7 @@ static inline int au_kmidx_sub(size_t sz, size_t new_sz)
/* kmem cache */
enum {
AuCache_DINFO,
AuCache_ICNTNR,
AuCache_Last
};
......@@ -110,6 +112,7 @@ extern struct kmem_cache *au_cache[AuCache_Last];
{ /* au_cache_free_##name##_norcu(p); */ \
au_cache_free_##name##_rcu(p); }
AuCacheFuncs(dinfo, DINFO);
AuCacheFuncs(icntnr, ICNTNR);
#endif /* __KERNEL__ */
......
......@@ -12,6 +12,18 @@
#ifdef __KERNEL__
/* flags for __si_read_lock()/aufs_read_lock()/di_read_lock() */
#define AuLock_DW 1 /* write-lock dentry */
#define AuLock_IR (1 << 1) /* read-lock inode */
#define AuLock_IW (1 << 2) /* write-lock inode */
#define au_ftest_lock(flags, name) ((flags) & AuLock_##name)
#define au_fset_lock(flags, name) \
do { (flags) |= AuLock_##name; } while (0)
#define au_fclr_lock(flags, name) \
do { (flags) &= ~AuLock_##name; } while (0)
/* ---------------------------------------------------------------------- */
/* super.c */
struct super_block;
struct inode *au_iget_locked(struct super_block *sb, ino_t ino);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment