#include <inttypes.h>
#include "list.h"
#include "obj.h"
#include "os_thread.h"
#include "out.h"
#include "sync.h"
#include "valgrind_internal.h"
#define PREV_OFF (offsetof(struct list_entry, pe_prev) + offsetof(PMEMoid, off))
#define NEXT_OFF (offsetof(struct list_entry, pe_next) + offsetof(PMEMoid, off))
struct list_args_common {
ssize_t pe_offset;
uint64_t obj_doffset;
struct list_entry *entry_ptr;
};
struct list_args_insert {
struct list_head *head;
PMEMoid dest;
struct list_entry *dest_entry_ptr;
int before;
};
struct list_args_reinsert {
struct list_head *head;
struct list_entry *entry_ptr;
uint64_t obj_doffset;
};
struct list_args_remove {
ssize_t pe_offset;
uint64_t obj_doffset;
struct list_head *head;
struct list_entry *entry_ptr;
};
static inline int
list_mutexes_lock(PMEMobjpool *pop,
struct list_head *head1, struct list_head *head2)
{
ASSERTne(head1, NULL);
if (!head2 || head1 == head2)
return pmemobj_mutex_lock(pop, &head1->lock);
PMEMmutex *lock1;
PMEMmutex *lock2;
if ((uintptr_t)&head1->lock < (uintptr_t)&head2->lock) {
lock1 = &head1->lock;
lock2 = &head2->lock;
} else {
lock1 = &head2->lock;
lock2 = &head1->lock;
}
int ret;
if ((ret = pmemobj_mutex_lock(pop, lock1)))
goto err;
if ((ret = pmemobj_mutex_lock(pop, lock2)))
goto err_unlock;
return 0;
err_unlock:
pmemobj_mutex_unlock(pop, lock1);
err:
return ret;
}
static inline void
list_mutexes_unlock(PMEMobjpool *pop,
struct list_head *head1, struct list_head *head2)
{
ASSERTne(head1, NULL);
if (!head2 || head1 == head2) {
pmemobj_mutex_unlock_nofail(pop, &head1->lock);
return;
}
pmemobj_mutex_unlock_nofail(pop, &head1->lock);
pmemobj_mutex_unlock_nofail(pop, &head2->lock);
}
static inline PMEMoid
list_get_dest(PMEMobjpool *pop, struct list_head *head, PMEMoid dest,
ssize_t pe_offset, int before)
{
if (dest.off)
return dest;
if (head->pe_first.off == 0 || !!before == POBJ_LIST_DEST_HEAD)
return head->pe_first;
struct list_entry *first_ptr = (struct list_entry *)OBJ_OFF_TO_PTR(pop,
(uintptr_t)((ssize_t)head->pe_first.off + pe_offset));
return first_ptr->pe_prev;
}
static size_t
list_set_oid_redo_log(PMEMobjpool *pop,
struct redo_log *redo, size_t redo_index,
PMEMoid *oidp, uint64_t obj_doffset, int oidp_inited)
{
ASSERT(OBJ_PTR_IS_VALID(pop, oidp));
if (!oidp_inited || oidp->pool_uuid_lo != pop->uuid_lo) {
if (oidp_inited)
ASSERTeq(oidp->pool_uuid_lo, 0);
uint64_t oid_uuid_off = OBJ_PTR_TO_OFF(pop,
&oidp->pool_uuid_lo);
redo_log_store(pop->redo, redo, redo_index, oid_uuid_off,
pop->uuid_lo);
redo_index += 1;
}
uint64_t oid_off_off = OBJ_PTR_TO_OFF(pop, &oidp->off);
redo_log_store(pop->redo, redo, redo_index, oid_off_off,
obj_doffset);
return redo_index + 1;
}
static size_t
list_update_head(PMEMobjpool *pop,
struct redo_log *redo, size_t redo_index,
struct list_head *head, uint64_t first_offset)
{
LOG(15, NULL);
uint64_t pe_first_off_off = OBJ_PTR_TO_OFF(pop, &head->pe_first.off);
redo_log_store(pop->redo, redo, redo_index + 0,
pe_first_off_off, first_offset);
if (head->pe_first.pool_uuid_lo == 0) {
uint64_t pe_first_uuid_off = OBJ_PTR_TO_OFF(pop,
&head->pe_first.pool_uuid_lo);
redo_log_store(pop->redo, redo, redo_index + 1,
pe_first_uuid_off, pop->uuid_lo);
return redo_index + 2;
} else {
return redo_index + 1;
}
}
static void
u64_add_offset(uint64_t *value, ssize_t off)
{
uint64_t prev = *value;
if (off >= 0) {
*value += (size_t)off;
ASSERT(*value >= prev);
} else {
*value -= (size_t)-off;
ASSERT(*value < prev);
}
}
static void
list_fill_entry_persist(PMEMobjpool *pop, struct list_entry *entry_ptr,
uint64_t next_offset, uint64_t prev_offset)
{
LOG(15, NULL);
VALGRIND_ADD_TO_TX(entry_ptr, sizeof(*entry_ptr));
entry_ptr->pe_next.pool_uuid_lo = pop->uuid_lo;
entry_ptr->pe_next.off = next_offset;
entry_ptr->pe_prev.pool_uuid_lo = pop->uuid_lo;
entry_ptr->pe_prev.off = prev_offset;
VALGRIND_REMOVE_FROM_TX(entry_ptr, sizeof(*entry_ptr));
pmemops_persist(&pop->p_ops, entry_ptr, sizeof(*entry_ptr));
}
static size_t
list_fill_entry_redo_log(PMEMobjpool *pop,
struct redo_log *redo, size_t redo_index,
struct list_args_common *args,
uint64_t next_offset, uint64_t prev_offset, int set_uuid)
{
LOG(15, NULL);
struct pmem_ops *ops = &pop->p_ops;
ASSERTne(args->entry_ptr, NULL);
ASSERTne(args->obj_doffset, 0);
if (set_uuid) {
VALGRIND_ADD_TO_TX(&(args->entry_ptr->pe_next.pool_uuid_lo),
sizeof(args->entry_ptr->pe_next.pool_uuid_lo));
VALGRIND_ADD_TO_TX(&(args->entry_ptr->pe_prev.pool_uuid_lo),
sizeof(args->entry_ptr->pe_prev.pool_uuid_lo));
args->entry_ptr->pe_next.pool_uuid_lo = pop->uuid_lo;
args->entry_ptr->pe_prev.pool_uuid_lo = pop->uuid_lo;
VALGRIND_REMOVE_FROM_TX(
&(args->entry_ptr->pe_next.pool_uuid_lo),
sizeof(args->entry_ptr->pe_next.pool_uuid_lo));
VALGRIND_REMOVE_FROM_TX(
&(args->entry_ptr->pe_prev.pool_uuid_lo),
sizeof(args->entry_ptr->pe_prev.pool_uuid_lo));
pmemops_persist(ops, args->entry_ptr, sizeof(*args->entry_ptr));
} else {
ASSERTeq(args->entry_ptr->pe_next.pool_uuid_lo, pop->uuid_lo);
ASSERTeq(args->entry_ptr->pe_prev.pool_uuid_lo, pop->uuid_lo);
}
uint64_t next_off_off = args->obj_doffset + NEXT_OFF;
uint64_t prev_off_off = args->obj_doffset + PREV_OFF;
u64_add_offset(&next_off_off, args->pe_offset);
u64_add_offset(&prev_off_off, args->pe_offset);
redo_log_store(pop->redo, redo, redo_index + 0, next_off_off,
next_offset);
redo_log_store(pop->redo, redo, redo_index + 1, prev_off_off,
prev_offset);
return redo_index + 2;
}
static size_t
list_remove_single(PMEMobjpool *pop,
struct redo_log *redo, size_t redo_index,
struct list_args_remove *args)
{
LOG(15, NULL);
if (args->entry_ptr->pe_next.off == args->obj_doffset) {
ASSERTeq(args->head->pe_first.off, args->obj_doffset);
ASSERTeq(args->entry_ptr->pe_prev.off, args->obj_doffset);
return list_update_head(pop, redo, redo_index, args->head, 0);
} else {
uint64_t next_off = args->entry_ptr->pe_next.off;
uint64_t next_prev_off = next_off + PREV_OFF;
u64_add_offset(&next_prev_off, args->pe_offset);
uint64_t prev_off = args->entry_ptr->pe_prev.off;
uint64_t prev_next_off = prev_off + NEXT_OFF;
u64_add_offset(&prev_next_off, args->pe_offset);
redo_log_store(pop->redo, redo, redo_index + 0,
next_prev_off, prev_off);
redo_log_store(pop->redo, redo, redo_index + 1,
prev_next_off, next_off);
redo_index += 2;
if (args->head->pe_first.off == args->obj_doffset) {
return list_update_head(pop, redo, redo_index,
args->head, next_off);
} else {
return redo_index;
}
}
}
static size_t
list_insert_before(PMEMobjpool *pop,
struct redo_log *redo, size_t redo_index,
struct list_args_insert *args, struct list_args_common *args_common,
uint64_t *next_offset, uint64_t *prev_offset)
{
LOG(15, NULL);
*next_offset = args->dest.off;
*prev_offset = args->dest_entry_ptr->pe_prev.off;
uint64_t dest_prev_off = args->dest.off + PREV_OFF;
u64_add_offset(&dest_prev_off, args_common->pe_offset);
uint64_t dest_prev_next_off = args->dest_entry_ptr->pe_prev.off +
NEXT_OFF;
u64_add_offset(&dest_prev_next_off, args_common->pe_offset);
redo_log_store(pop->redo, redo, redo_index + 0,
dest_prev_off, args_common->obj_doffset);
redo_log_store(pop->redo, redo, redo_index + 1,
dest_prev_next_off, args_common->obj_doffset);
return redo_index + 2;
}
static size_t
list_insert_after(PMEMobjpool *pop,
struct redo_log *redo, size_t redo_index,
struct list_args_insert *args, struct list_args_common *args_common,
uint64_t *next_offset, uint64_t *prev_offset)
{
LOG(15, NULL);
*next_offset = args->dest_entry_ptr->pe_next.off;
*prev_offset = args->dest.off;
uint64_t dest_next_off = args->dest.off + NEXT_OFF;
u64_add_offset(&dest_next_off, args_common->pe_offset);
uint64_t dest_next_prev_off = args->dest_entry_ptr->pe_next.off +
PREV_OFF;
u64_add_offset(&dest_next_prev_off, args_common->pe_offset);
redo_log_store(pop->redo, redo, redo_index + 0,
dest_next_off, args_common->obj_doffset);
redo_log_store(pop->redo, redo, redo_index + 1,
dest_next_prev_off, args_common->obj_doffset);
return redo_index + 2;
}
static size_t
list_insert_user(PMEMobjpool *pop,
struct redo_log *redo, size_t redo_index,
struct list_args_insert *args, struct list_args_common *args_common,
uint64_t *next_offset, uint64_t *prev_offset)
{
LOG(15, NULL);
if (args->dest.off == 0) {
ASSERTeq(args->head->pe_first.off, 0);
*next_offset = args_common->obj_doffset;
*prev_offset = args_common->obj_doffset;
redo_index = list_update_head(pop,
redo, redo_index, args->head,
args_common->obj_doffset);
} else {
if (args->before) {
redo_index = list_insert_before(pop,
redo, redo_index, args, args_common,
next_offset, prev_offset);
if (args->dest.off == args->head->pe_first.off) {
redo_index = list_update_head(pop,
redo, redo_index, args->head,
args_common->obj_doffset);
}
} else {
redo_index = list_insert_after(pop,
redo, redo_index, args, args_common,
next_offset, prev_offset);
}
}
return redo_index;
}
static int
list_insert_new(PMEMobjpool *pop,
size_t pe_offset, struct list_head *user_head, PMEMoid dest, int before,
size_t size, int (*constructor)(void *ctx, void *ptr,
size_t usable_size, void *arg), void *arg, PMEMoid *oidp)
{
LOG(3, NULL);
ASSERT(user_head != NULL);
int ret;
struct lane_section *lane_section;
#ifdef DEBUG
int r = pmemobj_mutex_assert_locked(pop, &user_head->lock);
ASSERTeq(r, 0);
#endif
lane_hold(pop, &lane_section, LANE_SECTION_LIST);
ASSERTne(lane_section, NULL);
ASSERTne(lane_section->layout, NULL);
struct lane_list_layout *section =
(struct lane_list_layout *)lane_section->layout;
struct redo_log *redo = section->redo;
size_t redo_index = 0;
uint64_t sec_off_off = OBJ_PTR_TO_OFF(pop, §ion->obj_offset);
if (constructor) {
if ((ret = pmalloc_construct(pop,
§ion->obj_offset, size,
constructor, arg, 0, 0, 0))) {
ERR("!pmalloc_construct");
goto err_pmalloc;
}
} else {
ret = pmalloc(pop, §ion->obj_offset, size, 0, 0);
if (ret) {
ERR("!pmalloc");
goto err_pmalloc;
}
}
uint64_t obj_doffset = section->obj_offset;
ASSERT((ssize_t)pe_offset >= 0);
dest = list_get_dest(pop, user_head, dest,
(ssize_t)pe_offset, before);
struct list_entry *entry_ptr =
(struct list_entry *)OBJ_OFF_TO_PTR(pop,
obj_doffset + pe_offset);
struct list_entry *dest_entry_ptr =
(struct list_entry *)OBJ_OFF_TO_PTR(pop,
dest.off + pe_offset);
struct list_args_insert args = {
.dest = dest,
.dest_entry_ptr = dest_entry_ptr,
.head = user_head,
.before = before,
};
struct list_args_common args_common = {
.obj_doffset = obj_doffset,
.entry_ptr = entry_ptr,
.pe_offset = (ssize_t)pe_offset,
};
uint64_t next_offset;
uint64_t prev_offset;
redo_index = list_insert_user(pop,
redo, redo_index, &args, &args_common,
&next_offset, &prev_offset);
list_fill_entry_persist(pop, entry_ptr,
next_offset, prev_offset);
if (oidp != NULL) {
if (OBJ_PTR_IS_VALID(pop, oidp)) {
redo_index = list_set_oid_redo_log(pop, redo,
redo_index, oidp, obj_doffset, 0);
} else {
oidp->off = obj_doffset;
oidp->pool_uuid_lo = pop->uuid_lo;
}
}
redo_log_store_last(pop->redo, redo, redo_index, sec_off_off, 0);
redo_log_process(pop->redo, redo, REDO_NUM_ENTRIES);
ret = 0;
err_pmalloc:
lane_release(pop);
ASSERT(ret == 0 || ret == -1);
return ret;
}
int
list_insert_new_user(PMEMobjpool *pop,
size_t pe_offset, struct list_head *user_head, PMEMoid dest, int before,
size_t size, int (*constructor)(void *ctx, void *ptr,
size_t usable_size, void *arg), void *arg, PMEMoid *oidp)
{
int ret;
if ((ret = pmemobj_mutex_lock(pop, &user_head->lock))) {
errno = ret;
LOG(2, "pmemobj_mutex_lock failed");
return -1;
}
ret = list_insert_new(pop, pe_offset, user_head,
dest, before, size, constructor, arg, oidp);
pmemobj_mutex_unlock_nofail(pop, &user_head->lock);
ASSERT(ret == 0 || ret == -1);
return ret;
}
int
list_insert(PMEMobjpool *pop,
ssize_t pe_offset, struct list_head *head,
PMEMoid dest, int before,
PMEMoid oid)
{
LOG(3, NULL);
ASSERTne(head, NULL);
int ret;
struct lane_section *lane_section;
lane_hold(pop, &lane_section, LANE_SECTION_LIST);
if ((ret = pmemobj_mutex_lock(pop, &head->lock))) {
errno = ret;
LOG(2, "pmemobj_mutex_lock failed");
ret = -1;
goto err;
}
ASSERTne(lane_section, NULL);
ASSERTne(lane_section->layout, NULL);
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, dest, pe_offset, before);
struct list_entry *entry_ptr =
(struct list_entry *)OBJ_OFF_TO_PTR(pop,
(uintptr_t)((ssize_t)oid.off + pe_offset));
struct list_entry *dest_entry_ptr =
(struct list_entry *)OBJ_OFF_TO_PTR(pop,
(uintptr_t)((ssize_t)dest.off + pe_offset));
struct list_args_insert args = {
.dest = dest,
.dest_entry_ptr = dest_entry_ptr,
.head = head,
.before = before,
};
struct list_args_common args_common = {
.obj_doffset = oid.off,
.entry_ptr = entry_ptr,
.pe_offset = (ssize_t)pe_offset,
};
uint64_t next_offset;
uint64_t prev_offset;
redo_index = list_insert_user(pop, redo, redo_index,
&args, &args_common, &next_offset, &prev_offset);
redo_index = list_fill_entry_redo_log(pop, redo, redo_index,
&args_common, next_offset, prev_offset, 1);
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;
}
static void
list_remove_free(PMEMobjpool *pop, size_t pe_offset,
struct list_head *user_head, PMEMoid *oidp)
{
LOG(3, NULL);
ASSERT(user_head != NULL);
#ifdef DEBUG
int r = pmemobj_mutex_assert_locked(pop, &user_head->lock);
ASSERTeq(r, 0);
#endif
struct lane_section *lane_section;
lane_hold(pop, &lane_section, LANE_SECTION_LIST);
ASSERTne(lane_section, NULL);
ASSERTne(lane_section->layout, NULL);
struct lane_list_layout *section =
(struct lane_list_layout *)lane_section->layout;
uint64_t sec_off_off = OBJ_PTR_TO_OFF(pop, §ion->obj_offset);
struct redo_log *redo = section->redo;
size_t redo_index = 0;
uint64_t obj_doffset = oidp->off;
ASSERT((ssize_t)pe_offset >= 0);
struct list_entry *entry_ptr =
(struct list_entry *)OBJ_OFF_TO_PTR(pop,
obj_doffset + pe_offset);
struct list_args_remove args = {
.pe_offset = (ssize_t)pe_offset,
.head = user_head,
.entry_ptr = entry_ptr,
.obj_doffset = obj_doffset
};
redo_index = list_remove_single(pop, redo, redo_index, &args);
if (OBJ_PTR_IS_VALID(pop, oidp))
redo_index = list_set_oid_redo_log(pop, redo, redo_index,
oidp, 0, 1);
else
oidp->off = 0;
redo_log_store_last(pop->redo, redo, redo_index, sec_off_off,
obj_doffset);
redo_log_process(pop->redo, redo, REDO_NUM_ENTRIES);
pfree(pop, §ion->obj_offset);
lane_release(pop);
}
int
list_remove_free_user(PMEMobjpool *pop, size_t pe_offset,
struct list_head *user_head, PMEMoid *oidp)
{
LOG(3, NULL);
int ret;
if ((ret = pmemobj_mutex_lock(pop, &user_head->lock))) {
errno = ret;
LOG(2, "pmemobj_mutex_lock failed");
return -1;
}
list_remove_free(pop, pe_offset, user_head, oidp);
pmemobj_mutex_unlock_nofail(pop, &user_head->lock);
return 0;
}
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,
};
redo_index = list_remove_single(pop, redo, redo_index, &args);
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;
}
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);
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) {
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;
redo_index = list_remove_single(pop, redo, redo_index, &args_remove);
redo_index = list_insert_user(pop, redo, redo_index, &args_insert,
&args_common, &next_offset, &prev_offset);
int set_uuid = pe_offset_new != pe_offset_old ? 1 : 0;
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;
}
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) {
pfree(pop, §ion->obj_offset);
}
return 0;
}
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;
}
static void *
lane_list_construct_rt(PMEMobjpool *pop)
{
return NULL;
}
static void
lane_list_destroy_rt(PMEMobjpool *pop, void *rt)
{
}
static int
lane_list_boot(PMEMobjpool *pop)
{
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
};
SECTION_PARM(LANE_SECTION_LIST, &list_ops);