*/
+#define pr_fmt(fmt) "SWAP_DCACHE: " fmt
+
#include <linux/dcache.h>
#include <linux/namei.h>
#include "swap_dcache.h"
-static atomic_t g_counter = ATOMIC_INIT(0);
-
-
-struct dentry *swap_dget_by_path(const char *filepath)
+static struct dentry *do_dget_by_path(const char *filepath)
{
struct path path;
struct dentry *dentry = NULL;
if (kern_path(filepath, LOOKUP_FOLLOW, &path) == 0) {
- dentry = swap_dget(path.dentry);
+ dentry = dget(path.dentry);
path_put(&path);
}
return dentry;
}
+
+
+#ifndef SWAP_DCACHE_DEBUG
+
+static atomic_t g_counter = ATOMIC_INIT(0);
+
+
+struct dentry *swap_dget_by_path(const char *filepath)
+{
+ struct dentry *dentry = do_dget_by_path(filepath);
+ if (dentry)
+ atomic_inc(&g_counter);
+
+ return dentry;
+}
EXPORT_SYMBOL_GPL(swap_dget_by_path);
struct dentry *swap_dget(struct dentry *dentry)
WARN(counter, "Incorrect dentry usage, counter=%d", counter);
BUG_ON(counter < 0);
}
+
+
+#else /* SWAP_DCACHE_DEBUG */
+
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+
+struct ddata {
+ struct list_head list;
+
+ struct dentry *dentry;
+ const char *module;
+ const char *file;
+ int line;
+
+ bool is_get;
+};
+
+
+static DEFINE_SPINLOCK(ddata_lock);
+static LIST_HEAD(ddata_head);
+
+
+static void add_to_list(struct dentry *dentry, bool is_get,
+ const char *module, const char *file, int line)
+{
+ unsigned long flags;
+ struct ddata *d;
+
+ /* skip dput() if dentry == NULL */
+ if (!dentry && !is_get)
+ return;
+
+ d = kmalloc(sizeof(*d), GFP_KERNEL);
+ BUG_ON(!d);
+
+ INIT_LIST_HEAD(&d->list);
+ d->dentry = dentry;
+ d->module = module;
+ d->file = file;
+ d->line = line;
+ d->is_get = is_get;
+
+ /* add to list */
+ spin_lock_irqsave(&ddata_lock, flags);
+ list_add_tail(&d->list, &ddata_head);
+ spin_unlock_irqrestore(&ddata_lock, flags);
+}
+
+static void free_list(void)
+{
+ struct ddata *d, *tmp;
+ list_for_each_entry_safe(d, tmp, &ddata_head, list) {
+ if (d->is_get)
+ dput(d->dentry);
+ list_del(&d->list);
+ kfree(d);
+ }
+}
+
+static void show_dentry_status(struct dentry *dentry, struct list_head *head)
+{
+ struct ddata *d;
+ int counter = 0;
+
+ /* check on dput() without dget() */
+ list_for_each_entry(d, head, list) {
+ counter += d->is_get ? 1 : -1;
+ if (counter < 0) {
+ pr_err("BUG: call dput() without dget() in %s[%s:%d]\n",
+ d->module, d->file, d->line);
+ }
+ }
+
+ /* check counter */
+ if (counter) {
+ pr_err("BUG: incorrect dentry[%p %s] counter=%d:\n",
+ dentry, dentry->d_name.name, counter);
+
+ list_for_each_entry(d, head, list) {
+ pr_err(" %s %s[%s:%d]\n",
+ d->is_get ? "DGET" : "DPUT",
+ d->module, d->file, d->line);
+ }
+ }
+}
+
+static void show_status(void)
+{
+ LIST_HEAD(tmp_head);
+
+ while (!list_empty(&ddata_head)) {
+ struct ddata *d, *tmp;
+ struct ddata *test_d;
+ LIST_HEAD(test_head);
+
+ test_d = list_first_entry(&ddata_head, struct ddata, list);
+ list_move_tail(&test_d->list, &test_head);
+
+ list_for_each_entry_safe(d, tmp, &ddata_head, list) {
+ if (test_d->dentry == d->dentry)
+ list_move_tail(&d->list, &test_head);
+ }
+
+ show_dentry_status(test_d->dentry, &test_head);
+ list_splice_tail_init(&test_head, &tmp_head);
+ }
+
+ list_splice_tail(&tmp_head, &ddata_head);
+}
+
+static bool is_enable = false;
+
+void check_on_enable(const char *cmd, const char *module,
+ const char *file, int line)
+{
+ if (!is_enable) {
+ pr_err("BUG: call %s() without initialization %s[%s:%d]\n",
+ cmd, module, file, line);
+ }
+}
+
+struct dentry *__swap_dget_by_path(const char *filepath,
+ const char *module,
+ const char *file, int line)
+{
+ struct dentry *dentry;
+
+ check_on_enable("DGET", module, file, line);
+
+ dentry = do_dget_by_path(filepath);
+ if (dentry)
+ add_to_list(dentry, true, module, file, line);
+
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(__swap_dget_by_path);
+
+struct dentry *__swap_dget(struct dentry *dentry,
+ const char *module,
+ const char *file, int line)
+{
+ check_on_enable("DGET", module, file, line);
+
+ if (dentry) {
+ dget(dentry);
+ add_to_list(dentry, true, module, file, line);
+ }
+
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(__swap_dget);
+
+void __swap_dput(struct dentry *dentry,
+ const char *module,
+ const char *file, int line)
+{
+ check_on_enable("DPUT", module, file, line);
+
+ if (dentry)
+ add_to_list(dentry, false, module, file, line);
+}
+EXPORT_SYMBOL_GPL(__swap_dput);
+
+int swap_dentry_init(void)
+{
+ is_enable = true;
+ return 0;
+}
+
+void swap_dentry_uninit(void)
+{
+ is_enable = false;
+ show_status();
+ free_list();
+}
+
+
+#endif /* SWAP_DCACHE_DEBUG */
struct dentry;
+/*
+ * Set "#define SWAP_DCACHE_DEBUG" for dcache debugging.
+ */
+
+#ifndef SWAP_DCACHE_DEBUG
+
+
struct dentry *swap_dget_by_path(const char *filepath);
struct dentry *swap_dget(struct dentry *dentry);
void swap_dput(struct dentry *dentry);
+
+#else /* SWAP_DCACHE_DEBUG */
+
+
+#define swap_dget_by_path(filepath) \
+ __swap_dget_by_path(filepath, KBUILD_MODNAME, __FILE__, __LINE__)
+#define swap_dget(dentry) \
+ __swap_dget(dentry, KBUILD_MODNAME, __FILE__, __LINE__)
+#define swap_dput(dentry) \
+ __swap_dput(dentry, KBUILD_MODNAME, __FILE__, __LINE__)
+
+struct dentry *__swap_dget_by_path(const char *filepath, const char *module,
+ const char *file, int line);
+struct dentry *__swap_dget(struct dentry *dentry, const char *module,
+ const char *file, int line);
+void __swap_dput(struct dentry *dentry, const char *module,
+ const char *file, int line);
+
+
+#endif /* SWAP_DCACHE_DEBUG */
+
+
int swap_dentry_init(void);
void swap_dentry_uninit(void);