/* * Copyright (C) 2005-2016 Junjiro R. Okajima */ /* * module global variables and operations */ #include #include #include "aufs.h" /* shrinkable realloc */ void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink) { size_t sz; int diff; sz = 0; diff = -1; if (p) { #if 0 /* unused */ if (!new_sz) { au_delayed_kfree(p); p = NULL; goto out; } #else AuDebugOn(!new_sz); #endif sz = ksize(p); diff = au_kmidx_sub(sz, new_sz); } if (sz && !diff) goto out; if (sz < new_sz) /* expand or SLOB */ p = krealloc(p, new_sz, gfp); else if (new_sz < sz && may_shrink) { /* shrink */ void *q; q = kmalloc(new_sz, gfp); if (q) { if (p) { memcpy(q, p, new_sz); au_delayed_kfree(p); } p = q; } else p = NULL; } out: return p; } void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp, int may_shrink) { p = au_krealloc(p, new_sz, gfp, may_shrink); if (p && new_sz > nused) memset(p + nused, 0, new_sz - nused); return p; } /* ---------------------------------------------------------------------- */ /* * aufs caches */ struct au_dfree au_dfree; /* delayed free */ static void au_do_dfree(struct work_struct *work __maybe_unused) { struct llist_head *head; struct llist_node *node, *next; #define AU_CACHE_DFREE_DO_BODY(name, idx, lnode) do { \ head = &au_dfree.cache[AuCache_##idx].llist; \ node = llist_del_all(head); \ for (; node; node = next) { \ struct au_##name *p \ = llist_entry(node, struct au_##name, \ lnode); \ next = llist_next(node); \ au_cache_free_##name(p); \ } \ } while (0) AU_CACHE_DFREE_DO_BODY(dinfo, DINFO, di_lnode); AU_CACHE_DFREE_DO_BODY(icntnr, ICNTNR, lnode); AU_CACHE_DFREE_DO_BODY(finfo, FINFO, fi_lnode); AU_CACHE_DFREE_DO_BODY(vdir, VDIR, vd_lnode); AU_CACHE_DFREE_DO_BODY(vdir_dehstr, DEHSTR, lnode); #ifdef CONFIG_AUFS_HNOTIFY AU_CACHE_DFREE_DO_BODY(hnotify, HNOTIFY, hn_lnode); #endif #define AU_DFREE_DO_BODY(llist, func) do { \ node = llist_del_all(llist); \ for (; node; node = next) { \ next = llist_next(node); \ func(node); \ } \ } while (0) AU_DFREE_DO_BODY(au_dfree.llist + AU_DFREE_KFREE, kfree); AU_DFREE_DO_BODY(au_dfree.llist + AU_DFREE_FREE_PAGE, au_free_page); #undef AU_CACHE_DFREE_DO_BODY #undef AU_DFREE_DO_BODY } AU_CACHE_DFREE_FUNC(dinfo, DINFO, di_lnode); AU_CACHE_DFREE_FUNC(icntnr, ICNTNR, lnode); AU_CACHE_DFREE_FUNC(finfo, FINFO, fi_lnode); AU_CACHE_DFREE_FUNC(vdir, VDIR, vd_lnode); AU_CACHE_DFREE_FUNC(vdir_dehstr, DEHSTR, lnode); static void au_cache_fin(void) { int i; struct au_cache *cp; /* * Make sure all delayed rcu free inodes are flushed before we * destroy cache. */ rcu_barrier(); /* excluding AuCache_HNOTIFY */ BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); flush_delayed_work(&au_dfree.dwork); for (i = 0; i < AuCache_HNOTIFY; i++) { cp = au_dfree.cache + i; AuDebugOn(!llist_empty(&cp->llist)); kmem_cache_destroy(cp->cache); cp->cache = NULL; } } static int __init au_cache_init(void) { struct au_cache *cp; cp = au_dfree.cache; cp[AuCache_DINFO].cache = AuCacheCtor(au_dinfo, au_di_init_once); if (cp[AuCache_DINFO].cache) /* SLAB_DESTROY_BY_RCU */ cp[AuCache_ICNTNR].cache = AuCacheCtor(au_icntnr, au_icntnr_init_once); if (cp[AuCache_ICNTNR].cache) cp[AuCache_FINFO].cache = AuCacheCtor(au_finfo, au_fi_init_once); if (cp[AuCache_FINFO].cache) cp[AuCache_VDIR].cache = AuCache(au_vdir); if (cp[AuCache_VDIR].cache) cp[AuCache_DEHSTR].cache = AuCache(au_vdir_dehstr); if (cp[AuCache_DEHSTR].cache) return 0; au_cache_fin(); return -ENOMEM; } /* ---------------------------------------------------------------------- */ int au_dir_roflags; #ifdef CONFIG_AUFS_SBILIST /* * iterate_supers_type() doesn't protect us from * remounting (branch management) */ struct au_sphlhead au_sbilist; #endif /* * functions for module interface. */ MODULE_LICENSE("GPL"); /* MODULE_LICENSE("GPL v2"); */ MODULE_AUTHOR("Junjiro R. Okajima "); MODULE_DESCRIPTION(AUFS_NAME " -- Advanced multi layered unification filesystem"); MODULE_VERSION(AUFS_VERSION); /* this module parameter has no meaning when SYSFS is disabled */ int sysaufs_brs = 1; MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); module_param_named(brs, sysaufs_brs, int, S_IRUGO); /* this module parameter has no meaning when USER_NS is disabled */ bool au_userns; MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); module_param_named(allow_userns, au_userns, bool, S_IRUGO); /* ---------------------------------------------------------------------- */ static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ int au_seq_path(struct seq_file *seq, struct path *path) { int err; err = seq_path(seq, path, au_esc_chars); if (err > 0) err = 0; else if (err < 0) err = -ENOMEM; return err; } /* ---------------------------------------------------------------------- */ static int __init aufs_init(void) { int err, i; char *p; struct au_cache *cp; p = au_esc_chars; for (i = 1; i <= ' '; i++) *p++ = i; *p++ = '\\'; *p++ = '\x7f'; *p = 0; au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); memcpy(aufs_iop_nogetattr, aufs_iop, sizeof(aufs_iop)); for (i = 0; i < AuIop_Last; i++) aufs_iop_nogetattr[i].getattr = NULL; /* First, initialize au_dfree */ for (i = 0; i < AuCache_Last; i++) { /* including hnotify */ cp = au_dfree.cache + i; cp->cache = NULL; init_llist_head(&cp->llist); } for (i = 0; i < AU_DFREE_Last; i++) init_llist_head(au_dfree.llist + i); INIT_DELAYED_WORK(&au_dfree.dwork, au_do_dfree); au_sbilist_init(); sysaufs_brs_init(); au_debug_init(); au_dy_init(); err = sysaufs_init(); if (unlikely(err)) goto out; err = au_procfs_init(); if (unlikely(err)) goto out_sysaufs; err = au_wkq_init(); if (unlikely(err)) goto out_procfs; err = au_loopback_init(); if (unlikely(err)) goto out_wkq; err = au_hnotify_init(); if (unlikely(err)) goto out_loopback; err = au_sysrq_init(); if (unlikely(err)) goto out_hin; err = au_cache_init(); if (unlikely(err)) goto out_sysrq; aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; err = register_filesystem(&aufs_fs_type); if (unlikely(err)) goto out_cache; /* since we define pr_fmt, call printk directly */ printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); goto out; /* success */ out_cache: au_cache_fin(); out_sysrq: au_sysrq_fin(); out_hin: au_hnotify_fin(); out_loopback: au_loopback_fin(); out_wkq: au_wkq_fin(); out_procfs: au_procfs_fin(); out_sysaufs: sysaufs_fin(); au_dy_fin(); flush_delayed_work(&au_dfree.dwork); out: return err; } static void __exit aufs_exit(void) { unregister_filesystem(&aufs_fs_type); au_cache_fin(); au_sysrq_fin(); au_hnotify_fin(); au_loopback_fin(); au_wkq_fin(); au_procfs_fin(); sysaufs_fin(); au_dy_fin(); flush_delayed_work(&au_dfree.dwork); } module_init(aufs_init); module_exit(aufs_exit);