fs: flush VFS cache on node deactivation
authorDavid Herrmann <dh.herrmann@gmail.com>
Mon, 22 Dec 2014 16:18:32 +0000 (17:18 +0100)
committerDavid Herrmann <dh.herrmann@gmail.com>
Mon, 22 Dec 2014 16:19:40 +0000 (17:19 +0100)
Whenever a node is deactivated, we now invalidate any cached dentries.
This will make sure that we don't leave any dead entries in the VFS cache.
While this is not bad as is, it does make the cache slower. Therefore,
flush those entries as they will never be reused anyway.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
fs.c
fs.h
node.c

diff --git a/fs.c b/fs.c
index a3bac17d270720759a2d333d09ab675c1ee67521..7fc5edeab1d1d1baf1ecc2159b0b8c1eb7f3d834 100644 (file)
--- a/fs.c
+++ b/fs.c
@@ -429,3 +429,96 @@ void kdbus_fs_exit(void)
 {
        unregister_filesystem(&fs_type);
 }
+
+/* acquire domain of @node, making sure all ancestors are active */
+static struct kdbus_domain *fs_acquire_domain(struct kdbus_node *node)
+{
+       struct kdbus_domain *domain;
+       struct kdbus_node *iter;
+
+       /* caller must guarantee that @node is linked */
+       for (iter = node; iter->parent; iter = iter->parent)
+               if (!kdbus_node_is_active(iter->parent))
+                       return NULL;
+
+       /* root nodes are always domains */
+       if (WARN_ON(iter->type != KDBUS_NODE_DOMAIN))
+               return NULL;
+
+       domain = kdbus_domain_from_node(iter);
+       if (!kdbus_node_acquire(&domain->node))
+               return NULL;
+
+       return domain;
+}
+
+/**
+ * kdbus_fs_flush() - flush dcache entries of a node
+ * @node:              Node to flush entries of
+ *
+ * This flushes all VFS filesystem cache entries for a node and all its
+ * children. This should be called whenever a node is destroyed during
+ * runtime. It will flush the cache entries so the linked objects can be
+ * deallocated.
+ *
+ * This is a no-op if you call it on active nodes (they really should stay in
+ * cache) or on nodes with deactivated parents (flushing the parent is enough).
+ * Furthermore, there is no need to call it on nodes whose lifetime is bound to
+ * their parents'. In those cases, the parent-flush will always also flush the
+ * children.
+ */
+void kdbus_fs_flush(struct kdbus_node *node)
+{
+       struct dentry *dentry, *parent_dentry = NULL;
+       struct kdbus_domain *domain;
+       struct qstr name;
+
+       /* active nodes should remain in cache */
+       if (!kdbus_node_is_deactivated(node))
+               return;
+
+       /* nodes that were never linked were never instantiated */
+       if (!node->parent)
+               return;
+
+       /* acquire domain and verify all ancestors are active */
+       domain = fs_acquire_domain(node);
+       if (!domain)
+               return;
+
+       switch (node->type) {
+       case KDBUS_NODE_ENDPOINT:
+               if (WARN_ON(!node->parent || !node->parent->name))
+                       goto exit;
+
+               name.name = node->parent->name;
+               name.len = strlen(node->parent->name);
+               parent_dentry = d_hash_and_lookup(domain->dentry, &name);
+               if (IS_ERR_OR_NULL(parent_dentry))
+                       goto exit;
+
+               /* fallthrough */
+       case KDBUS_NODE_BUS:
+               if (WARN_ON(!node->name))
+                       goto exit;
+
+               name.name = node->name;
+               name.len = strlen(node->name);
+               dentry = d_hash_and_lookup(parent_dentry ? : domain->dentry,
+                                          &name);
+               if (!IS_ERR_OR_NULL(dentry)) {
+                       d_invalidate(dentry);
+                       dput(dentry);
+               }
+
+               dput(parent_dentry);
+               break;
+
+       default:
+               /* all other types are bound to their parent lifetime */
+               break;
+       }
+
+exit:
+       kdbus_node_release(&domain->node);
+}
diff --git a/fs.h b/fs.h
index 29bc9ea0732991b7d69633732878e656ddb685d1..5c38a5774392e7a768de4131b23615111d10a9e7 100644 (file)
--- a/fs.h
+++ b/fs.h
 
 #include <linux/kernel.h>
 
+struct kdbus_node;
+
 int kdbus_fs_init(void);
 void kdbus_fs_exit(void);
+void kdbus_fs_flush(struct kdbus_node *node);
 
 #endif
diff --git a/node.c b/node.c
index d500ae3537bb6ef24c57c00fbc6ce9a7921b1de4..2fd9cd09f3feb2e89421a9b778ef6ebec300a708 100644 (file)
--- a/node.c
+++ b/node.c
@@ -24,6 +24,7 @@
 #include "bus.h"
 #include "domain.h"
 #include "endpoint.h"
+#include "fs.h"
 #include "handle.h"
 #include "node.h"
 #include "util.h"
@@ -645,6 +646,9 @@ void kdbus_node_deactivate(struct kdbus_node *node)
                        atomic_set(&pos->active, KDBUS_NODE_DRAINED);
                        wake_up_all(&pos->waitq);
 
+                       /* drop VFS cache */
+                       kdbus_fs_flush(pos);
+
                        /*
                         * If the node was activated and somone subtracted BIAS
                         * from it to deactivate it, we, and only us, are