[RFC v2 44/83] Log operation: invalidate log entries
From: Andiry Xu
Date: Sat Mar 10 2018 - 13:34:01 EST
From: Andiry Xu <jix024@xxxxxxxxxxx>
After new log entries are appended to the log, old log entries
can be marked invalid to faciliate garbage collection.
Signed-off-by: Andiry Xu <jix024@xxxxxxxxxxx>
---
fs/nova/log.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/nova/log.h | 4 ++
fs/nova/nova.h | 12 +++++
3 files changed, 176 insertions(+)
diff --git a/fs/nova/log.c b/fs/nova/log.c
index c8b7d2e..d150f2e 100644
--- a/fs/nova/log.c
+++ b/fs/nova/log.c
@@ -20,6 +20,88 @@
#include "inode.h"
#include "log.h"
+static int nova_execute_invalidate_reassign_logentry(struct super_block *sb,
+ void *entry, enum nova_entry_type type, int reassign,
+ unsigned int num_free)
+{
+ struct nova_file_write_entry *fw_entry;
+ int invalid = 0;
+
+ switch (type) {
+ case FILE_WRITE:
+ fw_entry = (struct nova_file_write_entry *)entry;
+ if (reassign)
+ fw_entry->reassigned = 1;
+ if (num_free)
+ fw_entry->invalid_pages += num_free;
+ if (fw_entry->invalid_pages == fw_entry->num_pages)
+ invalid = 1;
+ break;
+ case DIR_LOG:
+ if (reassign) {
+ ((struct nova_dentry *)entry)->reassigned = 1;
+ } else {
+ ((struct nova_dentry *)entry)->invalid = 1;
+ invalid = 1;
+ }
+ break;
+ case SET_ATTR:
+ ((struct nova_setattr_logentry *)entry)->invalid = 1;
+ invalid = 1;
+ break;
+ case LINK_CHANGE:
+ ((struct nova_link_change_entry *)entry)->invalid = 1;
+ invalid = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (invalid) {
+ u64 addr = nova_get_addr_off(NOVA_SB(sb), entry);
+
+ nova_inc_page_invalid_entries(sb, addr);
+ }
+
+ nova_persist_entry(entry);
+ return 0;
+}
+
+static int nova_invalidate_reassign_logentry(struct super_block *sb,
+ void *entry, enum nova_entry_type type, int reassign,
+ unsigned int num_free)
+{
+ nova_execute_invalidate_reassign_logentry(sb, entry, type,
+ reassign, num_free);
+ return 0;
+}
+
+static int nova_invalidate_logentry(struct super_block *sb, void *entry,
+ enum nova_entry_type type, unsigned int num_free)
+{
+ return nova_invalidate_reassign_logentry(sb, entry, type, 0, num_free);
+}
+
+static int nova_reassign_logentry(struct super_block *sb, void *entry,
+ enum nova_entry_type type)
+{
+ return nova_invalidate_reassign_logentry(sb, entry, type, 1, 0);
+}
+
+static inline int nova_invalidate_write_entry(struct super_block *sb,
+ struct nova_file_write_entry *entry, int reassign,
+ unsigned int num_free)
+{
+ if (!entry)
+ return 0;
+
+ if (num_free == 0 && entry->reassigned == 1)
+ return 0;
+
+ return nova_invalidate_reassign_logentry(sb, entry, FILE_WRITE,
+ reassign, num_free);
+}
+
static void nova_update_setattr_entry(struct inode *inode,
struct nova_setattr_logentry *entry,
struct nova_log_entry_info *entry_info)
@@ -279,6 +361,27 @@ static int nova_append_setattr_entry(struct super_block *sb,
return ret;
}
+/* Invalidate old setattr entry */
+static int nova_invalidate_setattr_entry(struct super_block *sb,
+ u64 last_setattr)
+{
+ struct nova_setattr_logentry *old_entry;
+ void *addr;
+ int ret;
+
+ addr = (void *)nova_get_block(sb, last_setattr);
+ old_entry = (struct nova_setattr_logentry *)addr;
+
+ /* Do not invalidate setsize entries */
+ if (!old_entry_freeable(sb, old_entry->epoch_id) ||
+ (old_entry->attr & ATTR_SIZE))
+ return 0;
+
+ ret = nova_invalidate_logentry(sb, old_entry, SET_ATTR, 0);
+
+ return ret;
+}
+
static int nova_can_inplace_update_setattr(struct super_block *sb,
struct nova_inode_info_header *sih, u64 epoch_id)
{
@@ -358,9 +461,35 @@ int nova_handle_setattr_operation(struct super_block *sb, struct inode *inode,
nova_update_inode(sb, inode, pi, &update);
}
+ /* Invalidate old setattr entry */
+ if (last_setattr)
+ nova_invalidate_setattr_entry(sb, last_setattr);
+
return 0;
}
+/* Invalidate old link change entry */
+int nova_invalidate_link_change_entry(struct super_block *sb,
+ u64 old_link_change)
+{
+ struct nova_link_change_entry *old_entry;
+ void *addr;
+ int ret;
+
+ if (old_link_change == 0)
+ return 0;
+
+ addr = (void *)nova_get_block(sb, old_link_change);
+ old_entry = (struct nova_link_change_entry *)addr;
+
+ if (!old_entry_freeable(sb, old_entry->epoch_id))
+ return 0;
+
+ ret = nova_invalidate_logentry(sb, old_entry, LINK_CHANGE, 0);
+
+ return ret;
+}
+
static int nova_can_inplace_update_lcentry(struct super_block *sb,
struct nova_inode_info_header *sih, u64 epoch_id)
{
@@ -481,6 +610,37 @@ int nova_append_file_write_entry(struct super_block *sb, struct nova_inode *pi,
return ret;
}
+/* Create dentry and delete dentry must be invalidated together */
+int nova_invalidate_dentries(struct super_block *sb,
+ struct nova_inode_update *update)
+{
+ struct nova_sb_info *sbi = NOVA_SB(sb);
+ struct nova_dentry *create_dentry;
+ struct nova_dentry *delete_dentry;
+ u64 create_curr, delete_curr;
+ int ret;
+
+ create_dentry = update->create_dentry;
+ delete_dentry = update->delete_dentry;
+
+ if (!create_dentry)
+ return 0;
+
+ nova_reassign_logentry(sb, create_dentry, DIR_LOG);
+
+ if (!old_entry_freeable(sb, create_dentry->epoch_id))
+ return 0;
+
+ create_curr = nova_get_addr_off(sbi, create_dentry);
+ delete_curr = nova_get_addr_off(sbi, delete_dentry);
+
+ nova_invalidate_logentry(sb, create_dentry, DIR_LOG, 0);
+
+ ret = nova_invalidate_logentry(sb, delete_dentry, DIR_LOG, 0);
+
+ return ret;
+}
+
int nova_inplace_update_dentry(struct super_block *sb,
struct inode *dir, struct nova_dentry *dentry, int link_change,
u64 epoch_id)
diff --git a/fs/nova/log.h b/fs/nova/log.h
index 74891b3..2548083 100644
--- a/fs/nova/log.h
+++ b/fs/nova/log.h
@@ -367,6 +367,8 @@ static inline int is_dir_init_entry(struct super_block *sb,
int nova_handle_setattr_operation(struct super_block *sb, struct inode *inode,
struct nova_inode *pi, unsigned int ia_valid, struct iattr *attr,
u64 epoch_id);
+int nova_invalidate_link_change_entry(struct super_block *sb,
+ u64 old_link_change);
int nova_append_link_change_entry(struct super_block *sb,
struct nova_inode *pi, struct inode *inode,
struct nova_inode_update *update, u64 *old_linkc, u64 epoch_id);
@@ -376,6 +378,8 @@ int nova_inplace_update_write_entry(struct super_block *sb,
int nova_append_file_write_entry(struct super_block *sb, struct nova_inode *pi,
struct inode *inode, struct nova_file_write_item *item,
struct nova_inode_update *update);
+int nova_invalidate_dentries(struct super_block *sb,
+ struct nova_inode_update *update);
int nova_inplace_update_dentry(struct super_block *sb,
struct inode *dir, struct nova_dentry *dentry, int link_change,
u64 epoch_id);
diff --git a/fs/nova/nova.h b/fs/nova/nova.h
index 03c4991..6cf3c33 100644
--- a/fs/nova/nova.h
+++ b/fs/nova/nova.h
@@ -328,6 +328,18 @@ struct inode_map {
int freed;
};
+
+/* Old entry is freeable if it is appended after the latest snapshot */
+static inline int old_entry_freeable(struct super_block *sb, u64 epoch_id)
+{
+ struct nova_sb_info *sbi = NOVA_SB(sb);
+
+ if (epoch_id == sbi->s_epoch_id)
+ return 1;
+
+ return 0;
+}
+
#include "balloc.h"
static inline unsigned long
--
2.7.4