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

aufs: iinfo core



See the documents in this commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent c7926831
# Copyright (C) 2005-2019 Junjiro R. Okajima
Basic Aufs Internal Structure
Superblock/Inode/Dentry/File Objects
----------------------------------------------------------------------
As like an ordinary filesystem, aufs has its own
superblock/inode/dentry/file objects. All these objects have a
dynamically allocated array and store the same kind of pointers to the
lower filesystem, branch.
For example, when you build a union with one readwrite branch and one
readonly, mounted /au, /rw and /ro respectively.
- /au = /rw + /ro
- /ro/fileA exists but /rw/fileA
Aufs lookup operation finds /ro/fileA and gets dentry for that. These
pointers are stored in a aufs dentry. The array in aufs dentry will be,
- [0] = NULL (because /rw/fileA doesn't exist)
- [1] = /ro/fileA
This style of an array is essentially same to the aufs
superblock/inode/dentry/file objects.
Because aufs supports manipulating branches, ie. add/delete/change
branches dynamically, these objects has its own generation. When
branches are changed, the generation in aufs superblock is
incremented. And a generation in other object are compared when it is
accessed. When a generation in other objects are obsoleted, aufs
refreshes the internal array.
Superblock
----------------------------------------------------------------------
Additionally aufs superblock has some data for policies to select one
among multiple writable branches, XIB files, pseudo-links and kobject.
See below in detail.
About the policies which supports copy-down a directory, see
wbr_policy.txt too.
......@@ -6,4 +6,5 @@
ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
obj-$(CONFIG_AUFS_FS) += aufs.o
aufs-y := module.o
aufs-y := module.o super.o \
iinfo.o
......@@ -12,7 +12,9 @@
#ifdef __KERNEL__
#include "inode.h"
#include "module.h"
#include "super.h"
#endif /* __KERNEL__ */
#endif /* __AUFS_H__ */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* inode private data
*/
#include "aufs.h"
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)
{
struct inode *h_inode;
struct au_hinode *hinode;
hinode = au_hinode(au_ii(inode), bindex);
h_inode = hinode->hi_inode;
AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
return h_inode;
}
/* todo: hard/soft set? */
void au_hiput(struct au_hinode *hinode)
{
iput(hinode->hi_inode);
}
void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode, unsigned int flags)
{
struct au_hinode *hinode;
struct inode *hi;
struct au_iinfo *iinfo = au_ii(inode);
hinode = au_hinode(iinfo, bindex);
hi = hinode->hi_inode;
AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
if (hi)
au_hiput(hinode);
hinode->hi_inode = h_inode;
if (h_inode) {
AuDebugOn(inode->i_mode
&& (h_inode->i_mode & S_IFMT)
!= (inode->i_mode & S_IFMT));
/* add more later */
}
}
/* ---------------------------------------------------------------------- */
void au_icntnr_init_once(void *_c)
{
struct au_icntnr *c = _c;
struct au_iinfo *iinfo = &c->iinfo;
init_rwsem(&iinfo->ii_rwsem);
down_write(&iinfo->ii_rwsem);
inode_init_once(&c->vfs_inode);
}
void au_hinode_init(struct au_hinode *hinode)
{
hinode->hi_inode = NULL;
}
int au_iinfo_init(struct inode *inode)
{
struct au_iinfo *iinfo;
struct super_block *sb;
struct au_hinode *hi;
int nbr, i;
sb = inode->i_sb;
iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
nbr = 1; /* re-commit later */
hi = kmalloc_array(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS);
if (hi) {
iinfo->ii_hinode = hi;
for (i = 0; i < nbr; i++, hi++)
au_hinode_init(hi);
iinfo->ii_btop = -1;
iinfo->ii_bbot = -1;
return 0;
}
return -ENOMEM;
}
void au_iinfo_fin(struct inode *inode)
{
struct au_iinfo *iinfo;
struct au_hinode *hi;
aufs_bindex_t bindex, bbot;
AuDebugOn(au_is_bad_inode(inode));
iinfo = au_ii(inode);
bindex = iinfo->ii_btop;
if (bindex >= 0) {
hi = au_hinode(iinfo, bindex);
bbot = iinfo->ii_bbot;
while (bindex++ <= bbot) {
if (hi->hi_inode)
au_hiput(hi);
hi++;
}
}
au_kfree_small(iinfo->ii_hinode);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* inode operations
*/
#ifndef __AUFS_INODE_H__
#define __AUFS_INODE_H__
#ifdef __KERNEL__
#include <linux/fs.h>
struct au_hinode {
struct inode *hi_inode;
};
struct au_iinfo {
struct rw_semaphore ii_rwsem;
aufs_bindex_t ii_btop, ii_bbot;
struct au_hinode *ii_hinode;
};
struct au_icntnr {
struct au_iinfo iinfo;
struct inode vfs_inode;
struct rcu_head rcu;
} ____cacheline_aligned_in_smp;
/* ---------------------------------------------------------------------- */
static inline struct au_iinfo *au_ii(struct inode *inode)
{
BUG_ON(is_bad_inode(inode));
return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
}
/* ---------------------------------------------------------------------- */
/* iinfo.c */
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
void au_hiput(struct au_hinode *hinode);
void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
struct inode *h_inode, unsigned int flags);
void au_icntnr_init_once(void *_c);
void au_hinode_init(struct au_hinode *hinode);
int au_iinfo_init(struct inode *inode);
void au_iinfo_fin(struct inode *inode);
/* ---------------------------------------------------------------------- */
static inline void au_icntnr_init(struct au_icntnr *c)
{
/* re-commit later */
}
/* ---------------------------------------------------------------------- */
static inline struct au_hinode *au_hinode(struct au_iinfo *iinfo,
aufs_bindex_t bindex)
{
return iinfo->ii_hinode + bindex;
}
static inline int au_is_bad_inode(struct inode *inode)
{
return !!(is_bad_inode(inode) || !au_hinode(au_ii(inode), 0));
}
static inline aufs_bindex_t au_ibtop(struct inode *inode)
{
return au_ii(inode)->ii_btop;
}
static inline aufs_bindex_t au_ibbot(struct inode *inode)
{
return au_ii(inode)->ii_bbot;
}
static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex)
{
au_ii(inode)->ii_btop = bindex;
}
static inline void au_set_ibbot(struct inode *inode, aufs_bindex_t bindex)
{
au_ii(inode)->ii_bbot = bindex;
}
static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)
{
return au_hinode(au_ii(inode), bindex);
}
#endif /* __KERNEL__ */
#endif /* __AUFS_INODE_H__ */
......@@ -66,6 +66,40 @@ void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
}
/* ---------------------------------------------------------------------- */
/*
* aufs caches
*/
struct kmem_cache *au_cache[AuCache_Last];
static void au_cache_fin(void)
{
int i;
/*
* Make sure all delayed rcu free inodes are flushed before we
* destroy cache.
*/
rcu_barrier();
for (i = 0; i < AuCache_Last; i++) {
kmem_cache_destroy(au_cache[i]);
au_cache[i] = NULL;
}
}
static int __init au_cache_init(void)
{
/* SLAB_DESTROY_BY_RCU */
au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr,
au_icntnr_init_once);
if (au_cache[AuCache_ICNTNR])
return 0;
au_cache_fin();
return -ENOMEM;
}
/* ---------------------------------------------------------------------- */
/*
* functions for module interface.
*/
......@@ -80,10 +114,19 @@ MODULE_VERSION(AUFS_VERSION);
static int __init aufs_init(void)
{
int err;
memset(au_cache, 0, sizeof(au_cache));
err = au_cache_init();
if (unlikely(err))
goto out;
/* since we define pr_fmt, call printk directly */
printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n");
return 0;
out:
return err;
}
module_init(aufs_init);
......@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include "debug.h"
#include "inode.h"
void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink);
void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
......@@ -75,5 +76,41 @@ static inline int au_kmidx_sub(size_t sz, size_t new_sz)
#endif
}
/* ---------------------------------------------------------------------- */
/* kmem cache */
enum {
AuCache_ICNTNR,
AuCache_Last
};
extern struct kmem_cache *au_cache[AuCache_Last];
#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD)
#define AuCache(type) KMEM_CACHE(type, AuCacheFlags)
#define AuCacheCtor(type, ctor) \
kmem_cache_create(#type, sizeof(struct type), \
__alignof__(struct type), AuCacheFlags, ctor)
#define AuCacheFuncs(name, index) \
static inline struct au_##name *au_cache_alloc_##name(void) \
{ return kmem_cache_alloc(au_cache[AuCache_##index], GFP_NOFS); } \
static inline void au_cache_free_##name##_norcu(struct au_##name *p) \
{ kmem_cache_free(au_cache[AuCache_##index], p); } \
\
static inline void au_cache_free_##name##_rcu_cb(struct rcu_head *rcu) \
{ void *p = rcu; \
p -= offsetof(struct au_##name, rcu); \
kmem_cache_free(au_cache[AuCache_##index], p); } \
static inline void au_cache_free_##name##_rcu(struct au_##name *p) \
{ BUILD_BUG_ON(sizeof(struct au_##name) < sizeof(struct rcu_head)); \
call_rcu(&p->rcu, au_cache_free_##name##_rcu_cb); } \
\
static inline void au_cache_free_##name(struct au_##name *p) \
{ /* au_cache_free_##name##_norcu(p); */ \
au_cache_free_##name##_rcu(p); }
AuCacheFuncs(icntnr, ICNTNR);
#endif /* __KERNEL__ */
#endif /* __AUFS_MODULE_H__ */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* mount and super_block operations
*/
#include <linux/iversion.h>
#include "aufs.h"
/*
* super_operations
*/
static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused)
{
struct au_icntnr *c;
c = au_cache_alloc_icntnr();
if (c) {
au_icntnr_init(c);
inode_set_iversion(&c->vfs_inode, 1); /* sigen(sb); */
c->iinfo.ii_hinode = NULL;
return &c->vfs_inode;
}
return NULL;
}
static void aufs_destroy_inode_cb(struct rcu_head *head)
{
struct inode *inode = container_of(head, struct inode, i_rcu);
au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode));
}
static void aufs_destroy_inode(struct inode *inode)
{
if (!au_is_bad_inode(inode))
au_iinfo_fin(inode);
call_rcu(&inode->i_rcu, aufs_destroy_inode_cb);
}
struct inode *au_iget_locked(struct super_block *sb, ino_t ino)
{
struct inode *inode;
int err;
inode = iget_locked(sb, ino);
if (unlikely(!inode)) {
inode = ERR_PTR(-ENOMEM);
goto out;
}
if (!(inode->i_state & I_NEW))
goto out;
err = au_iinfo_init(inode);
if (!err)
inode_inc_iversion(inode);
else {
iget_failed(inode);
inode = ERR_PTR(err);
}
out:
/* never return NULL */
AuDebugOn(!inode);
return inode;
}
/* ---------------------------------------------------------------------- */
static const struct super_operations aufs_sop = {
.alloc_inode = aufs_alloc_inode,
.destroy_inode = aufs_destroy_inode,
/* always deleting, no clearing */
.drop_inode = generic_delete_inode
};
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* super_block operations
*/
#ifndef __AUFS_SUPER_H__
#define __AUFS_SUPER_H__
#ifdef __KERNEL__
/* super.c */
struct super_block;
struct inode *au_iget_locked(struct super_block *sb, ino_t ino);
#endif /* __KERNEL__ */
#endif /* __AUFS_SUPER_H__ */
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