/* Append . and .. entries */ int nova_append_dir_init_entries(struct super_block *sb, struct nova_inode *pi, u64 self_ino, u64 parent_ino) { int allocated; u64 new_block; u64 curr_p; struct nova_dentry *de_entry; if (pi->log_head) { nova_dbg("%s: log head exists @ 0x%llx!\n", __func__, pi->log_head); return - EINVAL; } allocated = nova_allocate_inode_log_pages(sb, pi, 1, &new_block); if (allocated != 1) { nova_err(sb, "ERROR: no inode log page available\n"); return - ENOMEM; } pi->log_tail = pi->log_head = new_block; pi->i_blocks = 1; nova_flush_buffer(&pi->log_head, CACHELINE_SIZE, 0); de_entry = (struct nova_dentry *)nova_get_block(sb, new_block); de_entry->entry_type = DIR_LOG; de_entry->ino = cpu_to_le64(self_ino); de_entry->name_len = 1; de_entry->de_len = cpu_to_le16(NOVA_DIR_LOG_REC_LEN(1)); de_entry->mtime = CURRENT_TIME_SEC.tv_sec; de_entry->size = sb->s_blocksize; de_entry->links_count = 1; strncpy(de_entry->name, ".\0", 2); nova_flush_buffer(de_entry, NOVA_DIR_LOG_REC_LEN(1), 0); curr_p = new_block + NOVA_DIR_LOG_REC_LEN(1); de_entry = (struct nova_dentry *)((char *)de_entry + le16_to_cpu(de_entry->de_len)); de_entry->entry_type = DIR_LOG; de_entry->ino = cpu_to_le64(parent_ino); de_entry->name_len = 2; de_entry->de_len = cpu_to_le16(NOVA_DIR_LOG_REC_LEN(2)); de_entry->mtime = CURRENT_TIME_SEC.tv_sec; de_entry->size = sb->s_blocksize; de_entry->links_count = 2; strncpy(de_entry->name, "..\0", 3); nova_flush_buffer(de_entry, NOVA_DIR_LOG_REC_LEN(2), 0); curr_p += NOVA_DIR_LOG_REC_LEN(2); nova_update_tail(pi, curr_p); return 0; }
static u64 nova_append_range_node_entry(struct super_block *sb, struct nova_range_node *curr, u64 tail, unsigned long cpuid) { u64 curr_p; size_t size = sizeof(struct nova_range_node_lowhigh); struct nova_range_node_lowhigh *entry; curr_p = tail; if (curr_p == 0 || (is_last_entry(curr_p, size) && next_log_page(sb, curr_p) == 0)) { nova_dbg("%s: inode log reaches end?\n", __func__); goto out; } if (is_last_entry(curr_p, size)) curr_p = next_log_page(sb, curr_p); entry = (struct nova_range_node_lowhigh *)nova_get_block(sb, curr_p); entry->range_low = cpu_to_le64(curr->range_low); if (cpuid) entry->range_low |= cpu_to_le64(cpuid << 56); entry->range_high = cpu_to_le64(curr->range_high); nova_dbgv("append entry block low 0x%lx, high 0x%lx\n", curr->range_low, curr->range_high); nova_flush_buffer(entry, sizeof(struct nova_range_node_lowhigh), 0); out: return curr_p; }
/* * Append a nova_dentry to the current nova_inode_log_page. * Note unlike append_file_write_entry(), this method returns the tail pointer * after append. */ static u64 nova_append_dir_inode_entry(struct super_block *sb, struct nova_inode *pidir, struct inode *dir, u64 ino, struct dentry *dentry, unsigned short de_len, u64 tail, int link_change, u64 *curr_tail) { struct nova_inode_info *si = NOVA_I(dir); struct nova_inode_info_header *sih = &si->header; struct nova_dentry *entry; u64 curr_p; size_t size = de_len; int extended = 0; unsigned short links_count; timing_t append_time; NOVA_START_TIMING(append_dir_entry_t, append_time); curr_p = nova_get_append_head(sb, pidir, sih, tail, size, &extended); if (curr_p == 0) BUG(); entry = (struct nova_dentry *)nova_get_block(sb, curr_p); entry->entry_type = DIR_LOG; entry->ino = cpu_to_le64(ino); entry->name_len = dentry->d_name.len; memcpy_to_pmem_nocache(entry->name, dentry->d_name.name, dentry->d_name.len); entry->name[dentry->d_name.len] = '\0'; entry->file_type = 0; entry->invalid = 0; entry->mtime = cpu_to_le32(dir->i_mtime.tv_sec); entry->size = cpu_to_le64(dir->i_size); links_count = cpu_to_le16(dir->i_nlink); if (links_count == 0 && link_change == -1) links_count = 0; else links_count += link_change; entry->links_count = cpu_to_le16(links_count); /* Update actual de_len */ entry->de_len = cpu_to_le16(de_len); nova_dbg_verbose("dir entry @ 0x%llx: ino %llu, entry len %u, " "name len %u, file type %u\n", curr_p, entry->ino, entry->de_len, entry->name_len, entry->file_type); nova_flush_buffer(entry, de_len, 0); *curr_tail = curr_p + de_len; dir->i_blocks = pidir->i_blocks; NOVA_END_TIMING(append_dir_entry_t, append_time); return curr_p; }
void nova_save_inode_list_to_log(struct super_block *sb) { struct nova_inode *pi = nova_get_inode_by_ino(sb, NOVA_INODELIST1_INO); struct nova_sb_info *sbi = NOVA_SB(sb); unsigned long num_blocks; unsigned long num_nodes = 0; struct inode_map *inode_map; unsigned long i; u64 temp_tail; u64 new_block; int allocated; for (i = 0; i < sbi->cpus; i++) { inode_map = &sbi->inode_maps[i]; num_nodes += inode_map->num_range_node_inode; } num_blocks = num_nodes / RANGENODE_PER_PAGE; if (num_nodes % RANGENODE_PER_PAGE) num_blocks++; allocated = nova_allocate_inode_log_pages(sb, pi, num_blocks, &new_block); if (allocated != num_blocks) { nova_dbg("Error saving inode list: %d\n", allocated); return; } pi->log_head = new_block; nova_flush_buffer(&pi->log_head, CACHELINE_SIZE, 0); temp_tail = new_block; for (i = 0; i < sbi->cpus; i++) { inode_map = &sbi->inode_maps[i]; temp_tail = nova_save_range_nodes_to_log(sb, &inode_map->inode_inuse_tree, temp_tail, i); } nova_update_tail(pi, temp_tail); nova_dbg("%s: %lu inode nodes, pi head 0x%llx, tail 0x%llx\n", __func__, num_nodes, pi->log_head, pi->log_tail); }
void nova_save_blocknode_mappings_to_log(struct super_block *sb) { struct nova_inode *pi = nova_get_inode_by_ino(sb, NOVA_BLOCKNODE_INO); struct nova_sb_info *sbi = NOVA_SB(sb); struct nova_super_block *super; struct free_list *free_list; unsigned long num_blocknode = 0; unsigned long num_pages; int allocated; u64 new_block = 0; u64 temp_tail; int i; /* Allocate log pages before save blocknode mappings */ for (i = 0; i < sbi->cpus; i++) { free_list = nova_get_free_list(sb, i); num_blocknode += free_list->num_blocknode; nova_dbgv("%s: free list %d: %lu nodes\n", __func__, i, free_list->num_blocknode); } free_list = nova_get_free_list(sb, SHARED_CPU); num_blocknode += free_list->num_blocknode; nova_dbgv("%s: shared list: %lu nodes\n", __func__, free_list->num_blocknode); num_pages = num_blocknode / RANGENODE_PER_PAGE; if (num_blocknode % RANGENODE_PER_PAGE) num_pages++; allocated = nova_allocate_inode_log_pages(sb, pi, num_pages, &new_block); if (allocated != num_pages) { nova_dbg("Error saving blocknode mappings: %d\n", allocated); return; } /* * save the total allocated blocknode mappings * in super block * No transaction is needed as we will recover the fields * via failure recovery */ super = nova_get_super(sb); nova_memunlock_range(sb, &super->s_wtime, NOVA_FAST_MOUNT_FIELD_SIZE); super->s_wtime = cpu_to_le32(get_seconds()); nova_memlock_range(sb, &super->s_wtime, NOVA_FAST_MOUNT_FIELD_SIZE); nova_flush_buffer(super, NOVA_SB_SIZE, 0); /* Finally update log head and tail */ pi->log_head = new_block; nova_flush_buffer(&pi->log_head, CACHELINE_SIZE, 0); temp_tail = new_block; for (i = 0; i < sbi->cpus; i++) { temp_tail = nova_save_free_list_blocknodes(sb, i, temp_tail); } temp_tail = nova_save_free_list_blocknodes(sb, SHARED_CPU, temp_tail); nova_update_tail(pi, temp_tail); nova_dbg("%s: %lu blocknodes, %lu log pages, pi head 0x%llx, " "tail 0x%llx\n", __func__, num_blocknode, num_pages, pi->log_head, pi->log_tail); }
int nova_rebuild_dir_inode_tree(struct super_block *sb, struct nova_inode *pi, u64 pi_addr, struct nova_inode_info_header *sih) { struct nova_dentry *entry = NULL; struct nova_setattr_logentry *attr_entry = NULL; struct nova_link_change_entry *link_change_entry = NULL; struct nova_inode_log_page *curr_page; u64 ino = pi->nova_ino; unsigned short de_len; timing_t rebuild_time; void *addr; u64 curr_p; u64 next; u8 type; int ret; NOVA_START_TIMING(rebuild_dir_t, rebuild_time); nova_dbg_verbose("Rebuild dir %llu tree\n", ino); sih->pi_addr = pi_addr; curr_p = pi->log_head; if (curr_p == 0) { nova_err(sb, "Dir %llu log is NULL!\n", ino); BUG(); } nova_dbg_verbose("Log head 0x%llx, tail 0x%llx\n", curr_p, pi->log_tail); sih->log_pages = 1; while (curr_p != pi->log_tail) { if (goto_next_page(sb, curr_p)) { sih->log_pages++; curr_p = next_log_page(sb, curr_p); } if (curr_p == 0) { nova_err(sb, "Dir %llu log is NULL!\n", ino); BUG(); } addr = (void *)nova_get_block(sb, curr_p); type = nova_get_entry_type(addr); switch (type) { case SET_ATTR: attr_entry = (struct nova_setattr_logentry *)addr; nova_apply_setattr_entry(sb, pi, sih, attr_entry); sih->last_setattr = curr_p; curr_p += sizeof(struct nova_setattr_logentry); continue; case LINK_CHANGE: link_change_entry = (struct nova_link_change_entry *)addr; nova_apply_link_change_entry(pi, link_change_entry); sih->last_link_change = curr_p; curr_p += sizeof(struct nova_link_change_entry); continue; case DIR_LOG: break; default: nova_dbg("%s: unknown type %d, 0x%llx\n", __func__, type, curr_p); NOVA_ASSERT(0); } entry = (struct nova_dentry *)nova_get_block(sb, curr_p); nova_dbgv("curr_p: 0x%llx, type %d, ino %llu, " "name %s, namelen %u, rec len %u\n", curr_p, entry->entry_type, le64_to_cpu(entry->ino), entry->name, entry->name_len, le16_to_cpu(entry->de_len)); if (entry->ino > 0) { if (entry->invalid == 0) { /* A valid entry to add */ ret = nova_replay_add_dentry(sb, sih, entry); } } else { /* Delete the entry */ ret = nova_replay_remove_dentry(sb, sih, entry); } if (ret) { nova_err(sb, "%s ERROR %d\n", __func__, ret); break; } nova_rebuild_dir_time_and_size(sb, pi, entry); de_len = le16_to_cpu(entry->de_len); curr_p += de_len; } sih->i_size = le64_to_cpu(pi->i_size); sih->i_mode = le64_to_cpu(pi->i_mode); nova_flush_buffer(pi, sizeof(struct nova_inode), 0); /* Keep traversing until log ends */ curr_p &= PAGE_MASK; curr_page = (struct nova_inode_log_page *)nova_get_block(sb, curr_p); while ((next = curr_page->page_tail.next_page) != 0) { sih->log_pages++; curr_p = next; curr_page = (struct nova_inode_log_page *) nova_get_block(sb, curr_p); } pi->i_blocks = sih->log_pages; // nova_print_dir_tree(sb, sih, ino); NOVA_END_TIMING(rebuild_dir_t, rebuild_time); return 0; }