From bf17dfb2b372f630f6f8ca1cbf6e6472c30bba46 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Tue, 6 Apr 2021 21:46:25 +0800 Subject: [PATCH 04/50] add cslide for etmem Signed-off-by: Kemeng Shi --- CMakeLists.txt | 12 +- inc/etmem_inc/etmem.h | 3 +- inc/etmem_inc/etmem_common.h | 5 + inc/etmem_inc/etmem_engine.h | 20 + inc/etmem_inc/{etmem_task.h => etmem_obj.h} | 8 +- inc/etmemd_inc/etmemd.h | 4 + inc/etmemd_inc/etmemd_common.h | 6 +- inc/etmemd_inc/etmemd_cslide.h | 23 + inc/etmemd_inc/etmemd_engine.h | 51 +- inc/etmemd_inc/etmemd_file.h | 30 +- inc/etmemd_inc/etmemd_pool_adapter.h | 7 +- inc/etmemd_inc/etmemd_project.h | 40 +- inc/etmemd_inc/etmemd_rpc.h | 9 +- inc/etmemd_inc/etmemd_scan.h | 10 + inc/etmemd_inc/etmemd_slide.h | 3 +- inc/etmemd_inc/etmemd_task.h | 17 +- src/etmem_src/etmem.c | 64 +- src/etmem_src/etmem_common.c | 18 +- src/etmem_src/etmem_engine.c | 156 ++ src/etmem_src/etmem_obj.c | 197 ++ src/etmem_src/etmem_project.c | 90 +- src/etmem_src/etmem_rpc.c | 64 +- src/etmem_src/etmem_task.c | 161 -- src/etmemd_src/etmemd_common.c | 53 +- src/etmemd_src/etmemd_cslide.c | 2272 +++++++++++++++++++ src/etmemd_src/etmemd_engine.c | 86 +- src/etmemd_src/etmemd_file.c | 438 +--- src/etmemd_src/etmemd_migrate.c | 2 +- src/etmemd_src/etmemd_pool_adapter.c | 34 +- src/etmemd_src/etmemd_project.c | 707 ++++-- src/etmemd_src/etmemd_rpc.c | 143 +- src/etmemd_src/etmemd_scan.c | 150 +- src/etmemd_src/etmemd_slide.c | 186 +- src/etmemd_src/etmemd_task.c | 146 +- 34 files changed, 4047 insertions(+), 1168 deletions(-) create mode 100644 inc/etmem_inc/etmem_engine.h rename inc/etmem_inc/{etmem_task.h => etmem_obj.h} (89%) create mode 100644 inc/etmemd_inc/etmemd_cslide.h create mode 100644 src/etmem_src/etmem_engine.c create mode 100644 src/etmem_src/etmem_obj.c delete mode 100644 src/etmem_src/etmem_task.c create mode 100644 src/etmemd_src/etmemd_cslide.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a4a83ca..fa64b89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(ETMEMD_SRC ${ETMEMD_SRC_DIR}/etmemd_project.c ${ETMEMD_SRC_DIR}/etmemd_engine.c ${ETMEMD_SRC_DIR}/etmemd_slide.c + ${ETMEMD_SRC_DIR}/etmemd_cslide.c ${ETMEMD_SRC_DIR}/etmemd_task.c ${ETMEMD_SRC_DIR}/etmemd_scan.c ${ETMEMD_SRC_DIR}/etmemd_threadpool.c @@ -42,7 +43,8 @@ set(ETMEMD_SRC set(ETMEM_SRC ${ETMEM_SRC_DIR}/etmem.c ${ETMEM_SRC_DIR}/etmem_project.c - ${ETMEM_SRC_DIR}/etmem_task.c + ${ETMEM_SRC_DIR}/etmem_obj.c + ${ETMEM_SRC_DIR}/etmem_engine.c ${ETMEM_SRC_DIR}/etmem_rpc.c ${ETMEM_SRC_DIR}/etmem_common.c) @@ -54,8 +56,12 @@ add_executable(etmem set(EXECUTABLE_OUTPUT_PATH ${BUILD_DIR}/bin) +include(FindPkgConfig) +pkg_search_module(GLIB2 REQUIRED glib-2.0) + target_include_directories(etmemd PRIVATE - ${PROJECT_SOURCE_DIR}/inc/etmemd_inc) + ${PROJECT_SOURCE_DIR}/inc/etmemd_inc + ${GLIB2_INCLUDE_DIRS}) target_include_directories(etmem PRIVATE ${PROJECT_SOURCE_DIR}/inc/etmem_inc) @@ -68,7 +74,7 @@ if(CONFIG_DEBUG STREQUAL "y") endif() set_target_properties(etmemd PROPERTIES LINK_FLAGS "-s -fPIE -pie -fPIC -Wl,-z,relro,-z,now,-z,noexecstack -Wtrampolines") -target_link_libraries(etmemd PRIVATE pthread dl rt boundscheck) +target_link_libraries(etmemd PRIVATE pthread dl rt boundscheck numa ${GLIB2_LIBRARIES}) if( ${ARCHITECTURE} STREQUAL "aarch64" ) target_compile_options(etmemd PRIVATE -march=armv8-a) diff --git a/inc/etmem_inc/etmem.h b/inc/etmem_inc/etmem.h index 8d7b615..47979a2 100644 --- a/inc/etmem_inc/etmem.h +++ b/inc/etmem_inc/etmem.h @@ -24,6 +24,7 @@ typedef enum etmem_cmd_e { ETMEM_CMD_START, ETMEM_CMD_STOP, ETMEM_CMD_SHOW, + ETMEM_CMD_ENGINE, ETMEM_CMD_HELP, } etmem_cmd; @@ -37,7 +38,7 @@ struct etmem_conf { struct etmem_obj { char *name; void (*help)(void); - int (*do_cmd)(const struct etmem_conf *conf); + int (*do_cmd)(struct etmem_conf *conf); SLIST_ENTRY(etmem_obj) entry; }; diff --git a/inc/etmem_inc/etmem_common.h b/inc/etmem_inc/etmem_common.h index 9725c87..294d216 100644 --- a/inc/etmem_inc/etmem_common.h +++ b/inc/etmem_inc/etmem_common.h @@ -24,11 +24,16 @@ #define FILE_NAME_MAX_LEN 256 #define SOCKET_NAME_MAX_LEN 107 +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + struct mem_proj { etmem_cmd cmd; char *proj_name; + char *eng_name; + char *task_name; char *file_name; char *sock_name; + char *eng_cmd; }; int parse_name_string(const char *val, char **name_str, size_t max_len); diff --git a/inc/etmem_inc/etmem_engine.h b/inc/etmem_inc/etmem_engine.h new file mode 100644 index 0000000..6d1ca14 --- /dev/null +++ b/inc/etmem_inc/etmem_engine.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * etmem is licensed under the 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. + * Author: louhongxiang + * Create: 2019-12-10 + * Description: This is a header file of the data structure definition for etmem engine function. + ******************************************************************************/ + +#ifndef __ETMEM_ENGINE_H__ +#define __ETMEM_ENGINE_H__ +void engine_init(void); +void engine_exit(void); +#endif diff --git a/inc/etmem_inc/etmem_task.h b/inc/etmem_inc/etmem_obj.h similarity index 89% rename from inc/etmem_inc/etmem_task.h rename to inc/etmem_inc/etmem_obj.h index aa8f218..635b6b3 100644 --- a/inc/etmem_inc/etmem_task.h +++ b/inc/etmem_inc/etmem_obj.h @@ -13,10 +13,10 @@ * Description: This is a header file of the function declaration for migration functions. ******************************************************************************/ -#ifndef __ETMEM_TASK_H__ -#define __ETMEM_TASK_H__ +#ifndef __ETMEM_OBJ_H__ +#define __ETMEM_OBJ_H__ -void migrate_init(void); -void migrate_exit(void); +void obj_init(void); +void obj_exit(void); #endif diff --git a/inc/etmemd_inc/etmemd.h b/inc/etmemd_inc/etmemd.h index ed29d44..797049e 100644 --- a/inc/etmemd_inc/etmemd.h +++ b/inc/etmemd_inc/etmemd.h @@ -19,6 +19,10 @@ #include #include +#define PTE_SIZE_SHIFT 12 +#define PMD_SIZE_SHIFT 21 +#define PUD_SIZE_SHIFT 30 + /* * page type specified by size * */ diff --git a/inc/etmemd_inc/etmemd_common.h b/inc/etmemd_inc/etmemd_common.h index 2fe8a73..1b62bbd 100644 --- a/inc/etmemd_inc/etmemd_common.h +++ b/inc/etmemd_inc/etmemd_common.h @@ -25,6 +25,8 @@ #define DECIMAL_RADIX 10 #define ETMEMD_MAX_PARAMETER_NUM 5 +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + /* * function: parse cmdline passed to etmemd server. * @@ -43,7 +45,7 @@ int get_int_value(const char *val, int *value); int get_unsigned_int_value(const char *val, unsigned int *value); void etmemd_safe_free(void **ptr); -FILE *etmemd_get_proc_file(const char *pid, const char *file, const char *mode); +FILE *etmemd_get_proc_file(const char *pid, const char *file, int flags, const char *mode); int get_keyword_and_value(const char *str, char *key, char *val); @@ -56,4 +58,6 @@ int get_keyword_and_value(const char *str, char *key, char *val); * */ char *skip_blank_line(FILE *file); +int dprintf_all(int fd, const char *format, ...); + #endif diff --git a/inc/etmemd_inc/etmemd_cslide.h b/inc/etmemd_inc/etmemd_cslide.h new file mode 100644 index 0000000..2405f2d --- /dev/null +++ b/inc/etmemd_inc/etmemd_cslide.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * etmem is licensed under the 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. + * Author: louhongxiang + * Create: 2019-12-10 + * Description: This is a header file of the function declaration for cslide function. + ******************************************************************************/ + +#ifndef ETMEMD_CSLIDE_H +#define ETMEMD_CSLIDE_H + +#include "etmemd_engine.h" + +int fill_engine_type_cslide(struct engine *eng); + +#endif diff --git a/inc/etmemd_inc/etmemd_engine.h b/inc/etmemd_inc/etmemd_engine.h index 13d676c..77916a5 100644 --- a/inc/etmemd_inc/etmemd_engine.h +++ b/inc/etmemd_inc/etmemd_engine.h @@ -16,11 +16,13 @@ #ifndef ETMEMD_ENGINE_H #define ETMEMD_ENGINE_H +#include #include "etmemd.h" #include "etmemd_task.h" enum eng_type { SLIDE_ENGINE = 0, + CSLIDE_ENGINE, DYNAMIC_FB_ENGINE, HISTORICAL_FB_ENGINE, THIRDPARTY_ENGINE, @@ -32,44 +34,29 @@ enum eng_type { * */ struct engine { int engine_type; /* engine type used for elimination strategy */ + char *name; void *params; /* point to engine parameter struct */ - void *task; /* point to task this engine belongs to */ + struct project *proj; struct page_refs *page_ref; /* scan result */ + struct engine_ops *ops; + struct task *tasks; uint64_t page_cnt; /* number of pages */ - struct adapter *adp; /* configured migration strategy */ - - /* parse parameter configuration */ - int (*parse_param_conf)(struct engine *eng, FILE *file); - - /* migrate policy function */ - struct memory_grade *(*mig_policy_func)(struct page_refs **page_refs, void *params); - - /* alloc tkpid params space based on different policies. */ - int (*alloc_params)(struct task_pid **tk_pid); -}; - -/* - * adapter struct - * */ -struct adapter { - /* scan function */ - struct page_refs *(*do_scan)(const struct task_pid *tpid, const struct task *tk); - - /* migrate function */ - int (*do_migrate)(unsigned int pid, const struct memory_grade *memory_grade); -}; - -struct engine_item { - enum eng_type eng_type; - int (*fill_eng_func)(struct engine *eng); + struct engine *next; }; -struct engine_private_item { - char *priv_sec_name; - int (*fill_eng_private_func)(const struct engine *eng, const char *val); +struct engine_ops { + int (*fill_eng_params)(GKeyFile *config, struct engine *eng); + void (*clear_eng_params)(struct engine *eng); + int (*fill_task_params)(GKeyFile *config, struct task *task); + void (*clear_task_params)(struct task *tk); + int (*start_task)(struct engine *eng, struct task *tk); + void (*stop_task)(struct engine *eng, struct task *tk); + int (*alloc_pid_params)(struct engine *eng, struct task_pid **tk_pid); + void (*free_pid_params)(struct engine *eng, struct task_pid **tk_pid); + int (*eng_mgt_func)(struct engine *eng, struct task *tk, char *cmd, int fd); }; -const char *etmemd_get_eng_name(enum eng_type type); +struct engine *etmemd_engine_add(GKeyFile *config); +void etmemd_engine_remove(struct engine *eng); -int fill_engine_type(struct engine *eng, const char *val); #endif diff --git a/inc/etmemd_inc/etmemd_file.h b/inc/etmemd_inc/etmemd_file.h index a251d1b..34ccaf9 100644 --- a/inc/etmemd_inc/etmemd_file.h +++ b/inc/etmemd_inc/etmemd_file.h @@ -17,23 +17,31 @@ #define ETMEMD_FILE_H #include +#include #include "etmemd_project.h" #include "etmemd_task.h" -struct project_item { - char *proj_sec_name; - int (*fill_proj_func)(struct project *proj, const char *val); - bool optional; - bool set; +#define PROJ_GROUP "project" +#define ENG_GROUP "engine" +#define TASK_GROUP "task" + +enum val_type { + INT_VAL, + STR_VAL, }; -struct task_item { - char *task_sec_name; - int (*fill_task_func)(struct task *tk, const char *val); - bool optional; - bool set; +struct config_item { + char *key; + enum val_type type; + int (*fill)(void *obj, void *val); + bool option; }; -int etmemd_fill_proj_by_conf(struct project *proj, FILE *conf_file); +int parse_file_config(GKeyFile *config, char *group_name, struct config_item *items, unsigned n, void *obj); + +static inline int parse_to_int(void *val) +{ + return (int)(long long)val; +} #endif diff --git a/inc/etmemd_inc/etmemd_pool_adapter.h b/inc/etmemd_inc/etmemd_pool_adapter.h index 7448b50..3cb7b2a 100644 --- a/inc/etmemd_inc/etmemd_pool_adapter.h +++ b/inc/etmemd_inc/etmemd_pool_adapter.h @@ -21,10 +21,15 @@ #include "etmemd_project.h" #include "etmemd_log.h" +struct task_executor { + struct task *tk; + void *(*func)(void *arg); +}; + /* * Start process scanning and memory migration * */ -int start_threadpool_work(struct task *tk); +int start_threadpool_work(struct task_executor *executor); /* * Stop process and cleanup resource of scanning and memory migration diff --git a/inc/etmemd_inc/etmemd_project.h b/inc/etmemd_inc/etmemd_project.h index da929b4..d15c7fd 100644 --- a/inc/etmemd_inc/etmemd_project.h +++ b/inc/etmemd_inc/etmemd_project.h @@ -18,6 +18,7 @@ #include #include "etmemd_task.h" +#include "etmemd_engine.h" /* set the length of project name to 32 */ #define PROJECT_NAME_MAX_LEN 32 @@ -25,12 +26,12 @@ #define PROJECT_SHOW_COLM_MAX 128 struct project { - bool start; + char *name; int interval; int loop; int sleep; - char *name; - struct task *tasks; + bool start; + struct engine *engs; SLIST_ENTRY(project) entry; }; @@ -42,6 +43,10 @@ enum opt_result { OPT_PRO_STARTED, OPT_PRO_STOPPED, OPT_PRO_NOEXIST, + OPT_ENG_EXISTED, + OPT_ENG_NOEXIST, + OPT_TASK_EXISTED, + OPT_TASK_NOEXIST, OPT_INTER_ERR, OPT_RET_END, }; @@ -49,32 +54,33 @@ enum opt_result { /* * function: Add project to etmem server. * - * in: const char *project_name - name of the project to be added - * const char *file_name - name of the project configuration file + * in: GKeyFile *config - loaded config file * - * out: OPT_SUCCESS(0) - successed to add project - * other value - failed to add project + * out: OPT_SUCCESS(0) - successed to add project + * other value - failed to add project * */ -enum opt_result etmemd_project_add(const char *project_name, const char *file_name); +enum opt_result etmemd_project_add(GKeyFile *config); /* - * function: Delete given project. + * function: Remove given project. * - * in: const char *project_name - name of the project to be deleted + * in: GKeyFile *config - loaded config file * - * out: OPT_SUCCESS(0) - successed to delete project - * other value - failed to delete project + * out: OPT_SUCCESS(0) - successed to delete project + * other value - failed to delete project * */ -enum opt_result etmemd_project_delete(const char *project_name); +enum opt_result etmemd_project_remove(GKeyFile *config); /* * function: Show all the projects. * + * in: const char *project_name - name of the project to start migrate + * int fd - client socket to show projects info * * out: OPT_SUCCESS(0) - successed to show all the projects * other value - failed to show any project * */ -enum opt_result etmemd_project_show(void); +enum opt_result etmemd_project_show(const char *project_name, int fd); /* * function: Start migrate in given project. @@ -96,6 +102,12 @@ enum opt_result etmemd_migrate_start(const char *project_name); * */ enum opt_result etmemd_migrate_stop(const char *project_name); +enum opt_result etmemd_project_mgt_engine(const char *project_name, const char *eng_name, char *cmd, char *task_name, int sock_fd); +enum opt_result etmemd_project_add_engine(GKeyFile *config); +enum opt_result etmemd_project_remove_engine(GKeyFile *config); +enum opt_result etmemd_project_add_task(GKeyFile *config); +enum opt_result etmemd_project_remove_task(GKeyFile *config); + void etmemd_stop_all_projects(void); #endif diff --git a/inc/etmemd_inc/etmemd_rpc.h b/inc/etmemd_inc/etmemd_rpc.h index ff27494..146cec3 100644 --- a/inc/etmemd_inc/etmemd_rpc.h +++ b/inc/etmemd_inc/etmemd_rpc.h @@ -19,11 +19,12 @@ #include enum cmd_type { - PROJ_ADD = 0, - PROJ_DEL, + OBJ_ADD = 0, + OBJ_DEL, MIG_START, MIG_STOP, PROJ_SHOW, + ENG_CMD, }; enum rpc_decode_type { @@ -36,6 +37,10 @@ struct server_rpc_params { char *proj_name; char *file_name; int cmd; + int sock_fd; + char *eng_name; + char *eng_cmd; + char *task_name; }; struct server_rpc_parser { diff --git a/inc/etmemd_inc/etmemd_scan.h b/inc/etmemd_inc/etmemd_scan.h index 9ae387e..ed72e1a 100644 --- a/inc/etmemd_inc/etmemd_scan.h +++ b/inc/etmemd_inc/etmemd_scan.h @@ -16,6 +16,7 @@ #ifndef ETMEMD_SCAN_H #define ETMEMD_SCAN_H +#include #include "etmemd.h" #include "etmemd_task.h" @@ -31,6 +32,11 @@ #define MAPS_FILE "/maps" #define IDLE_SCAN_FILE "/idle_pages" +#define SMAPS_FILE "/smaps" +#define VMFLAG_HEAD "VmFlags" + +#define SCAN_AS_HUGE O_LARGEFILE + enum { VMA_STAT_READ = 0, VMA_STAT_WRITE, @@ -100,8 +106,11 @@ struct page_refs *etmemd_do_scan(const struct task_pid *tpid, const struct task /* free vma list struct */ void free_vmas(struct vmas *vmas); +struct page_refs **walk_vmas(int fd, struct walk_address *walk_address, struct page_refs **pf, unsigned long *use_rss); int get_page_refs(const struct vmas *vmas, const char *pid, struct page_refs **page_refs, unsigned long *use_rss); +int split_vmflags(char ***vmflags_array, char *vmflags); +struct vmas *get_vmas_with_flags(const char *pid, char **vmflags_array, int vmflags_num, bool is_anon_only); struct vmas *get_vmas(const char *pid); void clean_page_refs_unexpected(void *arg); @@ -110,4 +119,5 @@ void clean_memory_grade_unexpected(void *arg); struct page_refs *add_page_refs_into_memory_grade(struct page_refs *page_refs, struct page_refs **list); int init_g_page_size(void); +int page_type_to_size(enum page_type type); #endif diff --git a/inc/etmemd_inc/etmemd_slide.h b/inc/etmemd_inc/etmemd_slide.h index e95438d..e76e97a 100644 --- a/inc/etmemd_inc/etmemd_slide.h +++ b/inc/etmemd_inc/etmemd_slide.h @@ -16,10 +16,11 @@ #ifndef ETMEMD_SLIDE_H #define ETMEMD_SLIDE_H - +#include "etmemd_pool_adapter.h" #include "etmemd_engine.h" struct slide_params { + struct task_executor *executor; int t; /* watermark */ }; diff --git a/inc/etmemd_inc/etmemd_task.h b/inc/etmemd_inc/etmemd_task.h index b7fba2b..d6d89ea 100644 --- a/inc/etmemd_inc/etmemd_task.h +++ b/inc/etmemd_inc/etmemd_task.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "etmemd_threadpool.h" #include "etmemd_threadtimer.h" @@ -37,20 +38,16 @@ struct task_pid { struct task { char *type; char *value; + char *name; uint64_t max_threads; - struct project *proj; - struct engine *eng; struct task_pid *pids; + struct engine *eng; + void *params; pthread_t task_pt; timer_thread *timer_inst; thread_pool *threadpool_inst; - int (*start_etmem)(struct task *tk); - void (*stop_etmem)(struct task *tk); - void (*delete_etmem)(struct task *tk); - void *(*workflow_engine)(void *); - struct task *next; }; @@ -64,5 +61,9 @@ void etmemd_free_task_struct(struct task **tk); void free_task_pid_mem(struct task_pid **tk_pid); -void etmemd_print_tasks(const struct task *tk); +void etmemd_print_tasks(int fd, const struct task *tk, char *engine_name, bool started); + +struct task *etmemd_add_task(GKeyFile *config); +void etmemd_remove_task(struct task *tk); + #endif diff --git a/src/etmem_src/etmem.c b/src/etmem_src/etmem.c index 8bd171c..863f91b 100644 --- a/src/etmem_src/etmem.c +++ b/src/etmem_src/etmem.c @@ -19,8 +19,9 @@ #include #include "securec.h" #include "etmem.h" -#include "etmem_task.h" +#include "etmem_obj.h" #include "etmem_project.h" +#include "etmem_engine.h" #define CMD_POSITION 1 @@ -65,53 +66,10 @@ static int check_param(int argc, char *argv[], struct etmem_conf *conf, return -1; } - return 0; -} - -static void fill_cmd_type(struct etmem_conf *conf, const int val) -{ - conf->cmd = val; -} - -struct cmd_item { - char *cmd_name; - enum etmem_cmd_e cmd; - void (*fill_cmd_func)(struct etmem_conf *conf, const int val); -}; - -struct cmd_item g_cmd_items[] = { - {"add", ETMEM_CMD_ADD, fill_cmd_type}, - {"del", ETMEM_CMD_DEL, fill_cmd_type}, - {"start", ETMEM_CMD_START, fill_cmd_type}, - {"stop", ETMEM_CMD_STOP, fill_cmd_type}, - {"show", ETMEM_CMD_SHOW, fill_cmd_type}, - {"help", ETMEM_CMD_HELP, fill_cmd_type}, - {NULL, 0, NULL}, -}; - -static int parse_command(int argc, char *argv[], struct etmem_conf *conf, - const struct etmem_obj *obj) -{ - int ret = -1; - int i = 0; - - while (g_cmd_items[i].cmd_name != NULL) { - if (strcmp(argv[CMD_POSITION], g_cmd_items[i].cmd_name) == 0) { - g_cmd_items[i].fill_cmd_func(conf, (int)g_cmd_items[i].cmd); - ret = 0; - break; - } - i++; - } - - if (ret != 0) { - printf("invalid command %s\n", argv[CMD_POSITION]); - obj->help(); - return -1; - } - - conf->argc = argc - CMD_POSITION; - conf->argv = argv + CMD_POSITION; + argc--; + argv++; + conf->argc = argc; + conf->argv = argv; return 0; } @@ -139,7 +97,7 @@ static int parse_args(int argc, char *argv[], struct etmem_conf *conf, return -EINVAL; } - return parse_command(argc, argv, conf, *obj); + return 0; } void etmem_register_obj(struct etmem_obj *obj) @@ -162,7 +120,8 @@ int main(int argc, char *argv[]) SLIST_INIT(&g_etmem_objs); project_init(); - migrate_init(); + obj_init(); + engine_init(); if (parse_args(argc, argv, &conf, &obj) != 0) { if (conf.obj != NULL && strcmp(conf.obj, "help") == 0 && @@ -173,7 +132,7 @@ int main(int argc, char *argv[]) goto out; } - if (conf.cmd == ETMEM_CMD_HELP) { + if (strcmp(conf.argv[0], "help") == 0) { obj->help(); err = 0; goto out; @@ -187,7 +146,8 @@ int main(int argc, char *argv[]) } out: + engine_exit(); + obj_exit(); project_exit(); - migrate_exit(); return err; } diff --git a/src/etmem_src/etmem_common.c b/src/etmem_src/etmem_common.c index 9725db7..65d3690 100644 --- a/src/etmem_src/etmem_common.c +++ b/src/etmem_src/etmem_common.c @@ -60,7 +60,7 @@ int parse_name_string(const char *val, char **name_str, size_t max_len) int etmem_parse_check_result(int params_cnt, int argc) { if (params_cnt > 0 && argc - 1 > params_cnt * 2) { /* maximum number of args is limited to params_cnt * 2+1 */ - printf("warn: useless parameters passed in\n"); + printf("warn: etmem useless parameters passed in\n"); return -E2BIG; } @@ -83,6 +83,22 @@ void free_proj_member(struct mem_proj *proj) free(proj->sock_name); proj->sock_name = NULL; } + + if (proj->eng_name != NULL) { + free(proj->eng_name); + proj->eng_name = NULL; + } + + if (proj->task_name != NULL) { + free(proj->task_name); + proj->task_name = NULL; + } + + if (proj->eng_cmd != NULL) { + free(proj->eng_cmd); + proj->eng_cmd = NULL; + } + } diff --git a/src/etmem_src/etmem_engine.c b/src/etmem_src/etmem_engine.c new file mode 100644 index 0000000..7673205 --- /dev/null +++ b/src/etmem_src/etmem_engine.c @@ -0,0 +1,156 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * etmem is licensed under the 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. + * Author: louhongxiang + * Create: 2021-4-13 + * Description: engine operation for etmem client + ******************************************************************************/ + +#include +#include +#include "etmem_engine.h" +#include "securec.h" +#include "etmem.h" +#include "etmem_common.h" +#include "etmem_rpc.h" + +static void engine_help(void) +{ + printf("etmem engine help information\n"); +} + +static int engine_parse_cmd(struct etmem_conf *conf, struct mem_proj *proj) +{ + if (conf->argc == 0) { + printf("too few params for etmem client engine command\n"); + return -1; + } + proj->eng_cmd = conf->argv[0]; + proj->cmd = ETMEM_CMD_ENGINE; + return 0; +} + +static int engine_parse_args(struct etmem_conf *conf, struct mem_proj *proj) +{ + int opt; + int params_cnt = 0; + int ret; + struct option opts[] = { + {"proj_name", required_argument, NULL,'n'}, + {"socket", required_argument, NULL,'s'}, + {"engine", required_argument, NULL,'e'}, + {"task_name", required_argument, NULL,'t'}, + {NULL, 0, NULL,0}, + }; + + while ((opt = getopt_long(conf->argc, conf->argv, "n:s:e:t:", opts, NULL)) != -1) { + switch (opt) { + case 'n': + proj->proj_name = optarg; + break; + + case 's': + proj->sock_name = optarg; + break; + + case 'e': + proj->eng_name = optarg; + break; + + case 't': + proj->task_name = optarg; + break; + + case '?': + /* fallthrough */ + default: + printf("invalid options: %s\n", conf->argv[optind]); + return -EINVAL; + } + params_cnt++; + + } + ret = etmem_parse_check_result(params_cnt, conf->argc); + return ret; +} + +static int engine_check_params(struct mem_proj *proj) +{ + if (proj->sock_name == NULL || strlen(proj->sock_name) == 0) { + printf("socket name to connect must be given, please check.\n"); + return -1; + } + + if (proj->proj_name == NULL || strlen(proj->proj_name) == 0) { + printf("project name must be given, please check.\n"); + return -1; + } + + if (proj->eng_cmd == NULL || strlen(proj->eng_cmd) == 0) { + printf("engine command must be given, please check.\n"); + return -1; + } + + if (proj->eng_name == NULL || strlen(proj->eng_name) == 0) { + printf("engine name must be given, please check.\n"); + return -1; + } + + return 0; +} + +static int engine_do_cmd(struct etmem_conf *conf) +{ + struct mem_proj proj; + int ret; + + ret = memset_s(&proj, sizeof(struct mem_proj), 0, sizeof(struct mem_proj)); + if (ret != EOK) { + printf("memset_s for mem_proj fail\n"); + return ret; + } + + ret = engine_parse_cmd(conf, &proj); + if (ret != 0) { + printf("engine_parse_cmd fail\n"); + return -1; + } + + ret = engine_parse_args(conf, &proj); + if (ret != 0) { + printf("engine_parse_args fail\n"); + return -1; + } + + ret = engine_check_params(&proj); + if (ret != 0) { + printf("engine_check_params fail\n"); + return -1; + } + + ret = etmem_rpc_client(&proj); + return ret; +} + +static struct etmem_obj g_etmem_engine = { + .name = "engine", + .help = engine_help, + .do_cmd = engine_do_cmd, +}; + +void engine_init(void) +{ + etmem_register_obj(&g_etmem_engine); +} + +void engine_exit(void) +{ + etmem_unregister_obj(&g_etmem_engine); +} diff --git a/src/etmem_src/etmem_obj.c b/src/etmem_src/etmem_obj.c new file mode 100644 index 0000000..a3f66fe --- /dev/null +++ b/src/etmem_src/etmem_obj.c @@ -0,0 +1,197 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * etmem is licensed under the 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. + * Author: louhongxiang + * Create: 2021-4-14 + * Description: etmem obj command API. + ******************************************************************************/ + +#include +#include "securec.h" +#include "etmem.h" +#include "etmem_common.h" +#include "etmem_rpc.h" +#include "etmem_obj.h" + +static void obj_help(void) +{ + printf("\nUsage:\n" + " etmem obj add [options]\n" + " etmem obj del [options]\n" + " etmem obj help [options]\n" + "\nOptions:\n" + " -f|--file Add configuration file\n" + " -s|--socket Socket name to connect\n" + "\nNotes:\n" + " 1. Configuration file must be given.\n" + " 2. Socket name must be given.\n"); +} + +struct obj_cmd_item { + char *cmd_name; + enum etmem_cmd_e cmd; +}; + +static struct obj_cmd_item g_obj_cmd_items[] = { + {"add", ETMEM_CMD_ADD}, + {"del", ETMEM_CMD_DEL}, +}; + +static int obj_parse_cmd(struct etmem_conf *conf, struct mem_proj *proj) +{ + unsigned i; + char *cmd = NULL; + + cmd = conf->argv[0]; + for (i = 0; i < ARRAY_SIZE(g_obj_cmd_items); i++) { + if (strcmp(cmd, g_obj_cmd_items[i].cmd_name) == 0) { + proj->cmd = g_obj_cmd_items[i].cmd; + return 0; + } + } + + printf("obj cmd %s is not supportted.\n", cmd); + return -1; +} + +static int parse_file_name(const char *val, char **file_name) +{ + size_t len; + int ret; + + len = strlen(val) + 1; + if (len > FILE_NAME_MAX_LEN) { + printf("file name is too long, should not be larger than %u\n", FILE_NAME_MAX_LEN); + return -ENAMETOOLONG; + } + + *file_name = (char *)calloc(len, sizeof(char)); + if (*file_name == NULL) { + printf("malloc file name failed\n"); + return -ENOMEM; + } + + ret = strncpy_s(*file_name, len, val, len - 1); + if (ret != EOK) { + printf("strncpy_s file name fail\n"); + free(*file_name); + *file_name = NULL; + return ret; + } + + return 0; +} + +static int obj_parse_args(struct etmem_conf *conf, struct mem_proj *proj) +{ + int opt, ret; + int params_cnt = 0; + struct option opts[] = { + {"file", required_argument, NULL, 'f'}, + {"socket", required_argument, NULL, 's'}, + {NULL, 0, NULL, 0}, + }; + + while ((opt = getopt_long(conf->argc, conf->argv, "f:s:", opts, NULL)) != -1) { + switch (opt) { + case 'f': + ret = parse_file_name(optarg, &proj->file_name); + if (ret != 0) { + printf("parse file name failed\n"); + return ret; + } + break; + case 's': + ret = parse_name_string(optarg, &proj->sock_name, SOCKET_NAME_MAX_LEN); + if (ret != 0) { + printf("parse socket name failed.\n"); + return ret; + } + break; + case '?': + /* fallthrough */ + default: + printf("invalid option: %s\n", conf->argv[optind]); + return -EINVAL; + } + params_cnt++; + } + + ret = etmem_parse_check_result(params_cnt, conf->argc); + + return ret; +} + +static int obj_check_params(const struct mem_proj *proj) +{ + if (proj->sock_name == NULL || strlen(proj->sock_name) == 0) { + printf("socket name to connect must all be given, please check.\n"); + return -EINVAL; + } + + if (proj->file_name == NULL || strlen(proj->file_name) == 0) { + printf("file name must be given in add command.\n"); + return -EINVAL; + } + + return 0; +} + +static int obj_do_cmd(struct etmem_conf *conf) +{ + struct mem_proj proj; + int ret; + + ret = memset_s(&proj, sizeof(struct mem_proj), 0, sizeof(struct mem_proj)); + if (ret != EOK) { + printf("memset_s for mem_proj failed\n"); + return ret; + } + + ret = obj_parse_cmd(conf, &proj); + if (ret != 0) { + printf("obj_parse_cmd fail\n"); + return ret; + } + + ret = obj_parse_args(conf, &proj); + if (ret != 0) { + printf("obj_parse_args fail\n"); + goto EXIT; + } + + ret = obj_check_params(&proj); + if (ret != 0) { + printf("obj_check_params fail\n"); + goto EXIT; + } + + ret = etmem_rpc_client(&proj); + +EXIT: + free_proj_member(&proj); + return ret; +} + +static struct etmem_obj g_etmem_obj = { + .name = "obj", + .help = obj_help, + .do_cmd = obj_do_cmd, +}; + +void obj_init(void) +{ + etmem_register_obj(&g_etmem_obj); +} + +void obj_exit(void) +{ + etmem_unregister_obj(&g_etmem_obj); +} diff --git a/src/etmem_src/etmem_project.c b/src/etmem_src/etmem_project.c index 85363b0..790979e 100644 --- a/src/etmem_src/etmem_project.c +++ b/src/etmem_src/etmem_project.c @@ -27,65 +27,44 @@ static void project_help(void) { fprintf(stderr, "\nUsage:\n" - " etmem project add [options]\n" - " etmem project del [options]\n" + " etmem project start [options]\n" + " etmem project stop [options]\n" " etmem project show [options]\n" " etmem project help\n" "\nOptions:\n" - " -f|--file Add configuration file\n" " -n|--name Add project name\n" " -s|--socket Socket name to connect\n" "\nNotes:\n" " 1. Project name and socket name must be given when execute add or del option.\n" - " 2. Configuration file must be given when execute add option.\n" - " 3. Socket name must be given when execute show option.\n"); + " 2. Socket name must be given when execute show option.\n"); } -static int project_parse_cmd(const struct etmem_conf *conf, struct mem_proj *proj) -{ - switch (conf->cmd) { - case ETMEM_CMD_ADD: - /* fallthrough */ - case ETMEM_CMD_DEL: - /* fallthrough */ - case ETMEM_CMD_SHOW: - goto EXIT; - default: - printf("invalid command %u of project\n", conf->cmd); - return -EINVAL; - } +struct project_cmd_item { + char *cmd_name; + enum etmem_cmd_e cmd; +}; -EXIT: - proj->cmd = conf->cmd; - return 0; -} +static struct project_cmd_item g_project_cmd_items[] = { + {"show", ETMEM_CMD_SHOW}, + {"start", ETMEM_CMD_START}, + {"stop", ETMEM_CMD_STOP}, +}; -static int parse_file_name(const char *val, char **file_name) +static int project_parse_cmd(struct etmem_conf *conf, struct mem_proj *proj) { - size_t len; - int ret; - - len = strlen(val) + 1; - if (len > FILE_NAME_MAX_LEN) { - printf("file name too long, should not be larger than %u\n", FILE_NAME_MAX_LEN); - return -ENAMETOOLONG; - } - - *file_name = (char *)calloc(len, sizeof(char)); - if (*file_name == NULL) { - printf("malloc file name failed.\n"); - return -ENOMEM; - } - - ret = strncpy_s(*file_name, len, val, len - 1); - if (ret != EOK) { - printf("strncpy_s file name failed.\n"); - free(*file_name); - *file_name = NULL; - return ret; + unsigned i; + char *cmd = NULL; + + cmd = conf->argv[0]; + for (i = 0; i < ARRAY_SIZE(g_project_cmd_items); i++) { + if (strcmp(cmd, g_project_cmd_items[i].cmd_name) == 0) { + proj->cmd = g_project_cmd_items[i].cmd; + return 0; + } } - return 0; + printf("project cmd %s is not supported\n", cmd); + return -1; } static int project_parse_args(const struct etmem_conf *conf, struct mem_proj *proj) @@ -94,7 +73,6 @@ static int project_parse_args(const struct etmem_conf *conf, struct mem_proj *pr int ret; int params_cnt = 0; struct option opts[] = { - {"file", required_argument, NULL, 'f'}, {"name", required_argument, NULL, 'n'}, {"socket", required_argument, NULL, 's'}, {NULL, 0, NULL, 0}, @@ -103,13 +81,6 @@ static int project_parse_args(const struct etmem_conf *conf, struct mem_proj *pr while ((opt = getopt_long(conf->argc, conf->argv, "f:n:s:", opts, NULL)) != -1) { switch (opt) { - case 'f': - ret = parse_file_name(optarg, &proj->file_name); - if (ret != 0) { - printf("parse file name failed.\n"); - return ret; - } - break; case 'n': ret = parse_name_string(optarg, &proj->proj_name, PROJECT_NAME_MAX_LEN); if (ret != 0) { @@ -149,14 +120,9 @@ static int project_check_params(const struct mem_proj *proj) return 0; } - if (proj->proj_name == NULL || strlen(proj->proj_name) == 0) { - printf("project name must all be given, please check.\n"); - return -EINVAL; - } - - if (proj->cmd == ETMEM_CMD_ADD) { - if (proj->file_name == NULL || strlen(proj->file_name) == 0) { - printf("file name must be given in add command.\n"); + if (proj->cmd == ETMEM_CMD_START || proj->cmd == ETMEM_CMD_STOP) { + if (proj->proj_name == NULL || strlen(proj->proj_name) == 0) { + printf("project name must be given, please check\n"); return -EINVAL; } } @@ -164,7 +130,7 @@ static int project_check_params(const struct mem_proj *proj) return 0; } -static int project_do_cmd(const struct etmem_conf *conf) +static int project_do_cmd(struct etmem_conf *conf) { struct mem_proj proj; int ret; diff --git a/src/etmem_src/etmem_rpc.c b/src/etmem_src/etmem_rpc.c index 12a8f6d..8d03914 100644 --- a/src/etmem_src/etmem_rpc.c +++ b/src/etmem_src/etmem_rpc.c @@ -24,7 +24,7 @@ #include "securec.h" #include "etmem_rpc.h" -#define ETMEM_CLIENT_REGISTER "proj_name %s file_name %s cmd %d" +#define ETMEM_CLIENT_REGISTER "proj_name %s file_name %s cmd %d eng_name %s eng_cmd %s task_name %s" #define INT_MAX_LEN 9 #define ETMEM_RPC_RECV_BUF_LEN 512 @@ -86,6 +86,15 @@ static size_t etmem_client_get_str_len(const struct mem_proj *proj) if (proj->file_name != NULL) { total_len += strlen(proj->file_name); } + if (proj->eng_name != NULL) { + total_len += strlen(proj->eng_name); + } + if (proj->eng_cmd != NULL) { + total_len += strlen(proj->eng_cmd); + } + if (proj->task_name != NULL) { + total_len += strlen(proj->task_name); + } return total_len; } @@ -108,7 +117,10 @@ static int etmem_client_send(const struct mem_proj *proj, int sockfd) if (snprintf_s(reg_cmd, total_len, total_len - 1, ETMEM_CLIENT_REGISTER, proj->proj_name == NULL ? "-" : proj->proj_name, proj->file_name == NULL ? "-" : proj->file_name, - proj->cmd) <= 0) { + proj->cmd, + proj->eng_name == NULL ? "-" : proj->eng_name, + proj->eng_cmd == NULL ? "-" : proj->eng_cmd, + proj->task_name == NULL ? "-" : proj->task_name) <= 0) { printf("snprintf_s failed.\n"); goto EXIT; } @@ -135,16 +147,15 @@ EXIT: static bool etmem_recv_find_fail_keyword(const char *recv_msg) { if (strstr(recv_msg, "error") != NULL) { - printf("get rpc error response. %s\n", recv_msg); return true; } - return false; } static int etmem_client_recv(int sockfd) { - ssize_t ret; + ssize_t recv_size; + int ret = -1; char *recv_msg = NULL; uint8_t *recv_buf = NULL; size_t recv_len = ETMEM_RPC_RECV_BUF_LEN; @@ -155,22 +166,27 @@ static int etmem_client_recv(int sockfd) return -1; } - ret = recv(sockfd, recv_buf, recv_len, 0); - if (ret < 0) { - perror("recv failed:"); - goto EXIT; - } - if ((unsigned long)ret >= recv_len) { - printf("recv too long.\n"); - ret = -1; - goto EXIT; - } - - recv_msg = (char *)recv_buf; - recv_msg[recv_len - 1] = '\0'; - if (etmem_recv_find_fail_keyword(recv_msg)) { - ret = 0; - goto EXIT; + while (true) { + recv_size = recv(sockfd, recv_buf, recv_len - 1, 0); + if (recv_size < 0) { + perror("recv failed:"); + goto EXIT; + } + if (recv_size == 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + printf("recv timeout:\n"); + } + ret = 0; + goto EXIT; + } + + recv_msg = (char *)recv_buf; + recv_msg[recv_size] = '\0'; + printf("%s", recv_msg); + if (etmem_recv_find_fail_keyword(recv_msg)) { + printf("error occurs when getting response from etmemd server\n"); + goto EXIT; + } } EXIT: @@ -207,12 +223,6 @@ int etmem_rpc_client(const struct mem_proj *proj) ret = -ECOMM; goto EXIT; } - if (ret == 0) { - printf("error occurs when getting response from etmemd server.\n"); - ret = -EPERM; - goto EXIT; - } - ret = 0; EXIT: close(sockfd); diff --git a/src/etmem_src/etmem_task.c b/src/etmem_src/etmem_task.c deleted file mode 100644 index fc3206d..0000000 --- a/src/etmem_src/etmem_task.c +++ /dev/null @@ -1,161 +0,0 @@ -/****************************************************************************** - * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. - * etmem is licensed under the 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. - * Author: louhongxiang - * Create: 2019-12-10 - * Description: Migration command API. - ******************************************************************************/ - -#include -#include -#include -#include -#include "securec.h" -#include "etmem.h" -#include "etmem_task.h" -#include "etmem_common.h" -#include "etmem_rpc.h" - -static void migrate_help(void) -{ - fprintf(stderr, - "\nUsage:\n" - " etmem migrate start [options]\n" - " etmem migrate stop [options]\n" - " etmem migrate help\n" - "\nOptions:\n" - " -n|--name Add project name\n" - " -s|--socket Socket name to connect\n" - "\nNotes:\n" - " Project name and socket name must be given when execute start or stop option.\n"); -} - -static int migrate_parse_cmd(const struct etmem_conf *conf, struct mem_proj *proj) -{ - switch (conf->cmd) { - case ETMEM_CMD_START: - /* fallthrough */ - case ETMEM_CMD_STOP: - goto EXIT; - default: - printf("invalid command %u of migrate\n", conf->cmd); - return -EINVAL; - } - -EXIT: - proj->cmd = conf->cmd; - return 0; -} - -static int migrate_parse_args(const struct etmem_conf *conf, struct mem_proj *proj) -{ - int opt; - int params_cnt = 0; - int ret; - struct option opts[] = { - {"name", required_argument, NULL, 'n'}, - {"socket", required_argument, NULL, 's'}, - {NULL, 0, NULL, 0}, - }; - - while ((opt = getopt_long(conf->argc, conf->argv, "n:s:", - opts, NULL)) != -1) { - switch (opt) { - case 'n': - ret = parse_name_string(optarg, &proj->proj_name, PROJECT_NAME_MAX_LEN); - if (ret != 0) { - printf("parse project name failed.\n"); - return ret; - } - break; - case 's': - ret = parse_name_string(optarg, &proj->sock_name, SOCKET_NAME_MAX_LEN); - if (ret != 0) { - printf("parse socket name failed.\n"); - return ret; - } - break; - case '?': - /* fallthrough */ - default: - ret = -EINVAL; - printf("invalid option: %s\n", conf->argv[optind]); - return ret; - } - params_cnt++; - } - - ret = etmem_parse_check_result(params_cnt, conf->argc); - if (ret != 0) { - return ret; - } - - return 0; -} - -static int migrate_check_params(const struct mem_proj *proj) -{ - if (proj->proj_name == NULL || strlen(proj->proj_name) == 0 || - proj->sock_name == NULL || strlen(proj->sock_name) == 0) { - printf("project name and socket name must all be given, please check.\n"); - return -EINVAL; - } - - return 0; -} - -static int migrate_do_cmd(const struct etmem_conf *conf) -{ - struct mem_proj proj; - int ret; - - ret = memset_s(&proj, sizeof(struct mem_proj), 0, sizeof(struct mem_proj)); - if (ret != EOK) { - printf("memset_s for mem_proj failed.\n"); - return ret; - } - - ret = migrate_parse_cmd(conf, &proj); - if (ret != 0) { - goto EXIT; - } - - ret = migrate_parse_args(conf, &proj); - if (ret != 0) { - goto EXIT; - } - - ret = migrate_check_params(&proj); - if (ret != 0) { - goto EXIT; - } - - etmem_rpc_client(&proj); - -EXIT: - free_proj_member(&proj); - return ret; -} - -static struct etmem_obj g_etmem_migrate = { - .name = "migrate", - .help = migrate_help, - .do_cmd = migrate_do_cmd, -}; - -void migrate_init(void) -{ - etmem_register_obj(&g_etmem_migrate); -} - -void migrate_exit(void) -{ - etmem_unregister_obj(&g_etmem_migrate); -} diff --git a/src/etmemd_src/etmemd_common.c b/src/etmemd_src/etmemd_common.c index 0fe070c..43ed013 100644 --- a/src/etmemd_src/etmemd_common.c +++ b/src/etmemd_src/etmemd_common.c @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include "securec.h" #include "etmemd_common.h" @@ -214,9 +217,10 @@ static char *etmemd_get_proc_file_str(const char *pid, const char *file) return file_name; } -FILE *etmemd_get_proc_file(const char *pid, const char *file, const char *mode) +FILE *etmemd_get_proc_file(const char *pid, const char *file, int flags, const char *mode) { char *file_name = NULL; + int fd = -1; FILE *fp = NULL; file_name = etmemd_get_proc_file_str(pid, file); @@ -224,7 +228,14 @@ FILE *etmemd_get_proc_file(const char *pid, const char *file, const char *mode) return NULL; } - fp = fopen(file_name, mode); + fd = open(file_name, flags); + if (fd < 0) { + etmemd_log(ETMEMD_LOG_ERR, "open file %s fail\n", file_name); + goto free_file_name; + } + fp = fdopen(fd, mode); + +free_file_name: free(file_name); return fp; } @@ -366,3 +377,41 @@ char *skip_blank_line(FILE *file) return get_line; } + +static int write_all(int fd, const char *buf) +{ + ssize_t rest = strlen(buf); + ssize_t send_size; + + while (rest > 0) { + send_size = write(fd, buf, rest); + if (send_size < 0) { + return -1; + } + rest -= send_size; + } + return 0; +} + +int dprintf_all(int fd, const char *format, ...) +{ + char line[FILE_LINE_MAX_LEN]; + int ret; + va_list args_in; + + va_start(args_in, format); + ret = vsprintf_s(line, FILE_LINE_MAX_LEN, format, args_in); + if (ret > FILE_LINE_MAX_LEN) { + etmemd_log(ETMEMD_LOG_ERR, "fprintf_all fail as truncated.\n"); + return -1; + } + + ret = write_all(fd, line); + if (ret < 0) { + etmemd_log(ETMEMD_LOG_ERR, "write_all fail.\n"); + return -1; + } + + va_end(args_in); + return 0; +} diff --git a/src/etmemd_src/etmemd_cslide.c b/src/etmemd_src/etmemd_cslide.c new file mode 100644 index 0000000..a3692ad --- /dev/null +++ b/src/etmemd_src/etmemd_cslide.c @@ -0,0 +1,2272 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * etmem is licensed under the 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. + * Author: louhongxiang + * Create: 2021-4-19 + * Description: Memigd cslide API. + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "securec.h" +#include "etmemd_log.h" +#include "etmemd_common.h" +#include "etmemd_engine.h" +#include "etmemd_cslide.h" +#include "etmemd_scan.h" +#include "etmemd_migrate.h" +#include "etmemd_file.h" + +#define HUGE_1M_SIZE (1 << 20) +#define HUGE_2M_SIZE (2 << 20) +#define HUGE_1G_SIZE (1 << 30) +#define BYTE_TO_KB(s) ((s) >> 10) +#define KB_TO_BYTE(s) ((s) << 10) + +#define TO_PCT 100 +#define MAX_WM 100 +#define MIN_WM 0 + +#define BATCHSIZE (1 << 16) + +#define factory_foreach_working_pid_params(iter, factory) \ + for ((iter) = (factory)->working_head, next_working_params(&(iter)); (iter) != NULL; (iter) = (iter)->next, next_working_params(&(iter))) + +#define factory_foreach_pid_params(iter, factory) \ + for ((iter) = (factory)->working_head; (iter) != NULL; (iter) = (iter)->next) + +struct node_mem { + long long huge_total; + long long huge_free; +}; + +struct sys_mem { + int node_num; + struct node_mem *node_mem; +}; + +struct node_page_refs { + struct page_refs *head; + struct page_refs *tail; + int64_t size; + uint32_t num; +}; + +struct count_page_refs { + struct node_page_refs *node_pfs; + int node_num; +}; + +struct node_pair { + int index; + int hot_node; + int cold_node; +}; + +struct node_map { + struct node_pair *pair; + int total_num; + int cur_num; +}; + +struct node_verifier { + int node_num; + int *nodes_map_count; +}; + +struct cslide_task_params { + bool anon_only; + struct { + char *vmflags_str; + char **vmflags_array; + int vmflags_num; + }; +}; + +struct vma_pf { + struct vma *vma; + struct page_refs *page_refs; + struct vma_pf *next; +}; + +struct node_pages_info { + uint32_t hot; + uint32_t cold; +}; + +enum pid_param_state { + STATE_NONE = 0, + STATE_WORKING, + STATE_REMOVE, + STATE_FREE, +}; + +struct cslide_pid_params { + enum pid_param_state state; + int count; + struct count_page_refs *count_page_refs; + struct memory_grade *memory_grade; + struct node_pages_info *node_pages_info; + struct vmas *vmas; + struct vma_pf *vma_pf; + unsigned int pid; + struct cslide_eng_params *eng_params; + struct cslide_task_params *task_params; + struct cslide_pid_params *next; +}; + +struct cslide_params_factory { + pthread_mutex_t mtx; + struct cslide_pid_params *to_add_head; + struct cslide_pid_params *to_add_tail; + struct cslide_pid_params *working_head; +}; + +struct cslide_eng_params { + struct sys_mem mem; + struct node_map node_map; + int node_watermark; + int hot_threshold; + int hot_reserve; // in MB + int mig_quota; // in MB + pthread_t worker; + pthread_mutex_t stat_mtx; + time_t stat_time; + struct { + int loop; + int interval; + int sleep; + }; + struct cslide_params_factory factory; + bool finish; +}; + +struct ctrl_cap { + long long cap; + long long used; +}; + +struct node_ctrl { + struct ctrl_cap hot_move_cap; + struct ctrl_cap hot_prefetch_cap; + struct ctrl_cap cold_move_cap; + long long cold_replaced; + long long cold_free; + long long free; + long long total; + long long quota; + long long reserve; +}; + +struct flow_ctrl { + struct node_ctrl *node_ctrl; + int pair_num; + int hot_enough; + int prefetch_enough; + int cold_enough; +}; + +struct page_filter { + void (*flow_cal_func)(struct flow_ctrl *ctrl); + long long (*flow_move_func)(struct flow_ctrl *ctrl, long long target, int node); + bool (*flow_enough)(struct flow_ctrl *ctrl); + void (*filter_policy)(struct page_filter *filter, struct node_pair *pair, struct count_page_refs *cpf, struct memory_grade *memory_grade); + struct flow_ctrl *ctrl; + int count_start; + int count_end; + int count_step; +}; + +struct page_offset { + struct page_refs *page_refs; + uint64_t to_offset; +}; + +struct cslide_cmd_item { + char *name; + int (*func)(void *params, int fd); +}; + +struct vma_pf *g_share_vma_head = NULL; + +static inline int get_node_num(void) +{ + return numa_num_configured_nodes(); +} + +static int init_sys_mem(struct sys_mem *mem) +{ + int node_num = get_node_num(); + + if (node_num <= 0) { + etmemd_log(ETMEMD_LOG_ERR, "node number %d is invalid \n", node_num); + return -1; + } + + mem->node_mem = malloc(sizeof(struct node_mem) * node_num); + if (mem->node_mem == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc node_mem fail\n"); + return -1; + } + mem->node_num = node_num; + return 0; +} + +static void destroy_sys_mem(struct sys_mem *mem) +{ + mem->node_num = -1; + free(mem->node_mem); + mem->node_mem = NULL; +} + +static int read_hugepage_num(char *path) +{ + FILE *f = NULL; + char *line = NULL; + size_t line_len = 0; + int nr = -1; + + f = fopen(path, "r"); + if (f == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "open file %s failed\n", path); + return -1; + } + if (getline(&line, &line_len, f) < 0) { + etmemd_log(ETMEMD_LOG_ERR, "read free hugepage from %s fail\n", path); + goto close_file; + } + + nr = strtoull(line, NULL, 0); + free(line); +close_file: + fclose(f); + return nr; +} + +static long long get_single_huge_mem(int node, int huge_size, char *huge_state) +{ + char path[PATH_MAX]; + int nr; + + if (sprintf_s(path, PATH_MAX, "/sys/devices/system/node/node%d/hugepages/hugepages-%dkB/%s_hugepages", + node, huge_size, huge_state) <= 0) { + etmemd_log(ETMEMD_LOG_ERR, "snprintf path to get hugepage number fail\n"); + return -1; + } + + nr = read_hugepage_num(path); + if (nr < 0) { + return -1; + } + + return KB_TO_BYTE((long long)nr * (long long)huge_size); +} + +static long long get_node_huge_total(int node, char *huge_state) +{ + long long size_2m; + + size_2m = get_single_huge_mem(node, BYTE_TO_KB(HUGE_2M_SIZE), huge_state); + if (size_2m < 0) { + etmemd_log(ETMEMD_LOG_ERR, "get 2M %s page in node %d fail\n", huge_state, node); + return -1; + } + + return size_2m; +} + +static int get_node_huge_mem(int node, struct node_mem *mem) +{ + mem->huge_total = get_node_huge_total(node, "nr"); + if (mem->huge_total <= 0) { + etmemd_log(ETMEMD_LOG_ERR, "get total hugepages of node %d fail\n", node); + return -1; + } + mem->huge_free = get_node_huge_total(node, "free"); + if (mem->huge_free < 0) { + etmemd_log(ETMEMD_LOG_ERR, "get free hugepages of node %d fail\n", node); + return -1; + } + + return 0; +} + +static int get_node_mem(int node, struct node_mem *mem) +{ + if (get_node_huge_mem(node, mem) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "get huge memory info of node %d fail\n", node); + return -1; + } + + return 0; +} + +static int get_sys_mem(struct sys_mem *mem) +{ + int i; + + for (i = 0; i < mem->node_num; i++) { + if (get_node_mem(i, &(mem->node_mem[i])) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "get memory info of node %d fail\n", i); + return -1; + } + } + + return 0; +} + +static void init_node_page_refs(struct node_page_refs *npf) +{ + npf->head = NULL; + npf->tail = NULL; + npf->size = 0; + npf->num = 0; +} + +static void clean_node_page_refs(struct node_page_refs *npf) +{ + clean_page_refs_unexpected(&npf->head); + npf->tail = NULL; + npf->size = 0; + npf->num = 0; +} + +static void npf_add_pf(struct node_page_refs *npf, struct page_refs *page_refs) +{ + if (npf->head == NULL) { + npf->head = page_refs; + npf->tail = page_refs; + } else { + npf->tail->next = page_refs; + npf->tail = page_refs; + } + + npf->size += page_type_to_size(page_refs->type); + npf->num++; +} + +/* must called when all pf add to npf with npf_add_pf */ +static void npf_setup_tail(struct node_page_refs *npf) +{ + if (npf->tail != NULL) { + npf->tail->next = NULL; + } +} + +static long long move_npf_to_list(struct node_page_refs *npf, struct page_refs **list, long long size) +{ + struct page_refs *t = NULL; + struct page_refs *iter = NULL; + struct page_refs *tmp = NULL; + long long moved_size = 0; + + if (npf->size <= size) { + t = npf->tail; + moved_size = npf->size; + } else { + for (iter = npf->head; iter != NULL && size >= moved_size + page_type_to_size(iter->type); iter = iter->next) { + moved_size += page_type_to_size(iter->type); + t = iter; + } + } + + if (t != NULL) { + tmp = t->next; + t->next = *list; + *list = npf->head; + npf->head = tmp; + if (tmp == NULL) { + npf->tail = NULL; + } + } + + npf->size -= moved_size; + return moved_size; +} + +static int init_count_page_refs(struct count_page_refs *cpf, int node_num) +{ + int i; + + cpf->node_pfs = malloc(sizeof(struct node_page_refs) * node_num); + if (cpf->node_pfs == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc node_page_refs fail\n"); + return -1; + } + for (i = 0; i < node_num; i++) { + init_node_page_refs(&cpf->node_pfs[i]); + } + cpf->node_num = node_num; + return 0; +} + +static void clean_count_page_refs(struct count_page_refs *cpf) +{ + int i; + + for (i = 0; i < cpf->node_num; i++) { + clean_node_page_refs(&cpf->node_pfs[i]); + } +} + +static void destroy_count_page_refs(struct count_page_refs *cpf) +{ + clean_count_page_refs(cpf); + free(cpf->node_pfs); + cpf->node_pfs = NULL; + cpf->node_num = 0; +} + +static void insert_count_pfs(struct count_page_refs *cpf, struct page_refs *pf, int *nodes, int num) +{ + struct node_page_refs *npf = NULL; + struct page_refs *next = NULL; + int node, count, i; + + for (i = 0; i < num; i++) { + next = pf->next; + node = nodes[i]; + if (node < 0 || node >= cpf->node_num) { + etmemd_log(ETMEMD_LOG_WARN, "addr %llx with invalid node %d\n", pf->addr, node); + pf->next = NULL; + etmemd_free_page_refs(pf); + pf = next; + continue; + } + count = pf->count; + npf = &cpf[count].node_pfs[node]; + npf_add_pf(npf, pf); + pf = next; + } +} + +/* must called after all page_refs insert by insert_count_pfs */ +static void setup_count_pfs_tail(struct count_page_refs *cpf, int count) +{ + int node, c; + struct node_page_refs *npf = NULL; + + for (c = 0; c <= count; c++) { + for (node = 0; node < cpf->node_num; node++) { + npf = &cpf[c].node_pfs[node]; + npf_setup_tail(npf); + } + } +} + +static int init_node_map(struct node_map *node_map, int node_num) +{ + int pair_num; + + if (node_num % 2 != 0) { + etmemd_log(ETMEMD_LOG_ERR, "node_num is not even\n"); + return -1; + } + pair_num = node_num / 2; + node_map->pair = calloc(pair_num, sizeof(struct node_pair)); + if (node_map->pair == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memory for node map fail\n"); + return -1; + } + node_map->total_num = pair_num; + node_map->cur_num = 0; + return 0; +} + +static void destroy_node_map(struct node_map *map) +{ + free(map->pair); + map->pair = NULL; + map->total_num = 0; + map->cur_num = 0; +} + +static int add_node_pair(struct node_map *map, int cold_node, int hot_node) +{ + if (map->cur_num == map->total_num) { + etmemd_log(ETMEMD_LOG_ERR, "too much pair, add pair hot %d cold %d fail\n", + hot_node, cold_node); + return -1; + } + map->pair[map->cur_num].hot_node = hot_node; + map->pair[map->cur_num].cold_node = cold_node; + map->pair[map->cur_num].index = map->cur_num; + map->cur_num++; + return 0; +} + + +static int init_node_verifier(struct node_verifier *nv, int node_num) +{ + nv->nodes_map_count = calloc(node_num, sizeof(int)); + if (nv->nodes_map_count == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memroy for nodes_map_count failed\n"); + return -1; + } + + nv->node_num = node_num; + return 0; +} + +static void destroy_node_verifier(struct node_verifier *nv) +{ + free(nv->nodes_map_count); +} + +static bool is_node_valid(struct node_verifier *nv, int node) +{ + if (node < 0 || node >= nv->node_num) { + etmemd_log(ETMEMD_LOG_ERR, "node %d out of range\n", node); + return false; + } + + if (nv->nodes_map_count[node] != 0) { + etmemd_log(ETMEMD_LOG_ERR, "node %d remap\n", node); + return false; + } + + nv->nodes_map_count[node]++; + return true; +} + +static bool has_node_unmap(struct node_verifier *nv) +{ + int i; + bool ret = false; + + for (i = 0; i < nv->node_num; i++) { + if (nv->nodes_map_count[i] == 0) { + etmemd_log(ETMEMD_LOG_ERR, "unmap node %d\n", i); + ret = true; + } + } + + return ret; +} + +static void clear_task_params(struct cslide_task_params *params) +{ + if (params->vmflags_str != NULL) { + free(params->vmflags_str); + params->vmflags_str = NULL; + } + if (params->vmflags_array != NULL) { + free(params->vmflags_array); + params->vmflags_array = NULL; + } +} + +static struct cslide_pid_params *alloc_pid_params(struct cslide_eng_params *eng_params) +{ + int i; + struct cslide_pid_params *params = calloc(1, sizeof(struct cslide_pid_params)); + int count = eng_params->loop; + int pair_num = eng_params->node_map.cur_num; + int node_num = eng_params->mem.node_num; + + if (params == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "malloc cslide pid params fail\n"); + return NULL; + } + params->count_page_refs = malloc(sizeof(struct count_page_refs) * (count + 1)); + if (params->count_page_refs == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "malloc counted page_refs fail\n"); + goto free_params; + } + + for (i = 0; i <= count; i++) { + if (init_count_page_refs(¶ms->count_page_refs[i], node_num) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init counted page_refs fail\n"); + goto free_count_page_refs; + } + } + + params->count = count; + params->memory_grade = calloc(pair_num, sizeof(struct memory_grade)); + if (params->memory_grade == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memory_grade fail\n"); + goto free_count_page_refs; + } + + params->node_pages_info = calloc(node_num, sizeof(struct node_pages_info)); + if (params->node_pages_info == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc pages info fail\n"); + goto free_memory_grade; + } + return params; + +free_memory_grade: + free(params->memory_grade); + params->memory_grade = NULL; +free_count_page_refs: + for (i--; i >= 0; i--) { + destroy_count_page_refs(¶ms->count_page_refs[i]); + } + free(params->count_page_refs); + params->count_page_refs = NULL; +free_params: + free(params); + params = NULL; + return NULL; +} + +static void free_pid_params(struct cslide_pid_params *params) +{ + int count = params->count; + int i; + + free(params->node_pages_info); + params->node_pages_info = NULL; + free(params->memory_grade); + params->memory_grade = NULL; + for (i = 0; i <= count; i++) { + destroy_count_page_refs(¶ms->count_page_refs[i]); + } + free(params->count_page_refs); + params->count_page_refs = NULL; + if (params->task_params != NULL) { + clear_task_params(params->task_params); + free(params->task_params); + params->task_params = NULL; + } + free(params); +} + +static void clean_pid_param(struct cslide_pid_params *pid_params) +{ + struct cslide_eng_params *eng_params = pid_params->eng_params; + int pair_num = eng_params->node_map.cur_num; + int i; + + for (i = 0; i < pair_num; i++) { + clean_page_refs_unexpected(&pid_params->memory_grade[i].hot_pages); + clean_page_refs_unexpected(&pid_params->memory_grade[i].cold_pages); + } + for (i = 0; i <= pid_params->count; i++) { + clean_count_page_refs(&pid_params->count_page_refs[i]); + } +} + +static void destroy_factory(struct cslide_params_factory *factory) +{ + pthread_mutex_destroy(&factory->mtx); +} + +static int init_factory(struct cslide_params_factory *factory) +{ + return pthread_mutex_init(&factory->mtx, NULL); +} + +static void factory_add_pid_params(struct cslide_params_factory *factory, struct cslide_pid_params *params) +{ + enum pid_param_state state = params->state; + params->state = STATE_WORKING; + + if (state == STATE_NONE) { + pthread_mutex_lock(&factory->mtx); + params->next = factory->to_add_head; + factory->to_add_head = params; + if (factory->to_add_tail == NULL) { + factory->to_add_tail = params; + } + pthread_mutex_unlock(&factory->mtx); + } +} + +static void factory_remove_pid_params(struct cslide_params_factory *factory, struct cslide_pid_params *params) +{ + params->state = STATE_REMOVE; +} + +static void factory_free_pid_params(struct cslide_params_factory *factory, struct cslide_pid_params *params) +{ + params->state = STATE_FREE; +} + +static void next_working_params(struct cslide_pid_params **params) +{ + while (*params != NULL && (*params)->state != STATE_WORKING) { + *params = (*params)->next; + } +} + +/* add and free operations will take effect here */ +static void factory_update_pid_params(struct cslide_params_factory *factory) +{ + struct cslide_pid_params **prev = NULL; + struct cslide_pid_params *iter = NULL; + struct cslide_pid_params *to_add_head = NULL; + struct cslide_pid_params *to_add_tail = NULL; + + /* get new added params first */ + pthread_mutex_lock(&factory->mtx); + to_add_head = factory->to_add_head; + to_add_tail = factory->to_add_tail; + factory->to_add_head = NULL; + factory->to_add_tail = NULL; + pthread_mutex_unlock(&factory->mtx); + + if (to_add_head != NULL) { + to_add_tail->next = factory->working_head; + factory->working_head = to_add_head; + } + + /* clear the freed params */ + prev = &factory->working_head; + for (iter = *prev; iter != NULL; iter = *prev) { + if (iter->state != STATE_FREE) { + prev = &(iter->next); + continue; + } + *prev = iter->next; + iter->next = NULL; + free_pid_params(iter); + } +} + +static bool factory_working_empty(struct cslide_params_factory *factory) +{ + struct cslide_pid_params *pid_params = NULL; + + factory_foreach_pid_params(pid_params, factory) { + if (pid_params->state == STATE_WORKING) { + return false; + } + } + return true; +} + +static struct page_refs *next_vma_pf(struct cslide_pid_params *params, int *cur) +{ + int vma_pf_num = params->vmas->vma_cnt; + struct vma_pf *vma_pf = params->vma_pf; + struct page_refs *pf = NULL; + + while (*cur < vma_pf_num) { + pf = vma_pf[*cur].page_refs; + vma_pf[*cur].page_refs = NULL; + (*cur)++; + if (pf != NULL) { + break; + } + } + return pf; +} + +static int cslide_count_node_pfs(struct cslide_pid_params *params) +{ + struct page_refs *page_refs = NULL; + struct page_refs *last = NULL; + unsigned int pid = params->pid; + int batch_size = BATCHSIZE; + void **pages = NULL; + int *status = NULL; + int actual_num = 0; + int ret = -1; + int vma_i = 0; + + if (params->vmas == NULL || params->vma_pf == NULL) { + return 0; + } + + status = malloc(sizeof(int) * batch_size); + if (status == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "malloc status fail\n"); + return -1; + } + + pages = malloc(sizeof(void *) * batch_size); + if (pages == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "malloc pages fail\n"); + goto free_status; + } + + page_refs = next_vma_pf(params, &vma_i); + last = page_refs; + while (page_refs != NULL) { + pages[actual_num++] = (void *)page_refs->addr; + if (page_refs->next == NULL) { + page_refs->next = next_vma_pf(params, &vma_i); + } + if (actual_num == batch_size || page_refs->next == NULL) { + if (move_pages(pid, actual_num, pages, NULL, status, MPOL_MF_MOVE_ALL) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "get page refs numa node fail\n"); + goto free_pages; + } + insert_count_pfs(params->count_page_refs, last, status, actual_num); + last = page_refs->next; + actual_num = 0; + } + page_refs = page_refs->next; + } + setup_count_pfs_tail(params->count_page_refs, params->count); + ret = 0; + +free_pages: + free(pages); + pages = NULL; +free_status: + free(status); + status = NULL; + return ret; +} + +static inline bool cap_avail(struct ctrl_cap *cap) +{ + return cap->used < cap->cap; +} + +/* return true if cap is still available */ +static inline bool cap_cost(struct ctrl_cap *cap, long long *cost) +{ + if (*cost < cap->cap - cap->used) { + cap->used += *cost; + return true; + } + + *cost = cap->cap - cap->used; + cap->used = cap->cap; + return false; +} + +/* return true if node can move hot pages */ +static bool node_cal_hot_can_move(struct node_ctrl *node_ctrl) +{ + long long can_move; + + if (node_ctrl->quota < node_ctrl->free) { + can_move = node_ctrl->quota; + } else { + can_move = node_ctrl->free + (node_ctrl->quota - node_ctrl->free) / 2; + if (can_move > node_ctrl->free + node_ctrl->cold_free) { + can_move = node_ctrl->free + node_ctrl->cold_free; + } + } + + if (can_move > node_ctrl->total) { + can_move = node_ctrl->total; + } + node_ctrl->hot_move_cap.cap = can_move; + return can_move > 0; +} + +static inline bool node_can_move_hot(struct node_ctrl *node_ctrl) +{ + return cap_avail(&node_ctrl->hot_move_cap); +} + +static inline bool node_move_hot(struct node_ctrl *node_ctrl, long long *target) +{ + return cap_cost(&node_ctrl->hot_move_cap, target); +} + +static void node_update_hot_move(struct node_ctrl *node_ctrl) +{ + long long hot_move = node_ctrl->hot_move_cap.used; + + if (hot_move > node_ctrl->free) { + node_ctrl->cold_replaced += hot_move - node_ctrl->free; + node_ctrl->quota -= node_ctrl->free + (hot_move - node_ctrl->free) * 2; + node_ctrl->cold_free += node_ctrl->free; + node_ctrl->free = 0; + } else { + node_ctrl->free -= hot_move; + node_ctrl->quota -= hot_move; + node_ctrl->cold_free += hot_move; + } +} + +/* return true if node can prefetch hot pages */ +static bool node_cal_hot_can_prefetch(struct node_ctrl *node_ctrl) +{ + long long can_prefetch; + + if (node_ctrl->free <= node_ctrl->reserve) { + can_prefetch = 0; + goto exit; + } + + can_prefetch = node_ctrl->free - node_ctrl->reserve; + if (can_prefetch > node_ctrl->quota) { + can_prefetch = node_ctrl->quota; + } + +exit: + node_ctrl->hot_prefetch_cap.cap = can_prefetch; + return can_prefetch > 0; +} + +static inline bool node_can_prefetch_hot(struct node_ctrl *node_ctrl) +{ + return cap_avail(&node_ctrl->hot_prefetch_cap); +} + +static inline bool node_prefetch_hot(struct node_ctrl *node_ctrl, long long *target) +{ + return cap_cost(&node_ctrl->hot_prefetch_cap, target); +} + +static void node_update_hot_prefetch(struct node_ctrl *node_ctrl) +{ + long long hot_prefetch = node_ctrl->hot_prefetch_cap.used; + + node_ctrl->free -= hot_prefetch; + node_ctrl->quota -= hot_prefetch; + node_ctrl->cold_free += hot_prefetch; +} + +static bool node_cal_cold_can_move(struct node_ctrl *node_ctrl) +{ + long long can_move; + + can_move = node_ctrl->quota < node_ctrl->reserve - node_ctrl->free ? node_ctrl->quota : node_ctrl->reserve - node_ctrl->free; + if (can_move > node_ctrl->cold_free) { + can_move = node_ctrl->cold_free; + } + if (can_move < 0) { + can_move = 0; + } + + node_ctrl->cold_move_cap.cap = can_move + node_ctrl->cold_replaced; + return node_ctrl->cold_move_cap.cap > 0; +} + +static inline bool node_can_move_cold(struct node_ctrl *node_ctrl) +{ + return cap_avail(&node_ctrl->cold_move_cap); +} + +/* return true if still can move cold pages */ +static inline bool node_move_cold(struct node_ctrl *node_ctrl, long long *target) +{ + return cap_cost(&node_ctrl->cold_move_cap, target); +} + +static int init_flow_ctrl(struct flow_ctrl *ctrl, struct sys_mem *sys_mem, struct node_map *node_map, long long quota, long long reserve) +{ + struct node_pair *pair = NULL; + struct node_ctrl *tmp = NULL; + int i; + + ctrl->node_ctrl = calloc(node_map->cur_num, sizeof(struct node_ctrl)); + if (ctrl->node_ctrl == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memory for node_ctrl fail\n"); + return -1; + } + + ctrl->pair_num = node_map->cur_num; + ctrl->hot_enough = 0; + ctrl->cold_enough = 0; + ctrl->prefetch_enough = 0; + for (i = 0; i < ctrl->pair_num; i++) { + pair = &node_map->pair[i]; + tmp = &ctrl->node_ctrl[i]; + tmp->cold_free = sys_mem->node_mem[pair->cold_node].huge_free; + tmp->free = sys_mem->node_mem[pair->hot_node].huge_free; + tmp->total = sys_mem->node_mem[pair->hot_node].huge_total; + tmp->quota = quota; + tmp->reserve = reserve; + } + return 0; +} + +static void flow_cal_hot_can_move(struct flow_ctrl *ctrl) +{ + int i; + + for (i = 0; i < ctrl->pair_num; i++) { + if (!node_cal_hot_can_move(&ctrl->node_ctrl[i])) { + ctrl->hot_enough++; + } + } +} + +static inline bool is_hot_enough(struct flow_ctrl *ctrl) +{ + return ctrl->hot_enough == ctrl->pair_num; +} + +static long long ctrl_hot_move(struct flow_ctrl *ctrl, long long target, int node) +{ + struct node_ctrl *node_ctrl = &ctrl->node_ctrl[node]; + + if (!node_can_move_hot(node_ctrl)) { + return 0; + } + + if (!node_move_hot(node_ctrl, &target)) { + ctrl->hot_enough++; + } + + return target; +} + +static void flow_cal_hot_can_prefetch(struct flow_ctrl *ctrl) +{ + int i; + + for (i = 0; i < ctrl->pair_num; i++) { + node_update_hot_move(&ctrl->node_ctrl[i]); + if (!node_cal_hot_can_prefetch(&ctrl->node_ctrl[i])) { + ctrl->prefetch_enough++; + } + } +} + +static inline bool is_prefetch_enough(struct flow_ctrl *ctrl) +{ + return ctrl->prefetch_enough == ctrl->pair_num; +} + +static long long ctrl_prefetch_hot(struct flow_ctrl *ctrl, long long target, int node) +{ + struct node_ctrl *node_ctrl = &ctrl->node_ctrl[node]; + + if (!node_can_prefetch_hot(node_ctrl)) { + return 0; + } + + if (!node_prefetch_hot(node_ctrl, &target)) { + ctrl->prefetch_enough++; + } + + return target; +} + +static void flow_cal_cold_can_move(struct flow_ctrl *ctrl) +{ + int i; + + for (i = 0; i < ctrl->pair_num; i++) { + node_update_hot_prefetch(&ctrl->node_ctrl[i]); + if (!node_cal_cold_can_move(&ctrl->node_ctrl[i])) { + ctrl->cold_enough++; + } + } +} + +static inline bool is_cold_enough(struct flow_ctrl *ctrl) +{ + return ctrl->cold_enough == ctrl->pair_num; +} + +static long long ctrl_cold_move(struct flow_ctrl *ctrl, long long target, int node) +{ + struct node_ctrl *node_ctrl = &ctrl->node_ctrl[node]; + + if (!node_can_move_cold(node_ctrl)) { + return 0; + } + + if (!node_move_cold(node_ctrl, &target)) { + ctrl->cold_enough++; + } + + return target; +} + +static void destroy_flow_ctrl(struct flow_ctrl *ctrl) +{ + free(ctrl->node_ctrl); + ctrl->node_ctrl = NULL; +} + +static void do_filter(struct page_filter *filter, struct cslide_eng_params *eng_params) +{ + struct cslide_pid_params *params = NULL; + struct count_page_refs *cpf = NULL; + struct memory_grade *memory_grade = NULL; + struct node_pair *pair = NULL; + int pair_num = eng_params->node_map.cur_num; + int i, j; + + filter->flow_cal_func(filter->ctrl); + if (filter->flow_enough(filter->ctrl)) { + return; + } + + for (i = filter->count_start; i != filter->count_end; i += filter->count_step) { + factory_foreach_working_pid_params(params, &eng_params->factory) { + cpf = ¶ms->count_page_refs[i]; + for (j = 0; j < pair_num; j++) { + pair = &eng_params->node_map.pair[j]; + memory_grade = ¶ms->memory_grade[j]; + filter->filter_policy(filter, pair, cpf, memory_grade); + if (filter->flow_enough(filter->ctrl)) { + return; + } + } + } + } +} + +static void to_hot_policy(struct page_filter *filter, struct node_pair *pair, struct count_page_refs *cpf, struct memory_grade *memory_grade) +{ + long long can_move; + struct node_page_refs *npf = &cpf->node_pfs[pair->cold_node]; + + can_move = filter->flow_move_func(filter->ctrl, npf->size, pair->index); + move_npf_to_list(npf, &memory_grade->hot_pages, can_move); +} + +static void to_cold_policy(struct page_filter *filter, struct node_pair *pair, struct count_page_refs *cpf, struct memory_grade *memory_grade) +{ + long long can_move; + struct node_page_refs *npf = &cpf->node_pfs[pair->hot_node]; + + can_move = filter->flow_move_func(filter->ctrl, npf->size, pair->index); + move_npf_to_list(npf, &memory_grade->cold_pages, can_move); +} + +static void move_hot_pages(struct cslide_eng_params *eng_params, struct flow_ctrl *ctrl) +{ + struct page_filter filter; + filter.flow_cal_func = flow_cal_hot_can_move; + filter.flow_move_func = ctrl_hot_move; + filter.flow_enough = is_hot_enough; + filter.filter_policy = to_hot_policy; + filter.ctrl = ctrl; + filter.count_start = eng_params->loop; + filter.count_end = eng_params->hot_threshold - 1; + filter.count_step = -1; + do_filter(&filter, eng_params); +} + +static void prefetch_hot_pages(struct cslide_eng_params *eng_params, struct flow_ctrl *ctrl) +{ + struct page_filter filter; + filter.flow_cal_func = flow_cal_hot_can_prefetch; + filter.flow_move_func = ctrl_prefetch_hot; + filter.flow_enough = is_prefetch_enough; + filter.filter_policy = to_hot_policy; + filter.ctrl = ctrl; + filter.count_start = eng_params->hot_threshold - 1; + filter.count_end = -1; + filter.count_step = -1; + do_filter(&filter, eng_params); +} + +static void move_cold_pages(struct cslide_eng_params *eng_params, struct flow_ctrl *ctrl) +{ + struct page_filter filter; + filter.flow_cal_func = flow_cal_cold_can_move; + filter.flow_move_func = ctrl_cold_move; + filter.flow_enough = is_cold_enough; + filter.filter_policy = to_cold_policy; + filter.ctrl = ctrl; + filter.count_start = 0; + filter.count_end = eng_params->hot_threshold; + filter.count_step = 1; + do_filter(&filter, eng_params); +} + +static void cslide_filter_pfs(struct cslide_eng_params *eng_params) +{ + struct flow_ctrl ctrl; + long long quota = (long long)eng_params->mig_quota * HUGE_1M_SIZE; + long long reserve = (long long)eng_params->hot_reserve * HUGE_1M_SIZE; + + if (init_flow_ctrl(&ctrl, &eng_params->mem, &eng_params->node_map, quota, reserve) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init_flow_ctrl fail\n"); + return; + } + + move_hot_pages(eng_params, &ctrl); + prefetch_hot_pages(eng_params, &ctrl); + move_cold_pages(eng_params, &ctrl); + + destroy_flow_ctrl(&ctrl); +} + +static int cslide_policy(struct cslide_eng_params *eng_params) +{ + struct cslide_pid_params *pid_params = NULL; + int ret; + + factory_foreach_working_pid_params(pid_params, &eng_params->factory) { + ret = cslide_count_node_pfs(pid_params); + if (ret != 0) { + etmemd_log(ETMEMD_LOG_ERR, "count node page refs fail\n"); + return ret; + } + } + + cslide_filter_pfs(eng_params); + return 0; +} + +static void sort_add_vma_pf(struct vma_pf *vma_pf) +{ + struct vma_pf **iter = &g_share_vma_head; + + for (; *iter != NULL && (*iter)->vma->inode < vma_pf->vma->inode; iter = &((*iter)->next)) { + ; + } + + vma_pf->next = *iter; + *iter = vma_pf; +} + +static bool is_share(struct vma_pf *vma_pf) +{ + struct vma *vma = vma_pf->vma; + + if (vma->inode != 0 && vma->stat[VMA_STAT_MAY_SHARE]) { + return true; + } + + return false; +} + +static inline uint64_t to_offset(struct page_offset *po) +{ + return po->page_refs->addr + po->to_offset; +} + +static int page_offset_cmp(const void *a, const void *b) +{ + struct page_offset *l = (struct page_offset *)a; + struct page_offset *r = (struct page_offset *)b; + + return to_offset(l) - to_offset(r); +} + +static inline void init_merge_po(struct page_offset *to_merge_po, int count) +{ + qsort(to_merge_po, count, sizeof(struct page_offset), page_offset_cmp); +} + +static void next_po(struct page_offset *to_merge_po, int *count) +{ + struct page_offset *po = to_merge_po; + struct page_offset tmp; + uint64_t offset; + int i; + + po->page_refs = po->page_refs->next; + if (po->page_refs == NULL) { + for (i = 1; i < *count; i++) { + to_merge_po[i - 1] = to_merge_po[i]; + } + (*count)--; + return; + } + + tmp = *po; + offset = to_offset(po); + for (i = 1; i < *count; i++) { + if (to_offset(&to_merge_po[i]) >= offset) { + break; + } + to_merge_po[i - 1] = to_merge_po[i]; + } + to_merge_po[i - 1] = tmp; +} + +static void merge_share_pfs(struct page_refs **share_pfs, int share_pfs_num) +{ + int max_count = -1; + struct page_refs *max_pf = NULL; + int i; + + /* only keep one page_refs with max count */ + for (i = 0; i < share_pfs_num; i++) { + if (share_pfs[i]->count > max_count) { + max_pf = share_pfs[i]; + max_count = share_pfs[i]->count; + } + share_pfs[i]->count = -1; + } + max_pf->count = max_count; +} + +static int do_merge_vma_pf(struct vma_pf *vma_pf, int count) +{ + struct page_refs **share_pfs = NULL; + struct page_offset *to_merge_po = NULL; + struct page_offset *iter = NULL; + int share_pfs_num; + uint64_t cur_offset = 0; + uint64_t next_offset; + int i; + + share_pfs = calloc(count, sizeof(struct page_refs *)); + if (share_pfs == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc share_pfs fail\n"); + return -1; + } + + to_merge_po = calloc(count, sizeof(struct page_offset)); + if (to_merge_po == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc iter_pfs fail\n"); + free(share_pfs); + return -1; + } + + for (i = 0; i < count; i++) { + to_merge_po[i].page_refs = vma_pf->page_refs; + to_merge_po[i].to_offset = vma_pf->vma->offset - vma_pf->vma->start; + vma_pf = vma_pf->next; + } + + init_merge_po(to_merge_po, count); + iter = to_merge_po; + share_pfs[0] = iter->page_refs; + share_pfs_num = 1; + cur_offset = to_offset(iter); + + for (next_po(to_merge_po, &count); count > 0; next_po(to_merge_po, &count)) { + iter = to_merge_po; + next_offset = to_offset(iter); + if (next_offset == cur_offset) { + share_pfs[share_pfs_num] = iter->page_refs; + share_pfs_num++; + } else { + if (share_pfs_num > 1) { + merge_share_pfs(share_pfs, share_pfs_num); + } + share_pfs[0] = iter->page_refs; + share_pfs_num = 1; + cur_offset = next_offset; + } + + } + if (share_pfs_num > 1) { + merge_share_pfs(share_pfs, share_pfs_num); + } + + free(to_merge_po); + free(share_pfs); + return 0; +} + +static int cslide_merge_share_vmas(struct cslide_eng_params *eng_params) +{ + struct cslide_pid_params *pid_params = NULL; + struct vma_pf *vma_pf = NULL; + struct vma_pf *iter = NULL; + int count; + uint64_t i; + + factory_foreach_working_pid_params(pid_params, &eng_params->factory) { + vma_pf = pid_params->vma_pf; + if (vma_pf == NULL) { + continue; + } + for (i = 0; i < pid_params->vmas->vma_cnt; i++) { + if (is_share(&vma_pf[i])) { + sort_add_vma_pf(&vma_pf[i]); + } + } + } + + vma_pf = g_share_vma_head; + while (vma_pf != NULL) { + for (iter = vma_pf->next, count = 1; iter != NULL && iter->vma->inode == vma_pf->vma->inode; iter = iter->next, count++) { + ; + } + if (count > 1) { + if (do_merge_vma_pf(vma_pf, count) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "merge vma with inode %lld fail\n", vma_pf->vma->inode); + g_share_vma_head = NULL; + return -1; + } + } + vma_pf = iter; + } + g_share_vma_head = NULL; + return 0; +} + +static int cslide_get_vmas(struct cslide_pid_params *pid_params) +{ + struct cslide_task_params *task_params = pid_params->task_params; + struct vma *vma = NULL; + char pid[PID_STR_MAX_LEN] = {0}; + uint64_t i; + + if (snprintf_s(pid, PID_STR_MAX_LEN, PID_STR_MAX_LEN - 1, "%u", pid_params->pid) <= 0) { + etmemd_log(ETMEMD_LOG_ERR, "sprintf pid %u fail\n", pid_params->pid); + return -1; + } + pid_params->vmas = get_vmas_with_flags(pid, task_params->vmflags_array, task_params->vmflags_num, task_params->anon_only); + if (pid_params->vmas == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "get vmas for %s fail\n", pid); + return -1; + } + + pid_params->vma_pf = calloc(pid_params->vmas->vma_cnt, sizeof(struct vma_pf)); + if (pid_params->vma_pf == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memory for vma_pf fail\n"); + goto free_vmas; + } + + vma = pid_params->vmas->vma_list; + for (i = 0; i < pid_params->vmas->vma_cnt; i++) { + pid_params->vma_pf[i].vma = vma; + vma = vma->next; + } + return 0; + +free_vmas: + free_vmas(pid_params->vmas); + pid_params->vmas = NULL; + return -1; +} + +static void cslide_free_vmas(struct cslide_pid_params *params) +{ + uint64_t i; + + if (params->vmas == NULL) { + return; + } + + for (i = 0; i < params->vmas->vma_cnt; i++) { + clean_page_refs_unexpected(¶ms->vma_pf[i].page_refs); + } + free(params->vma_pf); + params->vma_pf = NULL; + free_vmas(params->vmas); + params->vmas = NULL; +} + +static int cslide_scan_vmas(struct cslide_pid_params *params) +{ + char pid[PID_STR_MAX_LEN] = {0}; + struct vmas *vmas = params->vmas; + struct vma *vma = NULL; + struct vma_pf *vma_pf = NULL; + FILE *scan_fp = NULL; + struct walk_address walk_address; + uint64_t i; + int fd; + + if (snprintf_s(pid, PID_STR_MAX_LEN, PID_STR_MAX_LEN - 1, "%u", params->pid) <= 0) { + etmemd_log(ETMEMD_LOG_ERR, "snpintf pid %u fail\n", params->pid); + return -1; + } + + scan_fp = etmemd_get_proc_file(pid, IDLE_SCAN_FILE, SCAN_AS_HUGE, "r"); + if (scan_fp == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "open %s file for pid %u fail\n", IDLE_SCAN_FILE, params->pid); + params->vma_pf = NULL; + return -1; + } + + fd = fileno(scan_fp); + if (fd == -1) { + fclose(scan_fp); + etmemd_log(ETMEMD_LOG_ERR, "fileno file fail for %s\n", IDLE_SCAN_FILE); + return -1; + } + for (i = 0; i < vmas->vma_cnt; i++) { + vma_pf = ¶ms->vma_pf[i]; + vma = vma_pf->vma; + walk_address.walk_start = vma->start; + walk_address.walk_end = vma->end; + if (walk_vmas(fd, &walk_address, &vma_pf->page_refs, NULL) == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "scan vma start %llu end %llu fail\n", vma->start, vma->end); + cslide_free_vmas(params); + fclose(scan_fp); + return -1; + } + } + + fclose(scan_fp); + return 0; +} + +static int cslide_do_scan(struct cslide_eng_params *eng_params) +{ + struct cslide_pid_params *iter = NULL; + int i; + + factory_foreach_working_pid_params(iter, &eng_params->factory) { + if (cslide_get_vmas(iter) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide get vmas fail\n"); + continue; + } + } + + for (i = 0; i < eng_params->loop; i++) { + factory_foreach_working_pid_params(iter, &eng_params->factory) { + if (iter->vmas == NULL) { + continue; + } + if (cslide_scan_vmas(iter) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide scan vmas fail\n"); + continue; + } + } + sleep(eng_params->sleep); + } + + if (cslide_merge_share_vmas(eng_params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide merge share vams fail\n"); + return -1; + } + + return 0; +} + +static int do_migrate_pages(unsigned int pid, struct page_refs *page_refs, int node) +{ + int batch_size = BATCHSIZE; + int ret = -1; + void **pages = NULL; + int *nodes = NULL; + int *status = NULL; + int actual_num = 0; + + if (page_refs == NULL) { + return 0; + } + + nodes = malloc(sizeof(int) * batch_size); + if (nodes == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "malloc nodes fail\n"); + return -1; + } + + status = malloc(sizeof(int) * batch_size); + if (status == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "malloc status fail\n"); + goto free_nodes; + } + + pages = malloc(sizeof(void *) * batch_size); + if (pages == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "malloc pages fail\n"); + goto free_status; + } + while (page_refs != NULL) { + pages[actual_num] = (void *)page_refs->addr; + nodes[actual_num] = node; + actual_num++; + page_refs = page_refs->next; + if (actual_num == batch_size || page_refs == NULL) { + ret = move_pages(pid, actual_num, pages, nodes, status, MPOL_MF_MOVE_ALL); + actual_num = 0; + if (ret != 0) { + etmemd_log(ETMEMD_LOG_ERR, "task %d move_pages fail with %d errno %d\n", pid, ret, errno); + continue; + } + } + } + + free(pages); + pages = NULL; +free_status: + free(status); + status = NULL; +free_nodes: + free(nodes); + nodes = NULL; + return ret; +} + +static int migrate_single_task(unsigned int pid, const struct memory_grade *memory_grade, int hot_node, int cold_node) +{ + int ret = -1; + + if (do_migrate_pages(pid, memory_grade->cold_pages, cold_node) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "migrate cold pages fail\n"); + return ret; + } + + if (do_migrate_pages(pid, memory_grade->hot_pages, hot_node) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "migrate hot pages fail\n"); + return ret; + } + + ret = 0; + return ret; +} + +static int cslide_do_migrate(struct cslide_eng_params *eng_params) +{ + struct cslide_pid_params *iter = NULL; + struct node_pair *pair = NULL; + int bind_node, i; + + factory_foreach_working_pid_params(iter, &eng_params->factory) { + for (i = 0; i < eng_params->node_map.cur_num; i++) { + pair = &eng_params->node_map.pair[i]; + bind_node = pair->hot_node < pair->cold_node ? pair->hot_node : pair->cold_node; + if (numa_run_on_node(bind_node) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "fail to run on node %d to migrate memory\n", bind_node); + } + migrate_single_task(iter->pid, &iter->memory_grade[i], pair->hot_node, pair->cold_node); + } + } + if (numa_run_on_node(-1) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "fail to run on all node after migrate memory\n"); + } + return 0; +} + +static bool is_node_empty(struct node_mem *mem) +{ + return mem->huge_free == mem->huge_total; +} + +static bool is_all_cold_node_empty(struct cslide_eng_params *eng_params) +{ + struct sys_mem *mem = &eng_params->mem; + struct node_pair *pair = NULL; + int i; + + for (i = 0; i < eng_params->node_map.cur_num; i++) { + pair = &eng_params->node_map.pair[i]; + if (!is_node_empty(&mem->node_mem[pair->cold_node])) { + return false; + } + } + return true; +} + +static inline bool is_busy(int node_watermark, long long total, long long free) +{ + return free * TO_PCT / total < node_watermark; +} + +static bool is_node_busy(int node_watermark, struct node_mem *mem) +{ + return is_busy(node_watermark, mem->huge_total, mem->huge_free); +} + +static bool is_any_hot_node_busy(struct cslide_eng_params *eng_params) +{ + struct sys_mem *mem = &eng_params->mem; + struct node_pair *pair = NULL; + int node_watermark = eng_params->node_watermark; + int i; + + for (i = 0; i < eng_params->node_map.cur_num; i++) { + pair = &eng_params->node_map.pair[i]; + if (is_node_busy(node_watermark, &mem->node_mem[pair->hot_node])) { + return true; + } + } + + return false; +} + +static bool need_migrate(struct cslide_eng_params *eng_params) +{ + if (!is_all_cold_node_empty(eng_params)) { + return true; + } + if (is_any_hot_node_busy(eng_params)) { + return true; + } + + return false; +} + +static void get_node_pages_info(struct cslide_pid_params *pid_params) +{ + struct cslide_eng_params *eng_params = pid_params->eng_params; + int n, c; + int t = eng_params->hot_threshold; + int count = pid_params->count; + int actual_t = t > count ? count + 1 : t; + int node_num = pid_params->count_page_refs->node_num; + struct node_pages_info *info = pid_params->node_pages_info; + + for (n = 0; n < node_num; n++) { + info[n].cold = 0; + info[n].hot = 0; + + for (c = 0; c < actual_t; c++) { + info[n].cold += pid_params->count_page_refs[c].node_pfs[n].num * 2 * 1024; + } + for (; c <= count; c++) { + info[n].hot += pid_params->count_page_refs[c].node_pfs[n].num * 2 * 1024; + } + } +} + +static void cslide_stat(struct cslide_eng_params *eng_params) +{ + struct cslide_pid_params *iter = NULL; + + pthread_mutex_lock(&eng_params->stat_mtx); + factory_foreach_working_pid_params(iter, &eng_params->factory) { + get_node_pages_info(iter); + } + eng_params->stat_time = time(NULL); + pthread_mutex_unlock(&eng_params->stat_mtx); +} + +static void cslide_clean_params(struct cslide_eng_params *eng_params) +{ + struct cslide_pid_params *iter = NULL; + + factory_foreach_pid_params(iter, &eng_params->factory) { + clean_pid_param(iter); + cslide_free_vmas(iter); + } +} + +static void destroy_cslide_eng_params(struct cslide_eng_params *params) +{ + destroy_factory(¶ms->factory); + pthread_mutex_destroy(¶ms->stat_mtx); + destroy_node_map(¶ms->node_map); + destroy_sys_mem(¶ms->mem); +} + +static int init_cslide_eng_params(struct cslide_eng_params *params) +{ + if (init_sys_mem(¶ms->mem) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init system memory fail\n"); + return -1; + } + + if (init_node_map(¶ms->node_map, params->mem.node_num) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init_node_map fail\n"); + goto destroy_sys_mem; + } + + if (pthread_mutex_init(¶ms->stat_mtx, NULL) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init stat mutex fail\n"); + goto destroy_node_map; + } + + if (init_factory(¶ms->factory) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init params factory fail\n"); + goto destroy_stat_mtx; + } + + return 0; + +destroy_stat_mtx: + pthread_mutex_destroy(¶ms->stat_mtx); + +destroy_node_map: + destroy_node_map(¶ms->node_map); + +destroy_sys_mem: + destroy_sys_mem(¶ms->mem); + return -1; +} + +static void *cslide_main(void *arg) +{ + struct cslide_eng_params *eng_params = (struct cslide_eng_params *)arg; + struct sys_mem *mem = NULL; + + while (true) { + factory_update_pid_params(&eng_params->factory); + if (eng_params->finish) { + etmemd_log(ETMEMD_LOG_DEBUG, "cslide task is stopping...\n"); + break; + } + if (factory_working_empty(&eng_params->factory)) { + goto next; + } + + mem = &eng_params->mem; + if (get_sys_mem(mem) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "get system meminfo fail\n"); + goto next; + } + + if (cslide_do_scan(eng_params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide_do_scan fail\n"); + goto next; + } + + if (cslide_policy(eng_params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide_policy fail\n"); + goto next; + } + + if (!need_migrate(eng_params)) { + etmemd_log(ETMEMD_LOG_DEBUG, "no need to migrate\n"); + goto next; + } + + if (cslide_do_migrate(eng_params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide_do_migrate fail\n"); + goto next; + } + +next: + cslide_stat(eng_params); + sleep(eng_params->interval); + cslide_clean_params(eng_params); + } + + factory_update_pid_params(&eng_params->factory); + destroy_cslide_eng_params(eng_params); + free(eng_params); + return NULL; +} + +static int cslide_alloc_pid_params(struct engine *eng, struct task_pid **tk_pid) +{ + struct cslide_eng_params *eng_params = (struct cslide_eng_params *)eng->params; + unsigned pid = (*tk_pid)->pid; + struct cslide_pid_params *pid_params = NULL; + + pid_params = alloc_pid_params(eng_params); + if (pid_params == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc cslide pid params fail\n"); + return -1; + } + + pid_params->pid = pid; + pid_params->eng_params = eng_params; + pid_params->task_params = (*tk_pid)->tk->params; + (*tk_pid)->params = pid_params; + return 0; +} + +static void cslide_free_pid_params(struct engine *eng, struct task_pid **tk_pid) +{ + struct cslide_eng_params *eng_params = (struct cslide_eng_params *)eng->params; + + if ((*tk_pid)->params != NULL) { + /* clear pid params in factory_update_pid_params in cslide_main */ + factory_free_pid_params(&eng_params->factory, (*tk_pid)->params); + (*tk_pid)->params = NULL; + } +} + +static int cslide_start_task(struct engine *eng, struct task *tk) +{ + struct cslide_eng_params *eng_params = (struct cslide_eng_params *)eng->params; + struct task_pid *task_pid = NULL; + + for (task_pid = tk->pids; task_pid != NULL; task_pid = task_pid->next) { + factory_add_pid_params(&eng_params->factory, task_pid->params); + } + + return 0; +} + +static void cslide_stop_task(struct engine *eng, struct task *tk) +{ + struct cslide_eng_params *eng_params = (struct cslide_eng_params *)eng->params; + struct task_pid *task_pid = NULL; + + for (task_pid = tk->pids; task_pid != NULL; task_pid = task_pid->next) { + factory_remove_pid_params(&eng_params->factory, task_pid->params); + } +} + +static char *get_time_stamp(time_t *t) +{ + char *ts = asctime(localtime(t)); + size_t len = strlen(ts); + + if (ts[len - 1] == '\n') { + ts[len - 1] = '\0'; + } + return ts; +} + +static int show_task_pages(void *params, int fd) +{ + struct cslide_pid_params *pid_params = (struct cslide_pid_params *)params; + struct cslide_eng_params *eng_params = pid_params->eng_params; + int node_num = pid_params->count_page_refs->node_num; + struct node_pages_info *info = pid_params->node_pages_info; + char *time_str = NULL; + int n; + + time_str = get_time_stamp(&eng_params->stat_time); + if (time_str != NULL) { + dprintf_all(fd, "[%s] ", time_str); + } + dprintf_all(fd, "task %d pages info (KB):\n", pid_params->pid); + dprintf_all(fd, "%5s %10s %10s %10s\n", "node", "used", "hot", "cold"); + for (n = 0; n < node_num; n++) { + dprintf_all(fd, "%5d %10d %10d %10d\n", n, + info[n].hot + info[n].cold, info[n].hot, info[n].cold); + } + return 0; +} + +static struct cslide_cmd_item g_task_cmd_items[] = { + {"showtaskpages", show_task_pages}, +}; + +static int show_host_pages(void *params, int fd) +{ + struct cslide_eng_params *eng_params = (struct cslide_eng_params *)params; + struct cslide_pid_params *iter = NULL; + char *time_str = NULL; + int node_num = eng_params->mem.node_num; + int n; + uint32_t total; + struct node_pages_info *info = calloc(node_num, sizeof(struct node_pages_info)); + + if (info == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memory for node_page_info fail\n"); + return -1; + } + + factory_foreach_working_pid_params(iter, &eng_params->factory) { + for (n = 0; n < node_num; n++) { + info[n].hot += iter->node_pages_info[n].hot; + info[n].cold += iter->node_pages_info[n].cold; + } + } + + time_str = get_time_stamp(&eng_params->stat_time); + if (time_str != NULL) { + dprintf_all(fd, "[%s] ", time_str); + } + dprintf_all(fd, "host pages info (KB):\n"); + dprintf_all(fd, "%5s %10s %10s %10s %10s\n", "node", "total", "used", "hot", "cold"); + for (n = 0; n < node_num; n++) { + total = eng_params->mem.node_mem[n].huge_total / 1024; + dprintf_all(fd, "%5d %10d %10d %10d %10d\n", + n, total, info[n].hot + info[n].cold, info[n].hot, info[n].cold); + } + + free(info); + return 0; +} + +struct cslide_cmd_item g_host_cmd_items[] = { + {"showhostpages", show_host_pages}, +}; + +static struct cslide_cmd_item *find_cmd_item(struct cslide_cmd_item *items, unsigned n, char *cmd) +{ + unsigned i; + + for (i = 0; i < n; i++) { + if (strcmp(cmd, items[i].name) == 0) { + return &items[i]; + } + } + + return NULL; +} + +static int cslide_engine_do_cmd(struct engine *eng, struct task *tk, char *cmd, int fd) +{ + struct cslide_eng_params *eng_params = (struct cslide_eng_params *)eng->params; + struct cslide_pid_params *pid_params = NULL; + struct cslide_cmd_item *item = NULL; + int ret; + + if (factory_working_empty(&eng_params->factory)) { + etmemd_log(ETMEMD_LOG_ERR, "no working pid under this cslide engine\n"); + return -1; + } + + item = find_cmd_item(g_host_cmd_items, ARRAY_SIZE(g_host_cmd_items), cmd); + if (item != NULL) { + pthread_mutex_lock(&eng_params->stat_mtx); + ret = item->func(eng_params, fd); + pthread_mutex_unlock(&eng_params->stat_mtx); + return ret; + } + + item = find_cmd_item(g_task_cmd_items, ARRAY_SIZE(g_task_cmd_items), cmd); + if (item == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "cslide cmd %s is not supportted\n", cmd); + return -1; + } + if (tk == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "task for cslide cmd %s not found\n", cmd); + return -1; + } + + if (tk->pids == NULL || tk->pids->params == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "task %s for cslide cmd %s not started\n", tk->name, cmd); + return -1; + } + + pid_params = (struct cslide_pid_params *)tk->pids->params; + pthread_mutex_lock(&eng_params->stat_mtx); + ret = item->func(pid_params, fd); + pthread_mutex_unlock(&eng_params->stat_mtx); + return ret; +} + +static int fill_task_anon_only(void *obj, void *val) +{ + int ret = 0; + struct cslide_task_params *params = (struct cslide_task_params *)obj; + char *anon_only = (char *)val; + + if (strcmp(anon_only, "yes") == 0) { + params->anon_only = true; + } else if (strcmp(anon_only, "no") == 0) { + params->anon_only = false; + } else { + etmemd_log(ETMEMD_LOG_ERR, "only_anon : not support %s\n", anon_only); + etmemd_log(ETMEMD_LOG_ERR, "only_anon : only support yes/no\n"); + ret = -1; + } + free(val); + return ret; +} + +static int fill_task_vm_flags(void *obj, void *val) +{ + struct cslide_task_params *params = (struct cslide_task_params *)obj; + char *vm_flags = (char *)val; + + params->vmflags_num = split_vmflags(¶ms->vmflags_array, vm_flags); + if (params->vmflags_num <= 0) { + free(val); + etmemd_log(ETMEMD_LOG_ERR, "fill vm flags fail\n"); + return -1; + } + + params->vmflags_str = vm_flags; + if (params->vmflags_num != 1 || strcmp(params->vmflags_array[0], "ht") != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide only work with ht set\n"); + return -1; + } + return 0; +} + +static struct config_item g_cslide_task_config_items[] = { + {"vm_flags", STR_VAL, fill_task_vm_flags, false}, + {"anon_only", STR_VAL, fill_task_anon_only, false}, +}; + +static int cslide_fill_task(GKeyFile *config, struct task *tk) +{ + struct cslide_task_params *params = calloc(1, sizeof(struct cslide_task_params)); + + if (params == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc cslide task params fail\n"); + return -1; + } + + if (parse_file_config(config, TASK_GROUP, g_cslide_task_config_items, + ARRAY_SIZE(g_cslide_task_config_items), (void *)params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide fill task params fail\n"); + goto exit; + } + + tk->params = params; + if (etmemd_get_task_pids(tk) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide fail to get task pids\n"); + tk->params = NULL; + goto exit; + } + return 0; + +exit: + clear_task_params(params); + free(params); + return -1; +} + +static void cslide_clear_task(struct task *tk) +{ + /* clear cslide task params when clear connected cslide pid params */ + etmemd_free_task_pids(tk); + tk->params = NULL; +} + +static int fill_node_pair(void *obj, void *val) +{ + struct cslide_eng_params *params = (struct cslide_eng_params *)obj; + char *node_pair_str = (char *)val; + char *pair = NULL; + char *saveptr_pair = NULL; + char *hot_node_str = NULL; + char *cold_node_str = NULL; + char *saveptr_node = NULL; + int hot_node, cold_node; + struct node_map *map = ¶ms->node_map; + struct node_verifier nv; + char *pair_delim = " ;"; + char *node_delim = " ,"; + int ret = -1; + + if (init_node_verifier(&nv, params->mem.node_num) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init_node_verifier fail\n"); + free(val); + return ret; + } + + for (pair = strtok_r(node_pair_str, pair_delim, &saveptr_pair); pair != NULL; pair = strtok_r(NULL, pair_delim, &saveptr_pair)) { + hot_node_str = strtok_r(pair, node_delim, &saveptr_node); + if (hot_node_str == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "parse hot node failed\n"); + goto err; + } + + cold_node_str = strtok_r(NULL, node_delim, &saveptr_node); + if (cold_node_str == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "parse cold node failed\n"); + goto err; + } + + if (get_int_value(hot_node_str, &hot_node) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "transfer hot node %s to integer fail\n", hot_node_str); + goto err; + } + + if (get_int_value(cold_node_str, &cold_node) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "transfer cold node %s to integer fail\n", cold_node_str); + goto err; + } + + if (!is_node_valid(&nv, hot_node) || !is_node_valid(&nv, cold_node)) { + etmemd_log(ETMEMD_LOG_ERR, "node %d(hot)->%d(cold) invalid\n", hot_node, cold_node); + goto err; + } + + if (add_node_pair(map, cold_node, hot_node) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "add %d(hot)->%d(cold) fail\n", hot_node, cold_node); + goto err; + } + } + + if (has_node_unmap(&nv)) { + etmemd_log(ETMEMD_LOG_ERR, "there is node unmap\n"); + goto err; + } + ret = 0; + +err: + free(val); + destroy_node_verifier(&nv); + return ret; +} + +static int fill_migrate_watermark(void *obj, void *val) +{ + struct cslide_eng_params *params = (struct cslide_eng_params *)obj; + int wm = parse_to_int(val); + + if (wm < MIN_WM || wm > MAX_WM) { + etmemd_log(ETMEMD_LOG_ERR, "migrate watermark %d invalid\n", wm); + return -1; + } + params->node_watermark = wm; + return 0; +} + +static int fill_hot_threshold(void *obj, void *val) +{ + struct cslide_eng_params *params = (struct cslide_eng_params *)obj; + int t = parse_to_int(val); + + if (t < 0) { + etmemd_log(ETMEMD_LOG_ERR, "config hot threshold %d not valid\n", t); + return -1; + } + + params->hot_threshold = t; + return 0; +} + +static int fill_hot_reserve(void *obj, void *val) +{ + struct cslide_eng_params *params = (struct cslide_eng_params *)obj; + int hot_reserve = parse_to_int(val); + + params->hot_reserve = hot_reserve; + return 0; +} + +static int fill_mig_quota(void *obj, void *val) +{ + struct cslide_eng_params *params = (struct cslide_eng_params *)obj; + int mig_quota = parse_to_int(val); + + params->mig_quota = mig_quota; + return 0; +} + +static struct config_item cslide_eng_config_items[] = { + {"node_pair", STR_VAL, fill_node_pair, false}, + {"node_watermark", INT_VAL, fill_migrate_watermark, false}, + {"hot_threshold", INT_VAL, fill_hot_threshold, false}, + {"node_mig_quota", INT_VAL, fill_mig_quota, false}, + {"node_hot_reserve", INT_VAL, fill_hot_reserve, false}, +}; + +static int cslide_fill_eng(GKeyFile *config, struct engine *eng) +{ + struct cslide_eng_params *params = calloc(1, sizeof(struct cslide_eng_params)); + + if (params == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc cslide engine params fail\n"); + return -1; + } + + if (init_cslide_eng_params(params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "init cslide engine params fail\n"); + return -1; + } + + params->loop = eng->proj->loop; + params->interval = eng->proj->interval; + params->sleep = eng->proj->sleep; + if (parse_file_config(config, ENG_GROUP, cslide_eng_config_items, + ARRAY_SIZE(cslide_eng_config_items), (void *)params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "cslide fill engine params fail\n"); + goto destroy_eng_params; + } + + eng->params = params; + if (pthread_create(¶ms->worker, NULL, cslide_main, params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "start cslide main worker fail\n"); + goto destroy_eng_params; + } + + return 0; + +destroy_eng_params: + destroy_cslide_eng_params(params); + return -1; +} + +static void cslide_clear_eng(struct engine *eng) +{ + struct cslide_eng_params *eng_params = eng->params; + /* clear cslide engine params in cslide_main */ + eng_params->finish = true; + eng->params = NULL; +} + +struct engine_ops g_cslide_eng_ops = { + .fill_eng_params = cslide_fill_eng, + .clear_eng_params = cslide_clear_eng, + .fill_task_params = cslide_fill_task, + .clear_task_params = cslide_clear_task, + .alloc_pid_params = cslide_alloc_pid_params, + .free_pid_params = cslide_free_pid_params, + .start_task = cslide_start_task, + .stop_task = cslide_stop_task, + .eng_mgt_func = cslide_engine_do_cmd, +}; + +int fill_engine_type_cslide(struct engine *eng) +{ + eng->ops = &g_cslide_eng_ops; + eng->engine_type = CSLIDE_ENGINE; + eng->name = "cslide"; + return 0; +} diff --git a/src/etmemd_src/etmemd_engine.c b/src/etmemd_src/etmemd_engine.c index 41d8fa5..98a7430 100644 --- a/src/etmemd_src/etmemd_engine.c +++ b/src/etmemd_src/etmemd_engine.c @@ -17,38 +17,82 @@ #include #include "etmemd_engine.h" #include "etmemd_slide.h" +#include "etmemd_cslide.h" #include "etmemd_log.h" +#include "etmemd_common.h" +#include "etmemd_file.h" -const char *etmemd_get_eng_name(enum eng_type type) +struct engine_item { + char *name; + int (*fill_eng_func)(struct engine *eng); +}; + +static struct engine_item g_engine_items[] = { + {"slide", fill_engine_type_slide}, + {"cslide", fill_engine_type_cslide}, +}; + +static struct engine_item *find_engine_item(const char *name) { - if (type == SLIDE_ENGINE) { - return "slide"; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(g_engine_items); i++) { + if (strcmp(name, g_engine_items[i].name) == 0) { + return &g_engine_items[i]; + } } - return ""; + return NULL; } -struct engine_item g_eng_items[ENGINE_TYPE_CNT] = { - {SLIDE_ENGINE, fill_engine_type_slide}, -}; - -int fill_engine_type(struct engine *eng, const char *val) +struct engine *etmemd_engine_add(GKeyFile *config) { - int ret = -1; - int i; + struct engine *eng = NULL; + struct engine_item *item = NULL; + char *name = NULL; - for (i = 0; i < ENGINE_TYPE_CNT; i++) { - if (strcmp(val, etmemd_get_eng_name(g_eng_items[i].eng_type)) == 0) { - ret = g_eng_items[i].fill_eng_func(eng); - break; - } + if (g_key_file_has_key(config, ENG_GROUP, "name", NULL) == FALSE) { + etmemd_log(ETMEMD_LOG_ERR, "engine name is not set\n"); + return NULL; } - if (ret != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid engine type %s\n", val); - return -1; + name = g_key_file_get_string(config, ENG_GROUP, "name", NULL); + if (name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "get name string of engine fail\n"); + return NULL; } - return 0; -} + item = find_engine_item(name); + if (item == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "engine %s not support\n", name); + goto free_name; + } + eng = calloc(1, sizeof(struct engine)); + if (eng == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memory for engine fail\n"); + goto free_name; + } + + if (item->fill_eng_func(eng) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "fill engine %s fail\n", name); + free(eng); + eng = NULL; + goto free_name; + } + + if (eng->ops == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "engine %s without operations\n", name); + free(eng); + eng = NULL; + } + +free_name: + free(name); + return eng; +} + +void etmemd_engine_remove(struct engine *eng) +{ + free(eng); +} diff --git a/src/etmemd_src/etmemd_file.c b/src/etmemd_src/etmemd_file.c index 1115d7a..ac2654e 100644 --- a/src/etmemd_src/etmemd_file.c +++ b/src/etmemd_src/etmemd_file.c @@ -13,444 +13,52 @@ * Description: File operation API. ******************************************************************************/ -#include -#include -#include -#include - -#include "securec.h" #include "etmemd_log.h" -#include "etmemd_common.h" -#include "etmemd_project.h" -#include "etmemd_engine.h" #include "etmemd_file.h" -#define MAX_INTERVAL_VALUE 1200 -#define MAX_SLEEP_VALUE 1200 -#define MAX_LOOP_VALUE 120 - -static int fill_project_interval(struct project *proj, const char *val) +static int parse_item(GKeyFile *config, char *group_name, struct config_item *item, void *obj) { - int value; - - if (get_int_value(val, &value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid project interval value.\n"); - return -1; - } - - if (value < 1 || value > MAX_INTERVAL_VALUE) { - etmemd_log(ETMEMD_LOG_ERR, "invalid project interval value, must between 1 and 1200.\n"); - return -1; - } - - proj->interval = value; - - return 0; -} + GError *error = NULL; + void *val; -static int fill_project_loop(struct project *proj, const char *val) -{ - int value; - - if (get_int_value(val, &value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid project loop value.\n"); - return -1; - } - - if (value < 1 || value > MAX_LOOP_VALUE) { - etmemd_log(ETMEMD_LOG_ERR, "invalid project loop value, must between 1 and 120.\n"); - return -1; - } - - proj->loop = value; - - return 0; -} - -static int fill_project_sleep(struct project *proj, const char *val) -{ - int value; - - if (get_int_value(val, &value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid project sleep value.\n"); - return -1; - } - - if (value < 1 || value > MAX_SLEEP_VALUE) { - etmemd_log(ETMEMD_LOG_ERR, "invalid project sleep value, must between 1 and 1200.\n"); - return -1; - } - - proj->sleep = value; - - return 0; -} - -static struct project_item g_project_items[] = { - {"interval", fill_project_interval, false, false}, - {"loop", fill_project_loop, false, false}, - {"sleep", fill_project_sleep, false, false}, - {NULL, NULL, false, false}, -}; - -static int fill_project_params(struct project *proj, const char *key, - const char *val) -{ - int ret = -1; - int i = 0; - - while (g_project_items[i].proj_sec_name != NULL) { - if (strcmp(key, g_project_items[i].proj_sec_name) == 0) { - ret = g_project_items[i].fill_proj_func(proj, val); - break; + if (!g_key_file_has_key(config, group_name, item->key, NULL)) { + if (item->option) { + return 0; } - - i++; - } - - if (ret != 0) { - etmemd_log(ETMEMD_LOG_ERR, "parse %s project config section fail\n", key); - return -1; - } - - g_project_items[i].set = true; - return 0; -} - -static int fill_task_type(struct task *new_task, const char *val) -{ - if (strcmp(val, "pid") != 0 && strcmp(val, "name") != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid task type, must be pid or name.\n"); - return -1; - } - - if (new_task->type != NULL) { - etmemd_log(ETMEMD_LOG_WARN, "duplicate config for task type.\n"); - return 0; - } - - new_task->type = calloc(1, strlen(val) + 1); - if (new_task->type == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "malloc task type fail.\n"); - return -1; - } - - if (strncpy_s(new_task->type, strlen(val) + 1, val, strlen(val)) != EOK) { - etmemd_log(ETMEMD_LOG_ERR, "strncpy_s for task type fail.\n"); - free(new_task->type); - new_task->type = NULL; - return -1; - } - - return 0; -} - -static int fill_task_value(struct task *new_task, const char *val) -{ - if (new_task->value != NULL) { - etmemd_log(ETMEMD_LOG_WARN, "duplicate config for task value.\n"); - return 0; - } - - new_task->value = calloc(1, strlen(val) + 1); - if (new_task->value == NULL) { - etmemd_log(ETMEMD_LOG_WARN, "malloc task value fail.\n"); - return -1; - } - - if (strncpy_s(new_task->value, strlen(val) + 1, val, strlen(val)) != EOK) { - etmemd_log(ETMEMD_LOG_ERR, "strncpy_s for task value fail.\n"); - free(new_task->value); - new_task->value = NULL; - return -1; - } - - return 0; -} - -static int fill_task_engine(struct task *new_task, const char *val) -{ - if (new_task->eng != NULL) { - etmemd_log(ETMEMD_LOG_ERR, "engine is already configured\n"); - return -1; - } - - new_task->eng = (struct engine *)calloc(1, sizeof(struct engine)); - if (new_task->eng == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "malloc engine fail\n"); - return -1; - } - - new_task->eng->task = (void *)new_task; - - if (fill_engine_type(new_task->eng, val) != 0) { - free(new_task->eng); - new_task->eng = NULL; - return -1; - } - - return 0; -} - -static int fill_task_max_threads(struct task *new_task, const char *val) -{ - int value; - int core; - - if (get_int_value(val, &value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid task max_threads value.\n"); + etmemd_log(ETMEMD_LOG_ERR, "key %s not set for group %s\n", item->key, group_name); return -1; } - if (value <= 0) { - etmemd_log(ETMEMD_LOG_WARN, - "Thread count is abnormal, set the default minimum of current thread count to 1\n"); - value = 1; - } - - core = get_nprocs(); - /* - * For IO intensive businesses, - * max-threads is limited to 2N+1 of the maximum number of threads - * */ - if (value > 2 * core + 1) { - etmemd_log(ETMEMD_LOG_WARN, - "max-threads is limited to 2N+1 of the maximum number of threads\n"); - value = 2 * core + 1; - } - - new_task->max_threads = value; - - return 0; -} - -static struct task_item g_task_items[] = { - {"type", fill_task_type, false, false}, - {"value", fill_task_value, false, false}, - {"engine", fill_task_engine, false, false}, - {"max_threads", fill_task_max_threads, true, false}, - {NULL, NULL, false, false}, -}; - -static int fill_task_params(struct task *new_task, const char *key, - const char *val) -{ - int ret = -1; - int i = 0; - - while (g_task_items[i].task_sec_name != NULL) { - if (strcmp(key, g_task_items[i].task_sec_name) == 0) { - ret = g_task_items[i].fill_task_func(new_task, val); + switch (item->type) { + case INT_VAL: + val = (void *)(long long)g_key_file_get_integer(config, group_name, item->key, &error); break; - } - - i++; - } - - if (ret != 0) { - etmemd_log(ETMEMD_LOG_ERR, "parse %s task config section fail\n", key); - return -1; - } - - g_task_items[i].set = true; - return 0; -} - -static int process_engine_param_keyword(const char *get_line, struct engine *eng, - FILE *conf_file, int *is_param) -{ - if (strcmp(get_line, "param") == 0) { - if (eng == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "must configure engine type first\n"); - return -1; - } - - if (eng->parse_param_conf(eng, conf_file) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "parse engine parameters fail\n"); + case STR_VAL: + val = (void *)g_key_file_get_string(config, group_name, item->key, &error); + break; + default: + etmemd_log(ETMEMD_LOG_ERR, "config item type %d not support\n", item->type); return -1; - } - - *is_param = 1; - } - - return 0; -} - -static bool etmemd_check_task_params(void) -{ - int i = 0; - - while (g_task_items[i].task_sec_name != NULL) { - /* do not check for the parameter which is optional */ - if (g_task_items[i].optional) { - i++; - continue; - } - - /* and the other parameters must be set */ - if (!g_task_items[i].set) { - etmemd_log(ETMEMD_LOG_ERR, "%s section must be set for task parameters\n", - g_task_items[i].task_sec_name); - return false; - } - - /* reset the flag of set of the section, and no need to do this for the optional ones */ - g_task_items[i].set = false; - i++; } - return true; -} - -/* - * new_task created in this function is needed during the whole - * process life cycle of etmemd, and will be released when - * etmemd exit in function etmemd_free_task_struct - * */ -static int get_task_params(FILE *conf_file, struct project *proj) -{ - struct task *new_task = NULL; - char key[KEY_VALUE_MAX_LEN] = {}; - char value[KEY_VALUE_MAX_LEN] = {}; - char *get_line = NULL; - int is_param = 0; - - new_task = (struct task *)calloc(1, sizeof(struct task)); - if (new_task == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "malloc task fail\n"); + if (error != NULL) { + etmemd_log(ETMEMD_LOG_ERR, "get value of key %s fail\n", item->key); return -1; } - new_task->proj = proj; - new_task->next = proj->tasks; - /* set default count of the thread pool to 1 */ - new_task->max_threads = 1; - - while ((get_line = skip_blank_line(conf_file)) != NULL) { - if (strcmp(get_line, "policies") != 0) { - if (process_engine_param_keyword(get_line, new_task->eng, - conf_file, &is_param) != 0) { - goto out_err; - } - - if (is_param == 1) { - is_param = 0; - continue; - } - - if (get_keyword_and_value(get_line, key, value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "get task keyword and value fail\n"); - goto out_err; - } - - if (fill_task_params(new_task, key, value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "fill task parameter fail\n"); - goto out_err; - } - - continue; - } - - goto next_task; - } - -next_task: - if (etmemd_check_task_params()) { - proj->tasks = new_task; - } else { - goto out_err; - } - - if (get_line == NULL) { - return 0; - } - - return get_task_params(conf_file, proj); - -out_err: - etmemd_free_task_struct(&new_task); - return -1; + return item->fill(obj, val); } -static int get_project_params(FILE *conf_file, struct project *proj) +int parse_file_config(GKeyFile *config, char *group_name, struct config_item *items, unsigned n, void *obj) { - char key[KEY_VALUE_MAX_LEN] = {}; - char value[KEY_VALUE_MAX_LEN] = {}; - char *get_line = NULL; - - while ((get_line = skip_blank_line(conf_file)) != NULL) { - if (strcmp(get_line, "policies") != 0) { - if (get_keyword_and_value(get_line, key, value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "get project keyword and value fail\n"); - return -1; - } - - if (fill_project_params(proj, key, value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "fill project parameter fail\n"); - return -1; - } - - continue; - } + unsigned i; - if (get_task_params(conf_file, proj) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "get task parameters fail\n"); + for (i = 0; i < n; i++) { + if (parse_item(config, group_name, &items[i], obj) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "parse config key %s fail\n", items[i].key); return -1; } } return 0; } - -static bool etmemd_check_project_params(void) -{ - int i = 0; - - while (g_project_items[i].proj_sec_name != NULL) { - /* do not check for the parameter which is optional */ - if (g_project_items[i].optional) { - i++; - continue; - } - - /* and the other parameters must be set */ - if (!g_project_items[i].set) { - etmemd_log(ETMEMD_LOG_ERR, "%s section must be set for project parameters\n", - g_project_items[i].proj_sec_name); - return false; - } - - /* reset the flag of set of the section, and no need to do this for the optional ones */ - g_project_items[i].set = false; - i++; - } - - return true; -} - -int etmemd_fill_proj_by_conf(struct project *proj, FILE *conf_file) -{ - char *get_line = NULL; - - get_line = skip_blank_line(conf_file); - if (get_line == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "invalid config file, should not be empty\n"); - return -1; - } - - if (strcmp(get_line, "options") != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid config file, must begin with \"options\"\n"); - return -1; - } - - if (get_project_params(conf_file, proj) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "get project parameters fail\n"); - return -1; - } - - if (!etmemd_check_project_params()) { - return -1; - } - - return 0; -} diff --git a/src/etmemd_src/etmemd_migrate.c b/src/etmemd_src/etmemd_migrate.c index 3e2f8e8..a7aa9b8 100644 --- a/src/etmemd_src/etmemd_migrate.c +++ b/src/etmemd_src/etmemd_migrate.c @@ -69,7 +69,7 @@ static int etmemd_migrate_mem(const char *pid, const char *grade_path, struct pa return 0; } - fp = etmemd_get_proc_file(pid, grade_path, "r+"); + fp = etmemd_get_proc_file(pid, grade_path, 0, "r+"); if (fp == NULL) { etmemd_log(ETMEMD_LOG_ERR, "cannot open %s for pid %s\n", grade_path, pid); return -1; diff --git a/src/etmemd_src/etmemd_pool_adapter.c b/src/etmemd_src/etmemd_pool_adapter.c index 890aae8..417f864 100644 --- a/src/etmemd_src/etmemd_pool_adapter.c +++ b/src/etmemd_src/etmemd_pool_adapter.c @@ -17,17 +17,18 @@ #include #include #include "etmemd_pool_adapter.h" +#include "etmemd_engine.h" -static void push_ctrl_workflow(struct task_pid **tk_pid) +static void push_ctrl_workflow(struct task_pid **tk_pid, void *(*exector)(void *)) { struct task_pid *curr_pid = NULL; struct task *tk = (*tk_pid)->tk; while (*tk_pid != NULL) { if (threadpool_add_worker(tk->threadpool_inst, - tk->workflow_engine, + exector, (*tk_pid)) != 0) { etmemd_log(ETMEMD_LOG_DEBUG, "Failed to push < pid %u, Task_value %s, project_name %s >\n", - (*tk_pid)->pid, tk->value, tk->proj->name); + (*tk_pid)->pid, tk->value, tk->eng->proj->name); curr_pid = *tk_pid; *tk_pid = (*tk_pid)->next; free_task_pid_mem(&curr_pid); @@ -40,18 +41,19 @@ static void push_ctrl_workflow(struct task_pid **tk_pid) static void *launch_threadtimer_executor(void *arg) { - struct task *tk = (struct task *)arg; + struct task_executor *executor = (struct task_executor*)arg; + struct task *tk = executor->tk; thread_pool *pool_inst = NULL; bool done = false; int execution_size; int scheduing_count; - if (tk->proj->start) { + if (tk->eng->proj->start) { if (etmemd_get_task_pids(tk) != 0) { return NULL; } - push_ctrl_workflow(&tk->pids); + push_ctrl_workflow(&tk->pids, executor->func); threadpool_notify(tk->threadpool_inst); @@ -72,32 +74,34 @@ static void *launch_threadtimer_executor(void *arg) return NULL; } -int start_threadpool_work(struct task *tk) +int start_threadpool_work(struct task_executor *executor) { + struct task *tk = executor->tk; + etmemd_log(ETMEMD_LOG_DEBUG, "start etmem for Task_value %s, project_name %s\n", - tk->value, tk->proj->name); + tk->value, tk->eng->proj->name); /* create the threadpool first and it will start auto */ tk->threadpool_inst = threadpool_create(tk->max_threads); if (tk->threadpool_inst == NULL) { etmemd_log(ETMEMD_LOG_ERR, "Thread pool creation failed for project <%s> task <%s>.\n", - tk->proj->name, tk->value); + tk->eng->proj->name, tk->value); return -1; } - tk->timer_inst = thread_timer_create(tk->proj->interval); + tk->timer_inst = thread_timer_create(tk->eng->proj->interval); if (tk->timer_inst == NULL) { threadpool_stop_and_destroy(&tk->threadpool_inst); etmemd_log(ETMEMD_LOG_ERR, "Timer task creation failed for project <%s> task <%s>.\n", - tk->proj->name, tk->value); + tk->eng->proj->name, tk->value); return -1; } - if (thread_timer_start(tk->timer_inst, launch_threadtimer_executor, tk) != 0) { + if (thread_timer_start(tk->timer_inst, launch_threadtimer_executor, executor) != 0) { threadpool_stop_and_destroy(&tk->threadpool_inst); thread_timer_destroy(&tk->timer_inst); etmemd_log(ETMEMD_LOG_ERR, "Timer task start failed for project <%s> task <%s>.\n", - tk->proj->name, tk->value); + tk->eng->proj->name, tk->value); return -1; } @@ -107,11 +111,11 @@ int start_threadpool_work(struct task *tk) void stop_and_delete_threadpool_work(struct task *tk) { etmemd_log(ETMEMD_LOG_DEBUG, "stop and delete task <%s> of project <%s>\n", - tk->value, tk->proj->name); + tk->value, tk->eng->proj->name); if (tk->timer_inst == NULL) { etmemd_log(ETMEMD_LOG_DEBUG, "task <%s> of project <%s> has not been started, return\n", - tk->value, tk->proj->name); + tk->value, tk->eng->proj->name); return; } diff --git a/src/etmemd_src/etmemd_project.c b/src/etmemd_src/etmemd_project.c index 7ddc63f..9ead14c 100644 --- a/src/etmemd_src/etmemd_project.c +++ b/src/etmemd_src/etmemd_project.c @@ -29,280 +29,612 @@ #include "etmemd_file.h" #include "etmemd_log.h" +#define MAX_INTERVAL_VALUE 1200 +#define MAX_SLEEP_VALUE 1200 +#define MAX_LOOP_VALUE 120 + static SLIST_HEAD(project_list, project) g_projects = SLIST_HEAD_INITIALIZER(g_projects); -static void free_before_delete_project(struct project *proj) +static struct project *get_proj_by_name(const char *name) { - struct task *tmp_tk = NULL; + struct project *proj = NULL; + + SLIST_FOREACH(proj, &g_projects, entry) { + if (strcmp(proj->name, name) == 0) { + return proj; + } + } - etmemd_safe_free((void **)&proj->name); + return NULL; +} - while (proj->tasks != NULL) { - tmp_tk = proj->tasks; - proj->tasks = proj->tasks->next; +static struct engine *get_eng_by_name(struct project *proj, const char *name) +{ + struct engine *eng = proj->engs; - etmemd_free_task_pids(tmp_tk); - etmemd_free_task_struct(&tmp_tk); + while (eng != NULL) { + if (strcmp(eng->name, name) == 0) { + return eng; + } + eng = eng->next; } + + return NULL; } -static FILE *memid_project_open_conf(const char *file_name) +static struct task *get_task_by_name(struct project *proj, struct engine *eng, const char *name) { - FILE *file = NULL; - char path[PATH_MAX] = {0}; - struct stat info; - int r; - int fd; + struct task *tk = eng->tasks; - if (realpath(file_name, path) == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "%s should be real path.\n", file_name); - return NULL; + while (tk != NULL) { + if (strcmp(tk->name, name) == 0) { + return tk; + } + tk = tk->next; } - fd = open(path, O_RDONLY); - if (fd == -1) { - return NULL; + return NULL; +} + +static char *get_obj_key(char *obj_name, const char *group_name) +{ + if (strcmp(obj_name, group_name) == 0) { + return "name"; + } else { + return obj_name; } +} + +static enum opt_result project_of_group(GKeyFile *config, const char *group_name, struct project **proj) +{ + *proj = NULL; + char *proj_name = NULL; + char *key = NULL; - r = fstat(fd, &info); - if (r == -1) { - close(fd); - return NULL; + key = get_obj_key(PROJ_GROUP, group_name); + if (g_key_file_has_key(config, group_name, key, NULL) == FALSE) { + etmemd_log(ETMEMD_LOG_ERR, "project name is not set for %s\n", group_name); + return OPT_INVAL; } - if (S_ISDIR(info.st_mode)) { - etmemd_log(ETMEMD_LOG_ERR, "%s should be a file , not a folder.\n", path); - close(fd); - return NULL; + proj_name = g_key_file_get_string(config, group_name, key, NULL); + if (proj_name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "get project name from %s fail\n", group_name); + return OPT_INTER_ERR; } - file = fdopen(fd, "r"); + *proj = get_proj_by_name(proj_name); - return file; + free(proj_name); + return OPT_SUCCESS; } -static struct project *etmemd_project_init_struct(const char *project_name) +static enum opt_result engine_of_group(GKeyFile *config, char *group_name, struct project *proj, struct engine **eng) { - struct project *proj = NULL; + char *key = NULL; + char *eng_name = NULL; + *eng = NULL; - proj = (struct project *)calloc(1, sizeof(struct project)); - if (proj == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "malloc for project fail.\n"); - return NULL; + key = get_obj_key(ENG_GROUP, group_name); + if (g_key_file_has_key(config, group_name, key, NULL) == FALSE) { + etmemd_log(ETMEMD_LOG_ERR, "engine is not set for %s\n", group_name); + return OPT_INVAL; } - proj->name = calloc(1, strlen(project_name) + 1); - if (proj->name == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "malloc project name fail.\n"); - free(proj); - return NULL; + eng_name = g_key_file_get_string(config, group_name, key, NULL); + if (eng_name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "get engine name from %s fail\n", group_name); + return OPT_INTER_ERR; } - if (strncpy_s(proj->name, strlen(project_name) + 1, project_name, - strlen(project_name)) != EOK) { - etmemd_log(ETMEMD_LOG_ERR, "strncpy_s for project name fail\n"); - free(proj->name); - proj->name = NULL; - free(proj); - return NULL; + *eng = get_eng_by_name(proj, eng_name); + free(eng_name); + return OPT_SUCCESS; +} + +static enum opt_result task_of_group(GKeyFile *config, char *group_name, struct project *proj, struct engine *eng, struct task **tk) +{ + char *task_name = NULL; + char *key = NULL; + *tk = NULL; + + key = get_obj_key(TASK_GROUP, group_name); + if (g_key_file_has_key(config, group_name, key, NULL) == FALSE) { + etmemd_log(ETMEMD_LOG_ERR, "task name is not set for %s\n", group_name); + return OPT_INVAL; + } + + task_name = g_key_file_get_string(config, group_name, key, NULL); + if (task_name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "get task name from %s fail\n", group_name); + return OPT_INTER_ERR; } - return proj; + *tk = get_task_by_name(proj, eng, task_name); + free(task_name); + return OPT_SUCCESS; } -static struct project *get_proj_by_name(const char *name) +static enum opt_result get_group_objs(GKeyFile *config, char *group_name, struct project **proj, struct engine **eng, struct task **tk) { - struct project *proj = NULL; + enum opt_result ret; - if (name == NULL || strlen(name) == 0) { - etmemd_log(ETMEMD_LOG_ERR, "project name must be given and should not be empty.\n"); - return NULL; + /* get project */ + ret = project_of_group(config, group_name, proj); + if (ret != OPT_SUCCESS) { + return ret; } - SLIST_FOREACH(proj, &g_projects, entry) { - if (strcmp(proj->name, name) == 0) { - return proj; - } + /* get engine */ + if (eng == NULL) { + return OPT_SUCCESS; + } + if (*proj == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "project of group %s not existed\n", group_name); + return OPT_PRO_NOEXIST; } - return NULL; + ret = engine_of_group(config, group_name, *proj, eng); + if (ret != OPT_SUCCESS) { + return ret; + } + + /* get task */ + if (tk == NULL) { + return OPT_SUCCESS; + } + if (*eng == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "engine of group %s not existed\n", group_name); + return OPT_ENG_NOEXIST; + } + + ret = task_of_group(config, group_name, *proj, *eng, tk); + if (ret != OPT_SUCCESS) { + return ret; + } + + return OPT_SUCCESS; } -static enum opt_result check_add_params(const char *project_name, const char *file_name) +static void project_insert_task(struct project *proj, struct engine *eng, struct task *task) { + task->next = eng->tasks; + eng->tasks = task; + task->eng = eng; +} + +static void project_remove_task(struct engine *eng, struct task *task) +{ + struct task **iter = &eng->tasks; + + for (; *iter != NULL && *iter != task; iter = &(*iter)->next) { + ; + } + + if (*iter == NULL) { + return; + } + + *iter = (*iter)->next; + task->next = NULL; + task->eng = NULL; +} + +static void do_remove_task(struct project *proj, struct engine *eng, struct task *tk) +{ + if (proj->start && eng->ops->stop_task != NULL) { + eng->ops->stop_task(eng, tk); + } + if (eng->ops->clear_task_params != NULL) { + eng->ops->clear_task_params(tk); + } + + project_remove_task(eng, tk); + etmemd_remove_task(tk); +} + +enum opt_result etmemd_project_add_task(GKeyFile *config) +{ + struct task *tk = NULL; struct project *proj = NULL; + struct engine *eng = NULL; + enum opt_result ret; - if (project_name == NULL || strlen(project_name) == 0) { - etmemd_log(ETMEMD_LOG_ERR, "project name must be given and should not be empty.\n"); - return OPT_INVAL; + ret = get_group_objs(config, TASK_GROUP, &proj, &eng, &tk); + if (ret != OPT_SUCCESS) { + return ret; } - if (strlen(project_name) > PROJECT_NAME_MAX_LEN) { - etmemd_log(ETMEMD_LOG_ERR, "the length of project name should not be larger than %d\n", - PROJECT_NAME_MAX_LEN); - return OPT_INVAL; + if (tk != NULL) { + etmemd_log(ETMEMD_LOG_ERR, "task already exist\n"); + return OPT_TASK_EXISTED; } - if (file_name == NULL || strlen(file_name) == 0) { - etmemd_log(ETMEMD_LOG_ERR, "file name must be given and should not be empty.\n"); - return OPT_INVAL; + tk = etmemd_add_task(config); + if (tk == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "create new task fail\n"); + return OPT_INTER_ERR; } - if (strlen(file_name) > FILE_NAME_MAX_LEN) { - etmemd_log(ETMEMD_LOG_ERR, "the length of file name should not be larger than %d\n", - FILE_NAME_MAX_LEN); - return OPT_INVAL; + project_insert_task(proj, eng, tk); + + if (eng->ops->fill_task_params != NULL && eng->ops->fill_task_params(config, tk) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "fill task param fail\n"); + goto remove_task; } - proj = get_proj_by_name(project_name); - if (proj != NULL) { - etmemd_log(ETMEMD_LOG_ERR, "project %s already exist.\n", project_name); - return OPT_PRO_EXISTED; + if (proj->start && eng->ops->start_task(eng, tk) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "start added task %s fail\n", tk->name); + goto clear_task; } return OPT_SUCCESS; + +clear_task: + if (eng->ops->clear_task_params != NULL) { + eng->ops->clear_task_params(tk); + } + +remove_task: + project_remove_task(eng, tk); + etmemd_remove_task(tk); + + return OPT_INTER_ERR; } -enum opt_result etmemd_project_add(const char *project_name, const char *file_name) +enum opt_result etmemd_project_remove_task(GKeyFile *config) { + struct task *tk = NULL; struct project *proj = NULL; - FILE *file = NULL; + struct engine *eng = NULL; enum opt_result ret; - ret = check_add_params(project_name, file_name); - if (ret != 0) { + ret = get_group_objs(config, TASK_GROUP, &proj, &eng, &tk); + if (ret != OPT_SUCCESS) { return ret; } - - file = memid_project_open_conf(file_name); - if (file == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "open %s fail.\n", file_name); - return OPT_INVAL; + if (tk == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "task not exsit\n"); + return OPT_TASK_NOEXIST; } - proj = etmemd_project_init_struct(project_name); - if (proj == NULL) { - goto out_close; + do_remove_task(proj, eng, tk); + return OPT_SUCCESS; +} + +static void project_insert_engine(struct project *proj, struct engine *eng) +{ + eng->next = proj->engs; + proj->engs = eng; + eng->proj = proj; +} + +static void project_remove_engine(struct project *proj, struct engine *eng) +{ + struct engine **iter = &(proj->engs); + + for (; *iter != NULL && *iter != eng; iter = &(*iter)->next) { + ; } - if (etmemd_fill_proj_by_conf(proj, file) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "get project from configuration file fail.\n"); - free_before_delete_project(proj); - free(proj); - proj = NULL; - goto out_close; + if (*iter == NULL) { + return; } - SLIST_INSERT_HEAD(&g_projects, proj, entry); - fclose(file); - return OPT_SUCCESS; + *iter = (*iter)->next; + eng->next = NULL; + eng->proj = NULL; +} + +static int project_start_engine(struct project *proj, struct engine *eng) +{ + struct task *tk = eng->tasks; + int ret = 0; -out_close: - fclose(file); - return OPT_INVAL; + if (eng->ops->start_task == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "engine %s not support start\n", eng->name); + return -1; + } + + for (; tk != NULL; tk = tk->next) { + if (eng->ops->start_task(eng, tk) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "start task %s fail.\n", tk->value); + ret = -1; + } + } + return ret; } -static void stop_tasks(struct project *proj) +static void project_stop_engine(struct project *proj, struct engine *eng) { - struct task *curr_task = NULL; + struct task *tk = eng->tasks; - if (!proj->start) { + if (eng->ops->stop_task == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "engine %s not support stop\n", eng->name); return; } + for (; tk != NULL; tk = tk->next) { + eng->ops->stop_task(eng, tk); + } +} - proj->start = false; +static void do_remove_engine_tasks(struct project *proj, struct engine *eng) +{ + while (eng->tasks != NULL) { + do_remove_task(proj, eng, eng->tasks); + } +} - curr_task = proj->tasks; - while (curr_task != NULL) { - curr_task->stop_etmem(curr_task); - etmemd_log(ETMEMD_LOG_DEBUG, "Stop for Task_value %s, project_name %s \n", curr_task->value, - curr_task->proj->name); - curr_task = curr_task->next; +static void do_remove_engine(struct project *proj, struct engine *eng) +{ + do_remove_engine_tasks(proj, eng); + if (eng->ops->clear_eng_params != NULL) { + eng->ops->clear_eng_params(eng); } + + project_remove_engine(proj, eng); + etmemd_engine_remove(eng); } -enum opt_result etmemd_project_delete(const char *project_name) +enum opt_result etmemd_project_add_engine(GKeyFile *config) { + struct engine *eng = NULL; struct project *proj = NULL; + enum opt_result ret; - proj = get_proj_by_name(project_name); - if (proj == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "get project fail to delete.\n"); - return OPT_PRO_NOEXIST; + ret = get_group_objs(config, ENG_GROUP, &proj, &eng, NULL); + if (ret != OPT_SUCCESS) { + return ret; + } + if (eng != NULL) { + etmemd_log(ETMEMD_LOG_ERR, "engine %s exists\n", eng->name); + return OPT_ENG_EXISTED; } - stop_tasks(proj); + eng = etmemd_engine_add(config); + if (eng == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "create engine fail\n"); + return OPT_INTER_ERR; + } - struct task *curr_task = NULL; - curr_task = proj->tasks; - while (curr_task != NULL) { - if (curr_task->delete_etmem != NULL) { - curr_task->delete_etmem(curr_task); - curr_task = curr_task->next; - } + project_insert_engine(proj, eng); + if (eng->ops->fill_eng_params != NULL && eng->ops->fill_eng_params(config, eng) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "fill %s engine params fail\n", eng->name); + project_remove_engine(proj, eng); + etmemd_engine_remove(eng); + return OPT_INTER_ERR; } - SLIST_REMOVE(&g_projects, proj, project, entry); + return OPT_SUCCESS; +} + +enum opt_result etmemd_project_remove_engine(GKeyFile *config) +{ + struct engine *eng = NULL; + struct project *proj = NULL; + enum opt_result ret; + + ret = get_group_objs(config, ENG_GROUP, &proj, &eng, NULL); + if (ret != OPT_SUCCESS) { + return ret; + } + if (eng == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "remove engine is not existed\n"); + return OPT_ENG_NOEXIST; + } + + do_remove_engine(proj, eng); + return OPT_SUCCESS; +} - free_before_delete_project(proj); +static int fill_project_name(void *obj, void *val) +{ + struct project *proj = (struct project *)obj; + char *name = (char *)val; + proj->name = name; + return 0; +} + +static int fill_project_loop(void *obj, void *val) +{ + struct project *proj = (struct project *)obj; + int loop = parse_to_int(val); + if (loop < 1 || loop > MAX_LOOP_VALUE) { + etmemd_log(ETMEMD_LOG_ERR, "invalid project loop value %d, it must be between 1 and %d.\n", + loop, MAX_LOOP_VALUE); + return -1; + } + + proj->loop = loop; + return 0; +} + +static int fill_project_interval(void *obj, void *val) +{ + struct project *proj = (struct project *)obj; + int interval = parse_to_int(val); + if (interval < 1 || interval > MAX_INTERVAL_VALUE) { + etmemd_log(ETMEMD_LOG_ERR, "invalid project interval value %d, it must be between 1 and %d.\n", + interval, MAX_INTERVAL_VALUE); + return -1; + } + + proj->interval = interval; + return 0; +} + +static int fill_project_sleep(void *obj, void *val) +{ + struct project *proj = (struct project *)obj; + int sleep = parse_to_int(val); + if (sleep < 1 || sleep > MAX_SLEEP_VALUE) { + etmemd_log(ETMEMD_LOG_ERR, "invalid project sleep value %d, it must be between 1 and %d.\n", + sleep, MAX_SLEEP_VALUE); + return -1; + } + + proj->sleep = sleep; + return 0; +} + +static struct config_item g_project_config_items[] = { + {"name", STR_VAL, fill_project_name, false}, + {"loop", INT_VAL, fill_project_loop, false}, + {"interval", INT_VAL, fill_project_interval, false}, + {"sleep", INT_VAL, fill_project_sleep, false}, +}; + +static void clear_project(struct project *proj) +{ + if (proj->name != NULL) { + free(proj->name); + proj->name = NULL; + } +} + +static int project_fill_by_conf(GKeyFile *config, struct project *proj) +{ + if (parse_file_config(config, PROJ_GROUP, g_project_config_items, ARRAY_SIZE(g_project_config_items), proj) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "parse project config fail\n"); + clear_project(proj); + return -1; + } + + return 0; +} + +static void do_remove_project(struct project *proj) +{ + SLIST_REMOVE(&g_projects, proj, project, entry); + while (proj->engs != NULL) { + do_remove_engine(proj, proj->engs); + } + clear_project(proj); free(proj); +} + +enum opt_result etmemd_project_add(GKeyFile *config) +{ + struct project *proj = NULL; + enum opt_result ret; + + ret = project_of_group(config, PROJ_GROUP, &proj); + if (ret != OPT_SUCCESS) { + return ret; + } + if (proj != NULL) { + etmemd_log(ETMEMD_LOG_ERR, "project %s existes\n", proj->name); + return OPT_PRO_EXISTED; + } + + proj = (struct project *)calloc(1, sizeof(struct project)); + if (proj == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc memory for project fail\n"); + return OPT_INTER_ERR; + } + if (project_fill_by_conf(config, proj) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "fill project from configuration file fail\n"); + free(proj); + proj = NULL; + return OPT_INVAL; + } + SLIST_INSERT_HEAD(&g_projects, proj, entry); return OPT_SUCCESS; } -static void etmemd_project_print_line(void) +enum opt_result etmemd_project_remove(GKeyFile *config) { - int i; + struct project *proj = NULL; + enum opt_result ret; - for (i = 0; i < PROJECT_SHOW_COLM_MAX; i++) { - printf("-"); + ret = get_group_objs(config, PROJ_GROUP, &proj, NULL, NULL); + if (ret != OPT_SUCCESS) { + return ret; + } + + if (proj == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "remove project not existed\n"); + return OPT_PRO_NOEXIST; } - printf("\n"); + + do_remove_project(proj); + return OPT_SUCCESS; } -static void etmemd_project_print(const struct project *proj) +static void etmemd_project_print(int fd, const struct project *proj) { - etmemd_project_print_line(); - printf("project: %s\n\n", proj->name); - printf("%-8s %-32s %-32s %-32s %-16s\n", - "number", - "type", - "value", - "engine", - "started"); - etmemd_print_tasks(proj->tasks); - etmemd_project_print_line(); + struct engine *eng = NULL; + + dprintf_all(fd, "project: %s\n", proj->name); + dprintf_all(fd, "%-8s %-8s %-16s %-16s %-16s %-8s\n", + "number", + "type", + "value", + "name", + "engine", + "started"); + for (eng = proj->engs; eng != NULL; eng = eng->next) { + etmemd_print_tasks(fd, eng->tasks, eng->name, proj->start); + } } -enum opt_result etmemd_project_show(void) +enum opt_result etmemd_project_show(const char *project_name, int sock_fd) { struct project *proj = NULL; bool exists = false; SLIST_FOREACH(proj, &g_projects, entry) { - etmemd_project_print(proj); + if (project_name != NULL && strcmp(project_name, proj->name) != 0) { + continue; + } + etmemd_project_print(sock_fd, proj); exists = true; } if (!exists) { - etmemd_project_print_line(); - printf("project: no project exits\n\n"); - etmemd_project_print_line(); + if (project_name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "no project exists\n"); + } else { + etmemd_log(ETMEMD_LOG_ERR, "project: project %s is not existed\n\n", project_name); + } + return OPT_PRO_NOEXIST; } - if (fflush(stdout) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "fflush stdout fail.\n"); - return OPT_INTER_ERR; + return OPT_SUCCESS; +} + +static int start_tasks(struct project *proj) +{ + struct engine *eng = NULL; + int ret = 0; + + for (eng = proj->engs; eng != NULL; eng = eng->next) { + if (project_start_engine(proj, eng) != 0) { + ret = -1; + } } - return OPT_SUCCESS; + return ret; +} + +static void stop_tasks(struct project *proj) +{ + struct engine *eng = NULL; + + for (eng = proj->engs; eng != NULL; eng = eng->next) { + project_stop_engine(proj, eng); + } } enum opt_result etmemd_migrate_start(const char *project_name) { struct project *proj = NULL; - struct task *curr_task = NULL; - int start_task_num = 0; + + if (project_name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "project is not set for start cmd\n"); + return OPT_INVAL; + } proj = get_proj_by_name(project_name); if (proj == NULL) { @@ -315,27 +647,12 @@ enum opt_result etmemd_migrate_start(const char *project_name) return OPT_PRO_STARTED; } - proj->start = true; - - curr_task = proj->tasks; - while (curr_task != NULL) { - if (curr_task->start_etmem(curr_task) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "start task %s fail.\n", - curr_task->value); - curr_task = curr_task->next; - continue; - } - - start_task_num++; - curr_task = curr_task->next; - } - - if (start_task_num == 0) { - etmemd_log(ETMEMD_LOG_ERR, "all tasks start fail.\n"); - proj->start = false; + if (start_tasks(proj) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "some task of project %s start fail\n", project_name); return OPT_INTER_ERR; } + proj->start = true; return OPT_SUCCESS; } @@ -343,6 +660,11 @@ enum opt_result etmemd_migrate_stop(const char *project_name) { struct project *proj = NULL; + if (project_name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "project is not set for stop cmd\n"); + return OPT_INVAL; + } + proj = get_proj_by_name(project_name); if (proj == NULL) { etmemd_log(ETMEMD_LOG_ERR, "get project fail.\n"); @@ -360,16 +682,47 @@ enum opt_result etmemd_migrate_stop(const char *project_name) return OPT_SUCCESS; } +enum opt_result etmemd_project_mgt_engine(const char *project_name, const char *eng_name, char *cmd, char *task_name, int sock_fd) +{ + struct engine *eng = NULL; + struct project *proj = NULL; + struct task *tk = NULL; + + if (project_name == NULL || eng_name == NULL || cmd == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "project, engine and cmd must all be given\n"); + return OPT_INVAL; + } + + proj = get_proj_by_name(project_name); + if (proj == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "project %s is not existed\n", project_name); + return OPT_PRO_NOEXIST; + } + if (!proj->start) { + etmemd_log(ETMEMD_LOG_ERR, "project %s is not started\n", project_name); + return OPT_INVAL; + } + + eng = get_eng_by_name(proj, eng_name); + if (eng == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "engine %s is not existed\n", eng_name); + return OPT_INVAL; + } + if (task_name != NULL) { + tk = get_task_by_name(proj, eng, task_name); + } + if (eng->ops->eng_mgt_func(eng, tk, cmd, sock_fd) != 0) { + return OPT_INVAL; + } + return OPT_SUCCESS; +} + void etmemd_stop_all_projects(void) { struct project *proj = NULL; while (!SLIST_EMPTY(&g_projects)) { proj = SLIST_FIRST(&g_projects); - SLIST_REMOVE_HEAD(&g_projects, entry); - stop_tasks(proj); - free_before_delete_project(proj); - free(proj); - proj = NULL; + do_remove_project(proj); } } diff --git a/src/etmemd_src/etmemd_rpc.c b/src/etmemd_src/etmemd_rpc.c index e74013a..e154083 100644 --- a/src/etmemd_src/etmemd_rpc.c +++ b/src/etmemd_src/etmemd_rpc.c @@ -18,11 +18,13 @@ #include #include #include +#include #include "securec.h" #include "etmemd_rpc.h" #include "etmemd_project.h" #include "etmemd_common.h" #include "etmemd_log.h" +#include "etmemd_file.h" /* the max length of sun_path in struct sockaddr_un is 108 */ #define RPC_ADDR_LEN_MAX 108 @@ -44,6 +46,9 @@ struct server_rpc_parser g_rpc_parser[] = { {"proj_name", DECODE_STRING, (void **)&(g_rpc_params.proj_name)}, {"file_name", DECODE_STRING, (void **)&(g_rpc_params.file_name)}, {"cmd", DECODE_INT, (void **)&(g_rpc_params.cmd)}, + {"eng_name", DECODE_STRING, (void **)&(g_rpc_params.eng_name)}, + {"eng_cmd", DECODE_STRING, (void **)&(g_rpc_params.eng_cmd)}, + {"task_name", DECODE_STRING, (void **)&(g_rpc_params.task_name)}, {NULL, DECODE_END, NULL}, }; @@ -51,9 +56,13 @@ struct rpc_resp_msg g_resp_msg_arr[] = { {OPT_SUCCESS, "success"}, {OPT_INVAL, "error: invalid parameters"}, {OPT_PRO_EXISTED, "error: project has been existed"}, + {OPT_PRO_NOEXIST, "error: project is not exist"}, {OPT_PRO_STARTED, "error: project has been started"}, {OPT_PRO_STOPPED, "error: project has been stopped"}, - {OPT_PRO_NOEXIST, "error: project is not exist"}, + {OPT_ENG_EXISTED, "error: engine has been existed"}, + {OPT_ENG_NOEXIST, "error: engine is not exist"}, + {OPT_TASK_EXISTED, "error: task has been existed"}, + {OPT_TASK_NOEXIST, "error: task is not exist"}, {OPT_INTER_ERR, "error: etmemd has internal error"}, {OPT_RET_END, NULL}, }; @@ -66,27 +75,123 @@ static void etmemd_set_flag(int s) g_sock_fd = -1; } +static void etmemd_ignore_sig(int s) +{ + return; +} + void etmemd_handle_signal(void) { signal(SIGINT, etmemd_set_flag); signal(SIGTERM, etmemd_set_flag); + signal(SIGPIPE, etmemd_ignore_sig); return; } +struct obj_cmd_item { + char *name; + enum opt_result (*func)(GKeyFile *config); +}; + +static enum opt_result do_obj_cmd(GKeyFile *config, struct obj_cmd_item *items, unsigned n) +{ + unsigned i; + bool parsed = false; + enum opt_result ret; + + for (i = 0; i < n; i++) { + if (g_key_file_has_group(config, items[i].name) == FALSE) { + continue; + } + + ret = items[i].func(config); + if (ret != 0) { + etmemd_log(ETMEMD_LOG_ERR, "parse group %s fail\n", items[i].name); + return ret; + } + parsed = true; + } + + if (!parsed) { + etmemd_log(ETMEMD_LOG_ERR, "no group has been parsed\n"); + return OPT_INVAL; + } + + return OPT_SUCCESS; +} + +struct obj_cmd_item obj_add_items[] = { + {PROJ_GROUP, etmemd_project_add}, + {ENG_GROUP, etmemd_project_add_engine}, + {TASK_GROUP, etmemd_project_add_task}, +}; + +static enum opt_result do_obj_add(GKeyFile *config) +{ + return do_obj_cmd(config, obj_add_items, ARRAY_SIZE(obj_add_items)); +} + +static struct obj_cmd_item obj_remove_items[] = { + {TASK_GROUP, etmemd_project_remove_task}, + {ENG_GROUP, etmemd_project_remove_engine}, + {PROJ_GROUP, etmemd_project_remove}, +}; + +static enum opt_result do_obj_remove(GKeyFile *config) +{ + return do_obj_cmd(config, obj_remove_items, ARRAY_SIZE(obj_remove_items)); +} + +static enum opt_result handle_obj_cmd(char *file_name, enum cmd_type type) +{ + GKeyFile *config = NULL; + enum opt_result ret; + + if (file_name == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "file name is not set for obj cmd\n"); + return OPT_INVAL; + } + + config = g_key_file_new(); + if (config == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "get empty config file fail\n"); + return OPT_INTER_ERR; + } + + if (g_key_file_load_from_file(config, file_name, G_KEY_FILE_NONE, NULL) == FALSE) { + etmemd_log(ETMEMD_LOG_ERR, "load config file fail\n"); + ret = OPT_INTER_ERR; + goto free_file; + } + + switch (type) { + case OBJ_ADD: + ret = do_obj_add(config); + break; + case OBJ_DEL: + ret = do_obj_remove(config); + break; + default: + ret = OPT_INVAL; + } + +free_file: + g_key_file_free(config); + return ret; +} + static enum opt_result etmemd_switch_cmd(const struct server_rpc_params svr_param) { enum opt_result ret = OPT_INVAL; switch (svr_param.cmd) { - case PROJ_ADD: - ret = etmemd_project_add(svr_param.proj_name, svr_param.file_name); - return ret; - case PROJ_DEL: - ret = etmemd_project_delete(svr_param.proj_name); + case OBJ_ADD: + case OBJ_DEL: + ret = handle_obj_cmd(svr_param.file_name, svr_param.cmd); return ret; case PROJ_SHOW: - ret = etmemd_project_show(); + ret = etmemd_project_show(svr_param.proj_name, svr_param.sock_fd); return ret; case MIG_START: ret = etmemd_migrate_start(svr_param.proj_name); @@ -94,6 +199,10 @@ static enum opt_result etmemd_switch_cmd(const struct server_rpc_params svr_para case MIG_STOP: ret = etmemd_migrate_stop(svr_param.proj_name); return ret; + case ENG_CMD: + ret = etmemd_project_mgt_engine(svr_param.proj_name, svr_param.eng_name, + svr_param.eng_cmd, svr_param.task_name, svr_param.sock_fd); + return ret; default: etmemd_log(ETMEMD_LOG_ERR, "Invalid command.\n"); return ret; @@ -297,11 +406,24 @@ static int etmemd_rpc_decode_int(void **ptr, const char *buf, return 0; } +static bool skip_null_arg(const char *buf, unsigned long *idx) +{ + if (buf[*idx] == '-') { + *idx += 2; // add 2 for skipping '- ' + return true; + } + return false; +} + static int etmemd_rpc_decode(struct server_rpc_parser *parser, const char *buf, unsigned long buf_len, unsigned long *idx) { int ret; + if (skip_null_arg(buf, idx)) { + return 0; + } + switch (parser->type) { case DECODE_STRING: ret = etmemd_rpc_decode_str(parser->member_ptr, buf, buf_len, idx); @@ -377,6 +499,10 @@ static void etmemd_rpc_send_response_msg(int sock_fd, enum opt_result result) int i = 0; ssize_t ret = -1; + if (result == OPT_SUCCESS) { + return; + } + while (g_resp_msg_arr[i].msg != NULL) { if (result != g_resp_msg_arr[i].result) { i++; @@ -398,6 +524,7 @@ static void etmemd_rpc_handle(int sock_fd) { enum opt_result ret; + g_rpc_params.sock_fd = sock_fd; ret = etmemd_switch_cmd(g_rpc_params); if (ret != OPT_SUCCESS) { etmemd_log(ETMEMD_LOG_ERR, "operate cmd %d fail\n", g_rpc_params.cmd); @@ -474,7 +601,7 @@ static int etmemd_rpc_accept(int sock_fd) goto RPC_EXIT; } - etmemd_log(ETMEMD_LOG_INFO, "etmemd get socket message \"%s\"\n", recv_buf); + etmemd_log(ETMEMD_LOG_DEBUG, "etmemd get socket message \"%s\"\n", recv_buf); if (etmemd_rpc_parse(recv_buf, (unsigned long)rc) == 0) { etmemd_rpc_handle(accp_fd); } diff --git a/src/etmemd_src/etmemd_scan.c b/src/etmemd_src/etmemd_scan.c index 66694a1..bb8dfa3 100644 --- a/src/etmemd_src/etmemd_scan.c +++ b/src/etmemd_src/etmemd_scan.c @@ -17,10 +17,12 @@ #include #include #include +#include #include "etmemd.h" #include "etmemd_scan.h" #include "etmemd_project.h" +#include "etmemd_engine.h" #include "etmemd_common.h" #include "etmemd_log.h" #include "securec.h" @@ -30,6 +32,8 @@ #define PUD_SIZE_SHIFT 30 #define HEXADECIMAL_RADIX 16 #define PMD_IDLE_PTES_PARAMETER 512 +#define VMFLAG_MAX_LEN 100 +#define VMFLAG_MAX_NUM 30 static const enum page_type g_page_type_by_idle_kind[] = { PTE_TYPE, @@ -45,11 +49,12 @@ static const enum page_type g_page_type_by_idle_kind[] = { PAGE_TYPE_INVAL, }; -static u_int64_t g_page_size[PAGE_TYPE_INVAL] = { - 1UL << PTE_SIZE_SHIFT, /* PTE size */ - 1UL << PMD_SIZE_SHIFT, /* PMD size */ - 1UL << PUD_SIZE_SHIFT, /* PUD size */ -}; +static uint64_t g_page_size[PAGE_TYPE_INVAL]; + +int page_type_to_size(enum page_type type) +{ + return g_page_size[type]; +} static unsigned int get_page_shift(long pagesize) { @@ -260,13 +265,91 @@ exit: return NULL; } -struct vmas *get_vmas(const char *pid) +static bool is_vma_with_vmflags(FILE *fp, char *vmflags_array[], int vmflags_num) +{ + char parse_line[FILE_LINE_MAX_LEN]; + size_t len; + int i; + + len = strlen(VMFLAG_HEAD); + while (fgets(parse_line, FILE_LINE_MAX_LEN - 1, fp) != NULL) { + /* skip the line which has no match length */ + if (strlen(parse_line) <= len) { + continue; + } + if (strncmp(VMFLAG_HEAD, parse_line, len) != 0) { + continue; + } + + /* check any flag in flags is set */ + for (i = 0; i < vmflags_num; i++) { + if (strstr(parse_line, vmflags_array[i]) == NULL) { + return false; + } + } + return true; + } + + return false; +} + +static bool is_vmflags_match(FILE *fp, char *vmflags_array[], int vmflags_num) +{ + if (vmflags_num == 0) { + return true; + } + return is_vma_with_vmflags(fp, vmflags_array, vmflags_num); +} + +static bool is_anon_match(bool is_anon_only, struct vma *vma) +{ + if (!is_anon_only) { + return true; + } + return is_anonymous(vma); +} + +int split_vmflags(char ***vmflags_array, char *vmflags) +{ + char *flag = NULL; + char *saveptr = NULL; + char *tmp_array[VMFLAG_MAX_NUM] = {}; + int vmflags_num = 0; + int i; + + for (flag = strtok_r(vmflags, " ", &saveptr); flag != NULL; flag = strtok_r(NULL, " ,", &saveptr)) { + tmp_array[vmflags_num++] = flag; + if (vmflags_num == VMFLAG_MAX_NUM) { + break; + } + } + + *vmflags_array = malloc(sizeof(char *) * vmflags_num); + if (*vmflags_array == NULL) { + return -1; + } + + for (i = 0; i < vmflags_num; i++) { + (*vmflags_array)[i] = tmp_array[i]; + } + + return vmflags_num; +} + +struct vmas *get_vmas_with_flags(const char *pid, char *vmflags_array[], int vmflags_num, bool is_anon_only) { struct vmas *ret_vmas = NULL; struct vma **tmp_vma = NULL; FILE *fp = NULL; char maps_line[FILE_LINE_MAX_LEN]; size_t len; + char *maps_file = NULL; + + if (vmflags_num == 0) { + maps_file = MAPS_FILE; + } else { + maps_file = SMAPS_FILE; + } ret_vmas = (struct vmas *)calloc(1, sizeof(struct vmas)); if (ret_vmas == NULL) { @@ -274,9 +357,9 @@ struct vmas *get_vmas(const char *pid) return NULL; } - fp = etmemd_get_proc_file(pid, MAPS_FILE, "r"); + fp = etmemd_get_proc_file(pid, maps_file, 0, "r"); if (fp == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "open %s file of %s fail\n", MAPS_FILE, pid); + etmemd_log(ETMEMD_LOG_ERR, "open %s file of %s fail\n", maps_file, pid); free(ret_vmas); return NULL; } @@ -291,23 +374,35 @@ struct vmas *get_vmas(const char *pid) ret_vmas = NULL; break; } - tmp_vma = &((*tmp_vma)->next); - ret_vmas->vma_cnt++; - /* if the file path is too long, maps_line cannot read file line completely, clean invalid characters. */ + /* if the file path is too long, maps_line cannot read file line completely, clean invalid characters */ while (maps_line[len - 1] != '\n') { if (fgets(maps_line, FILE_LINE_MAX_LEN - 1, fp) != NULL) { len = strlen(maps_line); continue; } } + + /* skip vma without vmflags */ + if (!is_vmflags_match(fp, vmflags_array, vmflags_num) || !is_anon_match(is_anon_only, *tmp_vma)) { + free(*tmp_vma); + *tmp_vma = NULL; + continue; + } + + tmp_vma = &((*tmp_vma)->next); + ret_vmas->vma_cnt++; } fclose(fp); - return ret_vmas; } +struct vmas *get_vmas(const char *pid) +{ + return get_vmas_with_flags(pid, NULL, 0, true); +} + static u_int64_t get_address_from_buf(const unsigned char *buf, u_int64_t index) { u_int64_t address; @@ -408,9 +503,9 @@ static struct page_refs **record_parse_result(u_int64_t addr, enum page_idle_typ enum page_type page_size_type; /* ignore unaligned address when walk, because pages handled need to be aligned */ - if ((addr & (g_page_size[g_page_type_by_idle_kind[type]] - 1)) > 0) { + if ((addr & (page_type_to_size(g_page_type_by_idle_kind[type]) - 1)) > 0) { etmemd_log(ETMEMD_LOG_WARN, "ignore address %lx which not aligned %lx for type %d\n", addr, - g_page_size[g_page_type_by_idle_kind[type]], type); + page_type_to_size(g_page_type_by_idle_kind[type]), type); return pf; } @@ -429,7 +524,7 @@ static struct page_refs **record_parse_result(u_int64_t addr, enum page_idle_typ break; } - addr += g_page_size[page_size_type]; + addr += page_type_to_size(page_size_type); } return pf; @@ -480,23 +575,23 @@ static struct page_refs **parse_vma_result(const unsigned char *buf, u_int64_t s } else if (type < PMD_IDLE_PTES) { pf = record_parse_result(address, type, nr, pf); } else { - address = address + (u_int64_t)nr * g_page_size[g_page_type_by_idle_kind[type]]; + address = address + (u_int64_t)nr * page_type_to_size(g_page_type_by_idle_kind[type]); continue; } if (pf == NULL) { return NULL; } - address = address + (u_int64_t)nr * g_page_size[g_page_type_by_idle_kind[type]]; + address = address + (u_int64_t)nr * page_type_to_size(g_page_type_by_idle_kind[type]); } *end = address; return pf; } -static struct page_refs **walk_vmas(int fd, - struct walk_address *walk_address, - struct page_refs **pf, - unsigned long *use_rss) +struct page_refs **walk_vmas(int fd, + struct walk_address *walk_address, + struct page_refs **pf, + unsigned long *use_rss) { unsigned char *buf = NULL; u_int64_t size; @@ -504,7 +599,7 @@ static struct page_refs **walk_vmas(int fd, /* we make the buffer size as fitable as within a vma. * because the size of buffer passed to kernel will be calculated again (<< (3 + PAGE_SHIFT)) */ - size = ((walk_address->walk_end - walk_address->walk_start) >> 3) / g_page_size[PTE_TYPE]; + size = ((walk_address->walk_end - walk_address->walk_start) >> 3) / page_type_to_size(PTE_TYPE); /* we need to compare the size to the minimum size that kernel handled */ size = size < EPT_IDLE_BUF_MIN ? EPT_IDLE_BUF_MIN : size; @@ -547,7 +642,7 @@ int get_page_refs(const struct vmas *vmas, const char *pid, struct page_refs **p struct page_refs **tmp_page_refs = NULL; struct walk_address walk_address = {0, 0, 0}; - scan_fp = etmemd_get_proc_file(pid, IDLE_SCAN_FILE, "r"); + scan_fp = etmemd_get_proc_file(pid, IDLE_SCAN_FILE, 0, "r"); if (scan_fp == NULL) { etmemd_log(ETMEMD_LOG_ERR, "open %s file fail\n", IDLE_SCAN_FILE); return -1; @@ -567,11 +662,6 @@ int get_page_refs(const struct vmas *vmas, const char *pid, struct page_refs **p continue; } - if (!(is_anonymous(vma))) { - vma = vma->next; - continue; - } - /* meeting this branch means the end of address for last scan is between the address of * start and end this round, so we start from lastScanEnd address in case of scan repeatly. */ walk_address.walk_end = vma->end; @@ -630,7 +720,7 @@ struct page_refs *etmemd_do_scan(const struct task_pid *tpid, const struct task } /* loop for scanning idle_pages to get result of memory access. */ - for (i = 0; i < tk->proj->loop; i++) { + for (i = 0; i < tk->eng->proj->loop; i++) { ret = get_page_refs(vmas, pid, &page_refs, NULL); if (ret != 0) { etmemd_log(ETMEMD_LOG_ERR, "scan operation failed\n"); @@ -639,7 +729,7 @@ struct page_refs *etmemd_do_scan(const struct task_pid *tpid, const struct task page_refs = NULL; break; } - sleep((unsigned)tk->proj->sleep); + sleep((unsigned)tk->eng->proj->sleep); } free_vmas(vmas); diff --git a/src/etmemd_src/etmemd_slide.c b/src/etmemd_src/etmemd_slide.c index fe8ae92..ea9ccb4 100644 --- a/src/etmemd_src/etmemd_slide.c +++ b/src/etmemd_src/etmemd_slide.c @@ -26,57 +26,7 @@ #include "etmemd_scan.h" #include "etmemd_migrate.h" #include "etmemd_pool_adapter.h" - -static int fill_slide_param_t(struct engine *eng, const char *val) -{ - int value; - struct slide_params *s_param = (struct slide_params *)eng->params; - struct task *tk = (struct task *)eng->task; - - if (get_int_value(val, &value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid engine param T value.\n"); - return -1; - } - - /* the max value of T watermark is weight of write multipled by time of loop */ - if (value < 1 || value > tk->proj->loop * WRITE_TYPE_WEIGHT) { - etmemd_log(ETMEMD_LOG_ERR, "invalid engine param T value, must between 1 and loop.\n"); - return -1; - } - - s_param->t = value; - - return 0; -} - -static int parse_slide_params(struct engine *eng, FILE *file) -{ - char key[KEY_VALUE_MAX_LEN] = {}; - char value[KEY_VALUE_MAX_LEN] = {}; - char *get_line = NULL; - - get_line = skip_blank_line(file); - if (get_line == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "lack of config for slide engine privately\n"); - return -1; - } - - if (get_keyword_and_value(get_line, key, value) != 0) { - etmemd_log(ETMEMD_LOG_ERR, "get engine param keyword and value fail\n"); - return -1; - } - - if (strcmp(key, "T") != 0) { - etmemd_log(ETMEMD_LOG_ERR, "invalid slide parameter keyword, must be T\n"); - return -1; - } - - if (fill_slide_param_t(eng, value) != 0) { - return -1; - } - - return 0; -} +#include "etmemd_file.h" static struct memory_grade *slide_policy_interface(struct page_refs **page_refs, void *params) { @@ -125,32 +75,20 @@ static int slide_do_migrate(unsigned int pid, const struct memory_grade *memory_ return ret; } -static void *slide_exector(void *arg) +static void *slide_executor(void *arg) { struct task_pid *tk_pid = (struct task_pid *)arg; struct page_refs *page_refs = NULL; struct memory_grade *memory_grade = NULL; - struct engine *eng = NULL; - - eng = tk_pid->tk->eng; - if (eng == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "pid %u engine is null!\n", tk_pid->pid); - return NULL; - } - - if (eng->adp == NULL || eng->adp->do_scan == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "pid %u scan function is not registered!\n", tk_pid->pid); - return NULL; - } /* register cleanup function in case of unexpected cancellation detected, * and register for memory_grade first, because it needs to clean after page_refs is cleaned */ pthread_cleanup_push(clean_memory_grade_unexpected, &memory_grade); pthread_cleanup_push(clean_page_refs_unexpected, &page_refs); - page_refs = eng->adp->do_scan(tk_pid, tk_pid->tk); + page_refs = etmemd_do_scan(tk_pid, tk_pid->tk); if (page_refs != NULL) { - memory_grade = eng->mig_policy_func(&page_refs, eng->params); + memory_grade = slide_policy_interface(&page_refs, tk_pid->tk->params); } /* no need to use page_refs any longer. @@ -165,12 +103,7 @@ static void *slide_exector(void *arg) goto exit; } - if (eng->adp->do_migrate == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "pid %u migrate function is not registered!\n", tk_pid->pid); - goto exit; - } - - if (eng->adp->do_migrate(tk_pid->pid, memory_grade) != 0) { + if (slide_do_migrate(tk_pid->pid, memory_grade) != 0) { etmemd_log(ETMEMD_LOG_DEBUG, "slide migrate for pid %u fail\n", tk_pid->pid); } @@ -184,41 +117,104 @@ exit: return NULL; } -static int alloc_slide_params(struct task_pid **tk_pid) +static int fill_task_threshold(void *obj, void *val) { - (*tk_pid)->params = NULL; + struct slide_params *params = (struct slide_params *)obj; + int t = parse_to_int(val); + + if (t < 0) { + etmemd_log(ETMEMD_LOG_ERR, "slide engine param T should not be less than 0"); + return -1; + } + + params->t = t; return 0; } -int fill_engine_type_slide(struct engine *eng) +static struct config_item g_slide_task_config_items[] = { + {"T", INT_VAL, fill_task_threshold, false}, +}; + +static int slide_fill_task(GKeyFile *config, struct task *tk) { - struct slide_params *s_param = NULL; - struct task *tk = (struct task *)eng->task; + struct slide_params *params = calloc(1, sizeof(struct slide_params)); - s_param = (struct slide_params *)calloc(1, sizeof(struct slide_params)); - if (s_param == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "malloc slide engine params fail\n"); + if (params == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc slide param fail\n"); return -1; } - eng->adp = (struct adapter *)calloc(1, sizeof(struct adapter)); - if (eng->adp == NULL) { - etmemd_log(ETMEMD_LOG_ERR, "malloc adapter for task %s engine fail\n", tk->value); - free(s_param); + if (parse_file_config(config, TASK_GROUP, g_slide_task_config_items, ARRAY_SIZE(g_slide_task_config_items), + (void *)params) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "slide fill task fail\n"); + goto free_params; + } + + if (params->t > tk->eng->proj->loop * WRITE_TYPE_WEIGHT) { + etmemd_log(ETMEMD_LOG_ERR, "engine param T must less than loop.\n"); + goto free_params; + } + tk->params = params; + return 0; + +free_params: + free(params); + return -1; +} + +static void slide_clear_task(struct task *tk) +{ + free(tk->params); + tk->params = NULL; +} + +static int slide_start_task(struct engine *eng, struct task *tk) +{ + struct slide_params *params = tk->params; + + params->executor = malloc(sizeof(struct task_executor)); + if (params->executor == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "slide alloc memory for task_executor fail\n"); return -1; } - eng->params = (void *)s_param; - eng->engine_type = SLIDE_ENGINE; - eng->parse_param_conf = parse_slide_params; - eng->mig_policy_func = slide_policy_interface; - eng->adp->do_scan = etmemd_do_scan; - eng->adp->do_migrate = slide_do_migrate; - eng->alloc_params = alloc_slide_params; - tk->start_etmem = start_threadpool_work; - tk->stop_etmem = stop_and_delete_threadpool_work; - tk->delete_etmem = stop_and_delete_threadpool_work; - tk->workflow_engine = slide_exector; + params->executor->tk = tk; + params->executor->func = slide_executor; + if (start_threadpool_work(params->executor) != 0) { + free(params->executor); + params->executor = NULL; + etmemd_log(ETMEMD_LOG_ERR, "slide start task executor fail\n"); + return -1; + } + + return 0; +} +static void slide_stop_task(struct engine *eng, struct task *tk) +{ + struct slide_params *params = tk->params; + + stop_and_delete_threadpool_work(tk); + free(params->executor); + params->executor = NULL; +} + +struct engine_ops slide_eng_ops = { + .fill_eng_params = NULL, + .clear_eng_params = NULL, + .fill_task_params = slide_fill_task, + .clear_task_params = slide_clear_task, + .start_task = slide_start_task, + .stop_task = slide_stop_task, + .alloc_pid_params = NULL, + .free_pid_params = NULL, + .eng_mgt_func = NULL, +}; + +int fill_engine_type_slide(struct engine *eng) +{ + eng->ops = &slide_eng_ops; + eng->engine_type = SLIDE_ENGINE; + eng->name = "slide"; return 0; } diff --git a/src/etmemd_src/etmemd_task.c b/src/etmemd_src/etmemd_task.c index 8474c6c..5c81b74 100644 --- a/src/etmemd_src/etmemd_task.c +++ b/src/etmemd_src/etmemd_task.c @@ -20,12 +20,14 @@ #include #include #include +#include #include "securec.h" #include "etmemd_log.h" #include "etmemd_common.h" #include "etmemd_task.h" #include "etmemd_engine.h" +#include "etmemd_file.h" static int get_pid_through_pipe(char *arg_pid[], const int *pipefd) { @@ -74,7 +76,10 @@ static int get_pid_through_pipe(char *arg_pid[], const int *pipefd) void free_task_pid_mem(struct task_pid **tk_pid) { - etmemd_safe_free((void **)(&((*tk_pid)->params))); + struct engine *eng = (*tk_pid)->tk->eng; + if (eng->ops->free_pid_params != NULL) { + eng->ops->free_pid_params(eng, tk_pid); + } etmemd_safe_free((void **)tk_pid); } @@ -91,8 +96,8 @@ static void clean_nouse_pid(struct task_pid **tk_pid) static struct task_pid *alloc_tkpid_node(unsigned int pid, struct task *tk) { - int ret; struct task_pid *tk_pid = NULL; + struct engine *eng = tk->eng; tk_pid = (struct task_pid *)calloc(1, sizeof(struct task_pid)); if (tk_pid == NULL) { @@ -102,8 +107,7 @@ static struct task_pid *alloc_tkpid_node(unsigned int pid, struct task *tk) tk_pid->pid = pid; tk_pid->tk = tk; - ret = tk->eng->alloc_params(&tk_pid); - if (ret != 0) { + if (eng->ops->alloc_pid_params != NULL && eng->ops->alloc_pid_params(eng, &tk_pid) != 0) { free(tk_pid); return NULL; } @@ -362,6 +366,13 @@ int etmemd_get_task_pids(struct task *tk) return 0; } +static void clear_task_struct(struct task *task) +{ + etmemd_safe_free((void **)&task->type); + etmemd_safe_free((void **)&task->value); + etmemd_safe_free((void **)&task->name); +} + void etmemd_free_task_struct(struct task **tk) { struct task *task = NULL; @@ -371,36 +382,125 @@ void etmemd_free_task_struct(struct task **tk) } task = *tk; - etmemd_safe_free((void **)&task->type); - etmemd_safe_free((void **)&task->value); - - if (task->eng != NULL) { - etmemd_safe_free((void **)&task->eng->params); - etmemd_safe_free((void **)&task->eng->adp); - task->eng->task = NULL; - etmemd_safe_free((void **)&task->eng); - task->eng = NULL; - } - - task->proj = NULL; + clear_task_struct(task); free(task); *tk = NULL; } -void etmemd_print_tasks(const struct task *tk) +void etmemd_print_tasks(int fd, const struct task *tk, char *eng_name, bool started) { const struct task *tmp = tk; int i = 1; while (tmp != NULL) { - printf("%-8d %-32s %-32s %-32s %-16s\n", - i, - tmp->type, - tmp->value, - etmemd_get_eng_name(tmp->eng->engine_type), - tmp->timer_inst == NULL ? "false" : "true"); + dprintf_all(fd, "%-8d %-8s %-16s %-16s %-16s %-8s\n", + i, + tmp->type, + tmp->value, + tmp->name, + eng_name, + started ? "true" : "false"); tmp = tmp->next; i++; } } + +static int fill_task_name(void *obj, void *val) +{ + struct task *tk = (struct task *)obj; + char *name = (char *)val; + tk->name = name; + return 0; +} + +static int fill_task_type(void *obj, void *val) +{ + struct task *tk = (struct task *)obj; + char *type = (char *)val; + if (strcmp(val, "pid") != 0 && strcmp(val, "name") != 0) { + etmemd_log(ETMEMD_LOG_ERR, "invalid task type, must be pid or name.\n"); + return -1; + } + + tk->type = type; + return 0; +} + +static int fill_task_value(void *obj, void *val) +{ + struct task *tk = (struct task *)obj; + char *value = (char *)val; + tk->value = value; + return 0; +} + +static int fill_task_threads(void *obj, void *val) +{ + struct task *tk = (struct task *)obj; + int max_threads = parse_to_int(val); + int core; + + if (max_threads < 0) { + etmemd_log(ETMEMD_LOG_WARN, + "Thread count is abnormal, set the default minimum of current thread count to 1\n"); + max_threads = 1; + } + + core = get_nprocs(); + /* + * For IO intensive bussinesses, max-threads is limited to 2N + 1 of the maximum number + * of threads + */ + if (max_threads > 2 * core + 1) { + etmemd_log(ETMEMD_LOG_WARN, + "max-threads is limited to 2N+1 of the maximum number of threads\n"); + max_threads = 2 * core + 1; + } + + tk->max_threads = max_threads; + return 0; +} + +struct config_item g_task_config_items[] = { + {"name", STR_VAL, fill_task_name, false}, + {"type", STR_VAL, fill_task_type, false}, + {"value", STR_VAL, fill_task_value, false}, + {"max_threads", INT_VAL, fill_task_threads, true}, +}; + +static int task_fill_by_conf(GKeyFile *config, struct task *tk) +{ + if (parse_file_config(config, TASK_GROUP, g_task_config_items, + ARRAY_SIZE(g_task_config_items), (void *)tk) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "parse task config fail\n"); + clear_task_struct(tk); + return -1; + } + return 0; +} + +struct task *etmemd_add_task(GKeyFile *config) +{ + struct task *tk = calloc(1, sizeof(struct task)); + + if (tk == NULL) { + etmemd_log(ETMEMD_LOG_ERR, "alloc task struct fail\n"); + return NULL; + } + + /* set default count of the thread pool to 1 */ + tk->max_threads = 1; + if (task_fill_by_conf(config, tk) != 0) { + etmemd_log(ETMEMD_LOG_ERR, "fill task from configuration file fail.\n"); + free(tk); + return NULL; + } + return tk; +} + +void etmemd_remove_task(struct task *tk) +{ + clear_task_struct(tk); + free(tk); +} -- 2.27.0