/* * Copyright (c) 2020 AIIT XUOS Lab * XiUOS is licensed under Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * http://license.coscl.org.cn/MulanPSL2 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. */ /** * @file actracer.c * @brief tracer implementation * @version 3.0 * @author AIIT XUOS Lab * @date 2023.08.25 */ /************************************************* File name: actracer.c Description: tracer implementation Others: History: 1. Date: 2023-08-28 Author: AIIT XUOS Lab Modification: 1. first version *************************************************/ #include #include #include "trap_common.h" #include "assert.h" #include "multicores.h" #include "spinlock.h" #include "task.h" #include "actracer.h" #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif struct SysTracer sys_tracer; char* tracer_space[TRACER_MEM_CHUNK_SIZE * NR_TRACER_MEM_CHUNKS]; struct TraceTag* const RequireRootTag() { static struct TraceTag root_trace_tag = { NULL }; return &root_trace_tag; } static inline int namecmp(const char* s, const char* t) { return strncmp(s, t, RESOURCE_NAME_SIZE); } /// @brief alloc a trace meta to trace resource static struct TraceMeta* alloc_trace_meta() { int index = -1; for (uint32_t idx = 0; idx < BITS_TRACEMETA_BITMAP; idx++) { if (sys_tracer.trace_meta_bit_map[idx] == 0xFFFFFFFF) { continue; } uint32_t position = __builtin_ffs(~sys_tracer.trace_meta_bit_map[idx]) - 1; if (position != 31) { // found a free bit sys_tracer.trace_meta_bit_map[idx] |= (1 << (position)); index = idx * 32 + position; break; } } if (index == -1) { panic("Tracer no enough TracerMeta."); } sys_tracer.trace_meta_poll[index].index = index; return &sys_tracer.trace_meta_poll[index]; } static bool dealloc_trace_meta(struct TraceMeta* meta) { int index = meta->index; // clear bitmap uint32_t outer_index = index / 32; uint32_t inner_index = index % 32; sys_tracer.trace_meta_bit_map[outer_index] &= (uint32_t)(~(1 << inner_index)); // clear meta sys_tracer.trace_meta_poll[index].type = TRACER_INVALID; if (index == -1) { panic("Tracer no enough TracerMeta."); } sys_tracer.trace_meta_poll[index].index = index; return &sys_tracer.trace_meta_poll[index]; } static tracer_mem_chunk_idx_t trace_meta_map_mem_chunk(struct TraceMeta* const p_trace_meta, tracer_mem_chunk_idx_t mem_chunk_num) { tracer_mem_chunk_idx_t addr = 0; /* direct mapping */ if (mem_chunk_num < TRACEMETA_NR_DIRECT) { if ((addr = p_trace_meta->addr[mem_chunk_num]) == 0) { p_trace_meta->addr[mem_chunk_num] = addr = tracer_mem_chunk_alloc(); } return addr; } /* indirect mapping */ mem_chunk_num -= TRACEMETA_NR_DIRECT; int indirect_mem_chunk_id = mem_chunk_num / NR_ADDR_PER_MEM_CHUNK; if (indirect_mem_chunk_id < TRACEMETA_NR_INDIRECT) { if ((addr = p_trace_meta->addr[TRACEMETA_NR_DIRECT + indirect_mem_chunk_id]) == 0) { p_trace_meta->addr[TRACEMETA_NR_DIRECT + indirect_mem_chunk_id] = addr = tracer_mem_chunk_alloc(); } mem_chunk_num -= indirect_mem_chunk_id * NR_ADDR_PER_MEM_CHUNK; } else { panic("tracer inode, bmap out of range"); // no return } // index mem_chunk struct tracer_mem_chunk* tracer_mem_chunk = tracer_mem_chunk_read(addr); tracer_mem_chunk_idx_t* indirect_list = (tracer_mem_chunk_idx_t*)tracer_mem_chunk->data; if ((addr = indirect_list[mem_chunk_num]) == 0) { indirect_list[mem_chunk_num] = addr = tracer_mem_chunk_alloc(); tracer_mem_chunk_write(tracer_mem_chunk); } tracer_mem_chunk_release(tracer_mem_chunk); return addr; } /// @brief write trace info by trace meta static int trace_write_info(struct TraceMeta* const p_trace_meta, char* src, uint32_t off, uint32_t n) { if (p_trace_meta->type == TRACER_INVALID) { return -1; } // fast path if (off == 0 && n <= sizeof(uintptr_t)) { p_trace_meta->reserved = *(uintptr_t*)src; return n; } if (UNLIKELY(off > p_trace_meta->size || off + n > VFS_FILE_MAXSIZE * TRACER_MEM_CHUNK_SIZE || off + n < off)) { return -1; } struct tracer_mem_chunk* tracer_mem_chunk; uint32_t m; for (uint32_t tot = 0; tot < n; tot += m, off += m, src += m) { tracer_mem_chunk = tracer_mem_chunk_read(trace_meta_map_mem_chunk(p_trace_meta, off / TRACER_MEM_CHUNK_SIZE)); m = min(n - tot, TRACER_MEM_CHUNK_SIZE - off % TRACER_MEM_CHUNK_SIZE); memmove(tracer_mem_chunk->data + off % TRACER_MEM_CHUNK_SIZE, src, m); tracer_mem_chunk_write(tracer_mem_chunk); tracer_mem_chunk_release(tracer_mem_chunk); } if (n > 0 && off > p_trace_meta->size) { p_trace_meta->size = off; } return n; } /// @brief read trace info by trace meta static int trace_read_info(struct TraceMeta* const p_trace_meta, char* dst, uint32_t off, uint32_t n) { if (p_trace_meta->type == TRACER_INVALID) { return -1; } if (off == 0 && n <= sizeof(uintptr_t)) { *(uintptr_t*)dst = p_trace_meta->reserved; return n; } if (UNLIKELY(off > p_trace_meta->size || off + n < off)) { return -1; } if (UNLIKELY(off + n > p_trace_meta->size)) { n = p_trace_meta->size - off; } static struct tracer_mem_chunk* tracer_mem_chunk; uint32_t m; for (uint32_t tot = 0; tot < n; tot += m, off += m, dst += m) { tracer_mem_chunk = tracer_mem_chunk_read(trace_meta_map_mem_chunk(p_trace_meta, off / TRACER_MEM_CHUNK_SIZE)); m = min(n - tot, TRACER_MEM_CHUNK_SIZE - off % TRACER_MEM_CHUNK_SIZE); memmove(dst, tracer_mem_chunk->data + off % TRACER_MEM_CHUNK_SIZE, m); tracer_mem_chunk_release(tracer_mem_chunk); } return n; } static struct TraceMeta* tracer_find_meta_onestep(struct TraceMeta* const p_owner, char* name, uint32_t* poff) { struct TraceResourceEntry resource_entry; if (p_owner->type != TRACER_OWNER) { ERROR("tracer_find_meta_onestep, not a dir, index: %d\n", p_owner->index); return NULL; } for (uint32_t off = 0; off < p_owner->size; off += sizeof(resource_entry)) { if (trace_read_info(p_owner, (char*)&resource_entry, off, sizeof(resource_entry)) != sizeof(resource_entry)) { panic("tracer_find_meta_onestep: read trace owner's resources failed\n"); } if (resource_entry.index == 0) { continue; } if (namecmp(name, resource_entry.name) == 0) { if (poff) { *poff = off; } uint32_t vindex = resource_entry.index; assert(vindex >= 0 && vindex < NR_MAX_TRACEMETA); return &sys_tracer.trace_meta_poll[vindex]; } } return NULL; } // Write a new vdirectory entry (name, index) into the vdirectory dp. static int tracer_append_meta(struct TraceMeta* p_owner, char* name, uint32_t index) { struct TraceResourceEntry resource_entry; int offset = 0; for (offset = 0; offset < p_owner->size; offset += sizeof(resource_entry)) { if (trace_read_info(p_owner, (char*)&resource_entry, offset, sizeof(resource_entry)) != sizeof(resource_entry)) { ERROR("tracer_append_meta failed, read owner's resources failed.\n"); return -1; } if (resource_entry.index == 0) { break; } } strncpy(resource_entry.name, name, RESOURCE_NAME_SIZE); resource_entry.index = index; if (trace_write_info(p_owner, (char*)&resource_entry, offset, sizeof(resource_entry)) != sizeof(resource_entry)) { ERROR("tracer_append_meta failed, append resource to owner failed.\n"); return -1; } return 0; } static struct TraceMeta* tracer_new_meta(struct TraceMeta* p_owner, char* name, short type) { struct TraceMeta* p_trace_meta; // check if owner entry exists uint32_t offset; if ((p_trace_meta = tracer_find_meta_onestep(p_owner, name, &offset)) != 0) { LOG("create resource(trace meta) failed, %s is existed\n", name); return NULL; } if ((p_trace_meta = alloc_trace_meta()) == 0) { ERROR("create resource(trace meta) failed, cache is no free\n"); return NULL; } p_trace_meta->type = type; p_trace_meta->size = 0; // update parent directory tracer_append_meta(p_owner, name, p_trace_meta->index); // update "." and ".." for vfs inode if (p_trace_meta->type == TRACER_OWNER) { tracer_append_meta(p_trace_meta, ".", p_trace_meta->index); tracer_append_meta(p_trace_meta, "..", p_owner->index); } return p_trace_meta; } static char* parse_path(char* path, char* name) { // skip extra '/' while (*path == '/') { path++; } if (*path == '\0') { return NULL; } // start of current name char* cur_start = path; while (*path != '/' && *path != '\0') { path++; } // handle current name int len = path - cur_start; if (len >= RESOURCE_NAME_SIZE) { strncpy(name, cur_start, RESOURCE_NAME_SIZE); } else { strncpy(name, cur_start, len); name[len] = '\0'; } return path; } static struct TraceMeta* tracer_find_meta(struct TraceMeta* const p_owner, char* path, int nameiparent, char* name) { struct TraceMeta* p_owner_inside = p_owner; struct TraceMeta* vnp; /* traverse TRACER_OWNER */ while ((path = parse_path(path, name)) != 0) { if (p_owner_inside->type != TRACER_OWNER) { return NULL; } if (nameiparent && *path == '\0') { return p_owner_inside; } if ((vnp = tracer_find_meta_onestep(p_owner_inside, name, NULL)) == 0) { DEBUG("Not such object: %s\n", path); return NULL; } p_owner_inside = vnp; } if (nameiparent) { return NULL; } return p_owner_inside; } int tracer_write_trace(struct TraceTag* const p_trace_tag, char* src, uint32_t off, uint32_t n) { if (src == NULL || p_trace_tag == NULL || p_trace_tag->meta == NULL) { return -1; } return trace_write_info(p_trace_tag->meta, src, off, n); } int tracer_read_trace(struct TraceTag* const p_trace_tag, char* dst, uint32_t off, uint32_t n) { if (dst == NULL || p_trace_tag == NULL || p_trace_tag->meta == NULL) { return -1; } return trace_read_info(p_trace_tag->meta, dst, off, n); } /// @brief static void trace_locate_inner(struct TraceTag* target, struct TraceTag* const p_trace_tag, char* path, bool parent) { char name[RESOURCE_NAME_SIZE]; struct TraceMeta* p_trace_meta = tracer_find_meta(p_trace_tag->meta, path, parent, name); // p_trace_meta: TRACER_OWNER, VT_FS or other. // TRACER_OWNER: path: "", name: "dir name" // other: path: "", name: "file name" if (p_trace_meta == NULL) { DEBUG("trace_locate, not found\n"); return; } target->type = p_trace_meta->type; target->meta = p_trace_meta; } static inline void trace_locate(struct TraceTag* target, struct TraceTag* const p_trace_tag, char* path) { trace_locate_inner(target, p_trace_tag, path, 0); } static inline void trace_locate_parent(struct TraceTag* target, struct TraceTag* const p_trace_tag, char* path) { trace_locate_inner(target, p_trace_tag, path, 1); } bool tracer_create_trace(struct TraceTag* target, struct TraceTag* p_trace_tag, char* path, short type) { struct TraceMeta *p_trace_meta, *p_owner; // find parent vfs inode if ((p_owner = p_trace_tag->meta) == 0) { LOG("create tracemeta failed, parent is null\n"); target->meta = NULL; return false; } p_trace_meta = tracer_new_meta(p_owner, path, type); target->meta = p_trace_meta; return true; } bool tracer_delete_trace(struct TraceTag* target, struct TraceTag* owner) { if (target->meta == NULL || owner->type != TRACER_OWNER) { return false; } struct TraceMeta* p_trace_meta = target->meta; struct TraceMeta* p_owner_meta = owner->meta; assert(p_trace_meta->type != TRACER_INVALID); if (p_trace_meta->type == TRACER_OWNER) { /// @todo support recursive delete } struct TraceResourceEntry resource_entry; bool is_owned = false; for (uint32_t off = 0; off < p_owner_meta->size; off += sizeof(resource_entry)) { if (trace_read_info(p_owner_meta, (char*)&resource_entry, off, sizeof(resource_entry)) != sizeof(resource_entry)) { panic("tracer_find_meta_onestep: read trace owner's resources failed\n"); } if (resource_entry.index == 0) { continue; } if (resource_entry.index == p_trace_meta->index) { resource_entry.index = 0; trace_write_info(owner->meta, (char*)&resource_entry, off, sizeof(resource_entry)); is_owned = true; break; } } if (!is_owned) { ERROR("delete trace(%d) not owned by given owner(%d).\n", target->meta->index, owner->meta->index); return false; } dealloc_trace_meta(p_trace_meta); return true; } void tracer_init(void) { /* init sys_tracer, the manager */ spinlock_init(&sys_tracer.mem_chunk_bitmap_lock, "tracer_mem_chunk_bitmap"); spinlock_init(&sys_tracer.trace_meta_bitmap_lock, "tracer_meta_bitmap"); memset(sys_tracer.mem_chunks_bit_map, 0, sizeof(sys_tracer.mem_chunk_bitmap_lock)); memset(sys_tracer.trace_meta_bit_map, 0, sizeof(sys_tracer.trace_meta_bit_map)); assert((TRACER_MEM_CHUNK_SIZE % sizeof(struct TraceMeta)) == 0); assert((TRACER_MEM_CHUNK_SIZE % sizeof(struct TraceResourceEntry)) == 0); // mem_chunk space, fit with mem_chunk_bit_map mem_chunk_synchronizer_init((uintptr_t)tracer_space, TRACER_MEM_CHUNK_SIZE, NR_TRACER_MEM_CHUNKS); /* build root inode */ alloc_trace_meta(); // inode as guard. /* build root trace_meta */ struct TraceMeta* root_tracemeta = alloc_trace_meta(); assert(root_tracemeta->index == 1); root_tracemeta->type = TRACER_OWNER; root_tracemeta->size = 0; tracer_append_meta(root_tracemeta, ".", root_tracemeta->index); tracer_append_meta(root_tracemeta, "..", root_tracemeta->index); RequireRootTag()->meta = root_tracemeta; } /// @brief find resource tag void tracer_find_tag(struct TraceTag* target, struct TraceTag* const source, char* path) { target->meta = NULL; struct TraceTag* p_trace_tag; if (*path == '/' || source == NULL) { p_trace_tag = RequireRootTag(); } else { p_trace_tag = source; } if (p_trace_tag == NULL || p_trace_tag->meta == NULL) { return; } trace_locate(target, p_trace_tag, path); } bool AchieveResourceTag(struct TraceTag* target, struct TraceTag* owner, char* name) { tracer_find_tag(target, owner, name); if (target->meta == NULL) { return false; } return true; } void* AchieveResource(struct TraceTag* target) { if (target->type == TRACER_OWNER) { return NULL; } void* p_resource = NULL; tracer_read_trace(target, (char*)&p_resource, 0, sizeof(void*)); assert(p_resource != NULL); return p_resource; } bool CreateResourceTag(struct TraceTag* new_tag, struct TraceTag* owner, char* name, tracemeta_ac_type type, void* p_resource) { new_tag->type = type; if (type == TRACER_OWNER) { return tracer_create_trace(new_tag, owner, name, type); } // handle ac resource types if (p_resource == NULL) { return false; } if (!tracer_create_trace(new_tag, owner, name, type)) { return false; } bool ret = tracer_write_trace(new_tag, (char*)&p_resource, 0, sizeof(void*)) == sizeof(void*); return ret; } bool DeleteResource(struct TraceTag* target, struct TraceTag* owner) { return tracer_delete_trace(target, owner); }