/* * redo_log_process -- (internal) process redo log entries */ void redo_log_process(PMEMobjpool *pop, struct redo_log *redo, size_t nentries) { LOG(15, "redo %p nentries %zu", redo, nentries); #ifdef DEBUG ASSERTeq(redo_log_check(pop, redo, nentries), 0); #endif uint64_t *val; while ((redo->offset & REDO_FINISH_FLAG) == 0) { val = (uint64_t *)((uintptr_t)pop->addr + redo->offset); VALGRIND_ADD_TO_TX(val, sizeof (*val)); *val = redo->value; VALGRIND_REMOVE_FROM_TX(val, sizeof (*val)); pop->flush(pop, val, sizeof (uint64_t)); redo++; } uint64_t offset = redo->offset & REDO_FLAG_MASK; val = (uint64_t *)((uintptr_t)pop->addr + offset); VALGRIND_ADD_TO_TX(val, sizeof (*val)); *val = redo->value; VALGRIND_REMOVE_FROM_TX(val, sizeof (*val)); pop->persist(pop, val, sizeof (uint64_t)); redo->offset = 0; pop->persist(pop, &redo->offset, sizeof (redo->offset)); }
/* * pmalloc_check -- consistency check of allocator lane section */ static int pmalloc_check(PMEMobjpool *pop, void *data, unsigned length) { LOG(3, "allocator lane %p", data); struct lane_alloc_layout *sec = data; int ret = redo_log_check(pop->redo, sec->redo, ALLOC_REDO_LOG_SIZE); if (ret != 0) ERR("allocator lane: redo log check failed"); return ret; }
/* * lane_allocator_check -- consistency check of allocator lane section */ static int lane_allocator_check(PMEMobjpool *pop, struct lane_section_layout *section) { LOG(3, "allocator lane %p", section); struct allocator_lane_section *sec = (struct allocator_lane_section *)section; int ret; if ((ret = redo_log_check(pop, sec->redo, MAX_ALLOC_OP_REDO)) != 0) ERR("allocator lane: redo log check failed"); return ret; }
int main(int argc, char *argv[]) { START(argc, argv, "obj_redo_log"); util_init(); if (argc < 4) FATAL_USAGE(); PMEMobjpool *pop = pmemobj_open_mock(argv[1]); UT_ASSERTne(pop, NULL); UT_ASSERTeq(util_is_zeroed((char *)pop->addr + PMEMOBJ_POOL_HDR_SIZE, pop->size - PMEMOBJ_POOL_HDR_SIZE), 1); char *end = NULL; errno = 0; size_t redo_size = strtoul(argv[2], &end, 0); if (errno || !end || *end != '\0') FATAL_USAGE(); UT_ASSERT(pop->size >= redo_size * sizeof(struct redo_log)); struct redo_log *redo = (struct redo_log *)pop->addr; uint64_t offset; uint64_t value; int i; int ret; size_t index; for (i = 3; i < argc; i++) { char *arg = argv[i]; UT_ASSERTne(arg, NULL); switch (arg[0]) { case 's': if (sscanf(arg, "s:%ld:0x%lx:0x%lx", &index, &offset, &value) != 3) FATAL_USAGE(); UT_OUT("s:%ld:0x%08lx:0x%08lx", index, offset, value); redo_log_store(pop, redo, index, offset, value); break; case 'f': if (sscanf(arg, "f:%ld:0x%lx:0x%lx", &index, &offset, &value) != 3) FATAL_USAGE(); UT_OUT("f:%ld:0x%08lx:0x%08lx", index, offset, value); redo_log_store_last(pop, redo, index, offset, value); break; case 'F': if (sscanf(arg, "F:%ld", &index) != 1) FATAL_USAGE(); UT_OUT("F:%ld", index); redo_log_set_last(pop, redo, index); break; case 'r': if (sscanf(arg, "r:0x%lx", &offset) != 1) FATAL_USAGE(); uint64_t *valp = (uint64_t *)((uintptr_t)pop->addr + offset); UT_OUT("r:0x%08lx:0x%08lx", offset, *valp); break; case 'e': if (sscanf(arg, "e:%ld", &index) != 1) FATAL_USAGE(); struct redo_log *entry = redo + index; int flag = (entry->offset & REDO_FINISH_FLAG) ? 1 : 0; offset = entry->offset & REDO_FLAG_MASK; value = entry->value; UT_OUT("e:%ld:0x%08lx:%d:0x%08lx", index, offset, flag, value); break; case 'P': redo_log_process(pop, redo, redo_size); UT_OUT("P"); break; case 'R': redo_log_recover(pop, redo, redo_size); UT_OUT("R"); break; case 'C': ret = redo_log_check(pop, redo, redo_size); UT_OUT("C:%d", ret); break; default: FATAL_USAGE(); } } pmemobj_close_mock(pop); DONE(NULL); }
/* * list_remove -- remove object from list * * pop - pmemobj handle * pe_offset - offset to list entry on user list relative to user data * head - list head * oid - target object ID */ int list_remove(PMEMobjpool *pop, ssize_t pe_offset, struct list_head *head, PMEMoid oid) { LOG(3, NULL); ASSERTne(head, NULL); int ret; struct lane_section *lane_section; lane_hold(pop, &lane_section, LANE_SECTION_LIST); ASSERTne(lane_section, NULL); ASSERTne(lane_section->layout, NULL); if ((ret = pmemobj_mutex_lock(pop, &head->lock))) { errno = ret; LOG(2, "pmemobj_mutex_lock failed"); ret = -1; goto err; } struct lane_list_layout *section = (struct lane_list_layout *)lane_section->layout; struct redo_log *redo = section->redo; size_t redo_index = 0; struct list_entry *entry_ptr = (struct list_entry *)OBJ_OFF_TO_PTR(pop, oid.off + (size_t)pe_offset); struct list_args_remove args = { .pe_offset = (ssize_t)pe_offset, .head = head, .entry_ptr = entry_ptr, .obj_doffset = oid.off, }; struct list_args_common args_common = { .obj_doffset = oid.off, .entry_ptr = entry_ptr, .pe_offset = (ssize_t)pe_offset, }; /* remove element from user list */ redo_index = list_remove_single(pop, redo, redo_index, &args); /* clear next and prev offsets in removing element using redo log */ redo_index = list_fill_entry_redo_log(pop, redo, redo_index, &args_common, 0, 0, 0); redo_log_set_last(pop->redo, redo, redo_index - 1); redo_log_process(pop->redo, redo, REDO_NUM_ENTRIES); pmemobj_mutex_unlock_nofail(pop, &head->lock); err: lane_release(pop); ASSERT(ret == 0 || ret == -1); return ret; } /* * list_move -- move object between two lists * * pop - pmemobj handle * pe_offset_old - offset to old list entry relative to user data * head_old - old list head * pe_offset_new - offset to new list entry relative to user data * head_new - new list head * dest - destination object ID * before - before/after destination * oid - target object ID */ int list_move(PMEMobjpool *pop, size_t pe_offset_old, struct list_head *head_old, size_t pe_offset_new, struct list_head *head_new, PMEMoid dest, int before, PMEMoid oid) { LOG(3, NULL); ASSERTne(head_old, NULL); ASSERTne(head_new, NULL); int ret; struct lane_section *lane_section; lane_hold(pop, &lane_section, LANE_SECTION_LIST); ASSERTne(lane_section, NULL); ASSERTne(lane_section->layout, NULL); /* * Grab locks in specified order to avoid dead-locks. * * XXX performance improvement: initialize oob locks at pool opening */ if ((ret = list_mutexes_lock(pop, head_new, head_old))) { errno = ret; LOG(2, "list_mutexes_lock failed"); ret = -1; goto err; } struct lane_list_layout *section = (struct lane_list_layout *)lane_section->layout; struct redo_log *redo = section->redo; size_t redo_index = 0; dest = list_get_dest(pop, head_new, dest, (ssize_t)pe_offset_new, before); struct list_entry *entry_ptr_old = (struct list_entry *)OBJ_OFF_TO_PTR(pop, oid.off + pe_offset_old); struct list_entry *entry_ptr_new = (struct list_entry *)OBJ_OFF_TO_PTR(pop, oid.off + pe_offset_new); struct list_entry *dest_entry_ptr = (struct list_entry *)OBJ_OFF_TO_PTR(pop, dest.off + pe_offset_new); if (head_old == head_new) { /* moving within the same list */ if (dest.off == oid.off) goto unlock; if (before && dest_entry_ptr->pe_prev.off == oid.off) { if (head_old->pe_first.off != dest.off) goto unlock; redo_index = list_update_head(pop, redo, redo_index, head_old, oid.off); goto redo_last; } if (!before && dest_entry_ptr->pe_next.off == oid.off) { if (head_old->pe_first.off != oid.off) goto unlock; redo_index = list_update_head(pop, redo, redo_index, head_old, entry_ptr_old->pe_next.off); goto redo_last; } } ASSERT((ssize_t)pe_offset_old >= 0); struct list_args_remove args_remove = { .pe_offset = (ssize_t)pe_offset_old, .head = head_old, .entry_ptr = entry_ptr_old, .obj_doffset = oid.off, }; struct list_args_insert args_insert = { .head = head_new, .dest = dest, .dest_entry_ptr = dest_entry_ptr, .before = before, }; ASSERT((ssize_t)pe_offset_new >= 0); struct list_args_common args_common = { .obj_doffset = oid.off, .entry_ptr = entry_ptr_new, .pe_offset = (ssize_t)pe_offset_new, }; uint64_t next_offset; uint64_t prev_offset; /* remove element from user list */ redo_index = list_remove_single(pop, redo, redo_index, &args_remove); /* insert element to user list */ redo_index = list_insert_user(pop, redo, redo_index, &args_insert, &args_common, &next_offset, &prev_offset); /* offsets differ, move is between different list entries - set uuid */ int set_uuid = pe_offset_new != pe_offset_old ? 1 : 0; /* fill next and prev offsets of moving element using redo log */ redo_index = list_fill_entry_redo_log(pop, redo, redo_index, &args_common, next_offset, prev_offset, set_uuid); redo_last: redo_log_set_last(pop->redo, redo, redo_index - 1); redo_log_process(pop->redo, redo, REDO_NUM_ENTRIES); unlock: list_mutexes_unlock(pop, head_new, head_old); err: lane_release(pop); ASSERT(ret == 0 || ret == -1); return ret; } /* * lane_list_recovery -- (internal) recover the list section of the lane */ static int lane_list_recovery(PMEMobjpool *pop, void *data, unsigned length) { LOG(7, "list lane %p", data); struct lane_list_layout *section = data; ASSERT(sizeof(*section) <= length); redo_log_recover(pop->redo, section->redo, REDO_NUM_ENTRIES); if (section->obj_offset) { /* alloc or free recovery */ pfree(pop, §ion->obj_offset); } return 0; } /* * lane_list_check -- (internal) check consistency of lane */ static int lane_list_check(PMEMobjpool *pop, void *data, unsigned length) { LOG(3, "list lane %p", data); struct lane_list_layout *section = data; int ret = 0; if ((ret = redo_log_check(pop->redo, section->redo, REDO_NUM_ENTRIES)) != 0) { ERR("list lane: redo log check failed"); ASSERT(ret == 0 || ret == -1); return ret; } if (section->obj_offset && !OBJ_OFF_FROM_HEAP(pop, section->obj_offset)) { ERR("list lane: invalid offset 0x%" PRIx64, section->obj_offset); return -1; } return 0; } /* * lane_list_construct_rt -- (internal) construct runtime part of list section */ static void * lane_list_construct_rt(PMEMobjpool *pop) { return NULL; } /* * lane_list_destroy_rt -- (internal) destroy runtime part of list section */ static void lane_list_destroy_rt(PMEMobjpool *pop, void *rt) { /* NOP */ } /* * lane_list_boot -- global runtime init routine of list section */ static int lane_list_boot(PMEMobjpool *pop) { /* NOP */ return 0; } /* * lane_list_cleanup -- global runtime cleanup routine of list section */ static int lane_list_cleanup(PMEMobjpool *pop) { /* NOP */ return 0; } static struct section_operations list_ops = { .construct_rt = lane_list_construct_rt, .destroy_rt = lane_list_destroy_rt, .recover = lane_list_recovery, .check = lane_list_check, .boot = lane_list_boot, .cleanup = lane_list_cleanup, }; SECTION_PARM(LANE_SECTION_LIST, &list_ops);