[RFC v2 78/83] GC: Thorough garbage collection.

2018-03-10 Thread Andiry Xu
From: Andiry Xu 

After fast gc, if the valid log entries still account for less
than the half of the log size, NOVA starts thorough garbage collection,
allocates a new log, copies the live log entries to it, and switches
to the new log atomically. The radix tree needs to be updated to point
to the new log.

Example:
I = Invalid, V = Valid

VIIV ->  -> VVII

 ||
 ||  fast gc
 \/

VIIV -> VVII

 ||
 ||  thorough gc
 \/



Signed-off-by: Andiry Xu 
---
 fs/nova/gc.c | 273 +++
 1 file changed, 273 insertions(+)

diff --git a/fs/nova/gc.c b/fs/nova/gc.c
index 1634c04..d74286e 100644
--- a/fs/nova/gc.c
+++ b/fs/nova/gc.c
@@ -18,6 +18,62 @@
 #include "nova.h"
 #include "inode.h"
 
+static bool curr_log_entry_invalid(struct super_block *sb,
+   struct nova_inode *pi, struct nova_inode_info_header *sih,
+   u64 curr_p, size_t *length)
+{
+   struct nova_file_write_entry *entry;
+   struct nova_dentry *dentry;
+   struct nova_setattr_logentry *setattr_entry;
+   struct nova_link_change_entry *linkc_entry;
+   void *entryc;
+   u8 type;
+   bool ret = true;
+
+   entryc = (void *)nova_get_block(sb, curr_p);
+   type = nova_get_entry_type(entryc);
+
+   switch (type) {
+   case SET_ATTR:
+   setattr_entry = (struct nova_setattr_logentry *) entryc;
+   if (setattr_entry->invalid == 0)
+   ret = false;
+   *length = sizeof(struct nova_setattr_logentry);
+   break;
+   case LINK_CHANGE:
+   linkc_entry = (struct nova_link_change_entry *) entryc;
+   if (linkc_entry->invalid == 0)
+   ret = false;
+   *length = sizeof(struct nova_link_change_entry);
+   break;
+   case FILE_WRITE:
+   entry = (struct nova_file_write_entry *) entryc;
+   if (entry->num_pages != entry->invalid_pages)
+   ret = false;
+   *length = sizeof(struct nova_file_write_entry);
+   break;
+   case DIR_LOG:
+   dentry = (struct nova_dentry *) entryc;
+   if (dentry->invalid == 0)
+   ret = false;
+   if (sih->last_dentry == curr_p)
+   ret = false;
+   *length = le16_to_cpu(dentry->de_len);
+   break;
+   case NEXT_PAGE:
+   /* No more entries in this page */
+   *length = PAGE_SIZE - ENTRY_LOC(curr_p);
+   break;
+   default:
+   nova_dbg("%s: unknown type %d, 0x%llx\n",
+   __func__, type, curr_p);
+   NOVA_ASSERT(0);
+   *length = PAGE_SIZE - ENTRY_LOC(curr_p);
+   break;
+   }
+
+   return ret;
+}
 
 static bool curr_page_invalid(struct super_block *sb,
struct nova_inode *pi, struct nova_inode_info_header *sih,
@@ -68,6 +124,210 @@ static void free_curr_page(struct super_block *sb,
nova_get_blocknr(sb, curr_head, btype), 1);
 }
 
+static int nova_gc_assign_file_entry(struct super_block *sb,
+   struct nova_inode_info_header *sih,
+   struct nova_file_write_entry *old_entry,
+   struct nova_file_write_entry *new_entry)
+{
+   struct nova_file_write_entry *temp;
+   void **pentry;
+   unsigned long start_pgoff = old_entry->pgoff;
+   unsigned int num = old_entry->num_pages;
+   unsigned long curr_pgoff;
+   int i;
+   int ret = 0;
+
+   for (i = 0; i < num; i++) {
+   curr_pgoff = start_pgoff + i;
+
+   pentry = radix_tree_lookup_slot(>tree, curr_pgoff);
+   if (pentry) {
+   temp = radix_tree_deref_slot(pentry);
+   if (temp == old_entry)
+   radix_tree_replace_slot(>tree, pentry,
+   new_entry);
+   }
+   }
+
+   return ret;
+}
+
+static int nova_gc_assign_dentry(struct super_block *sb,
+   struct nova_inode_info_header *sih, struct nova_dentry *old_dentry,
+   struct nova_dentry *new_dentry)
+{
+   struct nova_dentry *temp;
+   void **pentry;
+   unsigned long hash;
+   int ret = 0;
+
+   hash = BKDRHash(old_dentry->name, old_dentry->name_len);
+   nova_dbgv("%s: assign %s hash %lu\n", __func__,
+   old_dentry->name, hash);
+
+   /* FIXME: hash collision ignored here */
+   pentry = radix_tree_lookup_slot(>tree, hash);
+   if (pentry) {
+   temp = radix_tree_deref_slot(pentry);
+   if (temp == old_dentry)
+   radix_tree_replace_slot(>tree, pentry, new_dentry);
+   }
+
+   return ret;
+}
+
+static int nova_gc_assign_new_entry(struct super_block *sb,
+   

[RFC v2 78/83] GC: Thorough garbage collection.

2018-03-10 Thread Andiry Xu
From: Andiry Xu 

After fast gc, if the valid log entries still account for less
than the half of the log size, NOVA starts thorough garbage collection,
allocates a new log, copies the live log entries to it, and switches
to the new log atomically. The radix tree needs to be updated to point
to the new log.

Example:
I = Invalid, V = Valid

VIIV ->  -> VVII

 ||
 ||  fast gc
 \/

VIIV -> VVII

 ||
 ||  thorough gc
 \/



Signed-off-by: Andiry Xu 
---
 fs/nova/gc.c | 273 +++
 1 file changed, 273 insertions(+)

diff --git a/fs/nova/gc.c b/fs/nova/gc.c
index 1634c04..d74286e 100644
--- a/fs/nova/gc.c
+++ b/fs/nova/gc.c
@@ -18,6 +18,62 @@
 #include "nova.h"
 #include "inode.h"
 
+static bool curr_log_entry_invalid(struct super_block *sb,
+   struct nova_inode *pi, struct nova_inode_info_header *sih,
+   u64 curr_p, size_t *length)
+{
+   struct nova_file_write_entry *entry;
+   struct nova_dentry *dentry;
+   struct nova_setattr_logentry *setattr_entry;
+   struct nova_link_change_entry *linkc_entry;
+   void *entryc;
+   u8 type;
+   bool ret = true;
+
+   entryc = (void *)nova_get_block(sb, curr_p);
+   type = nova_get_entry_type(entryc);
+
+   switch (type) {
+   case SET_ATTR:
+   setattr_entry = (struct nova_setattr_logentry *) entryc;
+   if (setattr_entry->invalid == 0)
+   ret = false;
+   *length = sizeof(struct nova_setattr_logentry);
+   break;
+   case LINK_CHANGE:
+   linkc_entry = (struct nova_link_change_entry *) entryc;
+   if (linkc_entry->invalid == 0)
+   ret = false;
+   *length = sizeof(struct nova_link_change_entry);
+   break;
+   case FILE_WRITE:
+   entry = (struct nova_file_write_entry *) entryc;
+   if (entry->num_pages != entry->invalid_pages)
+   ret = false;
+   *length = sizeof(struct nova_file_write_entry);
+   break;
+   case DIR_LOG:
+   dentry = (struct nova_dentry *) entryc;
+   if (dentry->invalid == 0)
+   ret = false;
+   if (sih->last_dentry == curr_p)
+   ret = false;
+   *length = le16_to_cpu(dentry->de_len);
+   break;
+   case NEXT_PAGE:
+   /* No more entries in this page */
+   *length = PAGE_SIZE - ENTRY_LOC(curr_p);
+   break;
+   default:
+   nova_dbg("%s: unknown type %d, 0x%llx\n",
+   __func__, type, curr_p);
+   NOVA_ASSERT(0);
+   *length = PAGE_SIZE - ENTRY_LOC(curr_p);
+   break;
+   }
+
+   return ret;
+}
 
 static bool curr_page_invalid(struct super_block *sb,
struct nova_inode *pi, struct nova_inode_info_header *sih,
@@ -68,6 +124,210 @@ static void free_curr_page(struct super_block *sb,
nova_get_blocknr(sb, curr_head, btype), 1);
 }
 
+static int nova_gc_assign_file_entry(struct super_block *sb,
+   struct nova_inode_info_header *sih,
+   struct nova_file_write_entry *old_entry,
+   struct nova_file_write_entry *new_entry)
+{
+   struct nova_file_write_entry *temp;
+   void **pentry;
+   unsigned long start_pgoff = old_entry->pgoff;
+   unsigned int num = old_entry->num_pages;
+   unsigned long curr_pgoff;
+   int i;
+   int ret = 0;
+
+   for (i = 0; i < num; i++) {
+   curr_pgoff = start_pgoff + i;
+
+   pentry = radix_tree_lookup_slot(>tree, curr_pgoff);
+   if (pentry) {
+   temp = radix_tree_deref_slot(pentry);
+   if (temp == old_entry)
+   radix_tree_replace_slot(>tree, pentry,
+   new_entry);
+   }
+   }
+
+   return ret;
+}
+
+static int nova_gc_assign_dentry(struct super_block *sb,
+   struct nova_inode_info_header *sih, struct nova_dentry *old_dentry,
+   struct nova_dentry *new_dentry)
+{
+   struct nova_dentry *temp;
+   void **pentry;
+   unsigned long hash;
+   int ret = 0;
+
+   hash = BKDRHash(old_dentry->name, old_dentry->name_len);
+   nova_dbgv("%s: assign %s hash %lu\n", __func__,
+   old_dentry->name, hash);
+
+   /* FIXME: hash collision ignored here */
+   pentry = radix_tree_lookup_slot(>tree, hash);
+   if (pentry) {
+   temp = radix_tree_deref_slot(pentry);
+   if (temp == old_dentry)
+   radix_tree_replace_slot(>tree, pentry, new_dentry);
+   }
+
+   return ret;
+}
+
+static int nova_gc_assign_new_entry(struct super_block *sb,
+   struct nova_inode *pi, struct