diff --git a/tmm_driver/src/tmm_driver.c b/tmm_driver/src/tmm_driver.c index c8d85bda7519f85f73d1e2285b8255c0257b5e7a..b0864ac4ab82397489dd075c3685ca364b52dbba 100644 --- a/tmm_driver/src/tmm_driver.c +++ b/tmm_driver/src/tmm_driver.c @@ -12,6 +12,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chenzheng "); @@ -23,6 +24,21 @@ MODULE_DESCRIPTION("Get tmm base memory info module"); #define MAX_TMP_FORMAT_SIZE 100 #define ORDERED_PAGE_SIZE (1ULL << 21) +#define MBS_PER_GB 1024 +#define _2M_PER_GB 512 +#define _4K_PER_2M 512 +#define _4K_PER_GB (_2M_PER_GB * _4K_PER_2M) +#define REGULAR_SPLIT_GB 2 +#define _2M_UEFI 511 +#define _4K_UEFI (130 * 256) +#define ORDER_NINE 9 +#define MB_SHIFT 20 +#define DECIMAL 10 + +#define VIRTCCA_TTT_MAX 8192 +#define VIRTCCA_PAGE_ENTRY_NUM 512 +#define VIRTCCA_MAX_ENTRY_NUM (VIRTCCA_TTT_MAX * VIRTCCA_PAGE_ENTRY_NUM) + struct node_mem { uint32_t nr_free[MAX_PAGE_ORDER_NUM]; uint64_t node_mem_size; @@ -75,6 +91,9 @@ static is_virtcca_available_t is_virtcca_available_func = NULL; typedef uint64_t (*tmi_tmm_info_show_t)(uint64_t option, uint64_t tmm_info_addr); static tmi_tmm_info_show_t tmi_tmm_info_show_func = NULL; +static atomic_t g_mig_check_src_ok = ATOMIC_INIT(0); +static atomic_t g_mig_check_dst_ok = ATOMIC_INIT(0); + static struct kprobe kp_sym_lookup = { .symbol_name = "kallsyms_lookup_name", }; @@ -270,7 +289,7 @@ static ssize_t memory_info_show(struct kobject *kobj, memory_info_size = memory_info_format(&buf, memory_info); } else { memory_info_size = -1; - pr_err("tmm_drivr: unable to get memory_info\n"); + pr_err("tmm_driver: unable to get memory_info\n"); } kfree(memory_info); @@ -419,6 +438,333 @@ static ssize_t buddy_info_show(struct kobject *kobj, return buddy_info_size; } +static int mig_src_meta_data_check(tmm_memory_info_s *memory_info, uint64_t *mig_mem) +{ + int numa_index; + uint64_t total_memory = 0; + uint64_t gb_nums = 0; + uint64_t _2mb_nums = 0; + uint64_t ttt_meta_data_used = 0; + uint64_t meta_data_need = 0; + meta_datas_info_s *tx_ptr = NULL; + + for (numa_index = 0; numa_index < TMM_MAX_NODE_NUM; numa_index++) { + if (mig_mem[numa_index] > (memory_info->node_mems[numa_index].node_mem_size >> MB_SHIFT)) { + pr_err("tmm_driver: invalid input memory."); + return -EINVAL; + } + gb_nums += (mig_mem[numa_index] / MBS_PER_GB); + total_memory += mig_mem[numa_index]; + if (memory_info->node_mems[numa_index].node_mem_size == 0) { + if (mig_mem[numa_index]) { + pr_err("tmm_driver: invalid input memory."); + return -EINVAL; + } + continue; + } + + tx_ptr = &memory_info->ttt_mems[numa_index]; + ttt_meta_data_used += (tx_ptr->reserved_block_cnt + tx_ptr->extend_blocks_cnt) - + (tx_ptr->reserved_block_free_cnt + tx_ptr->extend_blocks_free_cnt); + } + + if (ttt_meta_data_used > VIRTCCA_MAX_ENTRY_NUM) { + pr_err("tmm_driver: error ttt_meta_data_used."); + return -EINVAL; + } + + if (gb_nums >= 1) + gb_nums--; + gb_nums = (gb_nums < REGULAR_SPLIT_GB) ? gb_nums : REGULAR_SPLIT_GB; + _2mb_nums = (total_memory - (gb_nums * MBS_PER_GB)) / 2 + _2M_UEFI; + meta_data_need = _2mb_nums * (_4K_PER_2M - 1) + gb_nums * (_4K_PER_GB - 1); + + if (meta_data_need > VIRTCCA_MAX_ENTRY_NUM - ttt_meta_data_used) { + pr_err("tmm_driver: Attention! Src meta data resource may not enough for migration."); + return ENOMEM; + } + + return 0; +} + +static int mig_dst_mem_check(tmm_memory_info_s *memory_info, uint64_t *mig_mem) +{ + uint64_t mem_node_free; + uint64_t cvm_used; + uint64_t meta_data_used; + int numa_index; + + for (numa_index = 0; numa_index < TMM_MAX_NODE_NUM; numa_index++) { + if (mig_mem[numa_index] == 0) + continue; + if (memory_info->node_mems[numa_index].node_mem_size == 0) { + pr_info("tmm_driver: Attention! Dst NUMA %d memory is not enough\n", numa_index); + return ENOMEM; + } + + mem_node_free = cal_numa_node_mem_info(memory_info, numa_index, &cvm_used, &meta_data_used); + pr_info("tmm_driver: mem_node_free = %llu MB\n", mem_node_free >> MB_SHIFT); + if ((mem_node_free >> MB_SHIFT) < mig_mem[numa_index]) { + pr_err("tmm_driver: Attention! Dst NUMA %d memory is not enough, excepted %llu MB, but only %llu MB\n", + numa_index, mig_mem[numa_index], mem_node_free >> MB_SHIFT); + return ENOMEM; + } + } + + return 0; +} + +static int mig_dst_page_check(tmm_memory_info_s *memory_info, uint64_t *mig_mem) +{ + int numa_index; + int node_index; + node_mem_s *node_mems = memory_info->node_mems; + uint64_t gb_nums = 0; + uint64_t mb_nums = 0; + uint64_t free_mb = 0; + bool is_first_numa = true; + + for (numa_index = 0; numa_index < TMM_MAX_NODE_NUM; numa_index++) { + if (mig_mem[numa_index] == 0) + continue; + if (memory_info->node_mems[numa_index].node_mem_size == 0) { + pr_info("tmm_driver: Attention! Dst NUMA %d page is not enough\n", numa_index); + return ENOMEM; + } + + if (is_first_numa) { + /* The first 1GB memory is used for data field, consistingof 4K and 2M pages */ + if (mig_mem[numa_index] <= MBS_PER_GB) + gb_nums = 0; + else + gb_nums = mig_mem[numa_index] / MBS_PER_GB - 1; + is_first_numa = false; + } else { + gb_nums = mig_mem[numa_index] / MBS_PER_GB; + } + mb_nums = mig_mem[numa_index] - gb_nums * MBS_PER_GB; + + if (node_mems[numa_index].nr_free[ORDER_NINE] > gb_nums) + continue; + else if (node_mems[numa_index].nr_free[ORDER_NINE] == gb_nums) { + for (node_index = 0; node_index < ORDER_NINE; node_index++) { + free_mb += node_mems[numa_index].nr_free[node_index] * (1 << (node_index + 1)); + } + + if (free_mb >= mb_nums) + continue; + + pr_err("tmm_driver: Attention! Dst NUMA %d page is not enough for migration.\n", numa_index); + return ENOMEM; + } + pr_err("tmm_driver: Attention! Dst NUMA %d page is not enough for migration.\n", numa_index); + return ENOMEM; + } + + return 0; +} + +static int mig_dst_meta_data_check(tmm_memory_info_s *memory_info, uint64_t *mig_mem) +{ + int numa_index; + uint64_t total_memory = 0; + uint64_t gb_nums = 0; + uint64_t _2mb_nums = 0; + uint64_t _4kb_nums = 0; + uint64_t ttt_meta_data_used = 0; + uint64_t meta_data_need = 0; + meta_datas_info_s *tx_ptr = NULL; + + for (numa_index = 0; numa_index < TMM_MAX_NODE_NUM; numa_index++) { + total_memory += mig_mem[numa_index]; + if (memory_info->node_mems[numa_index].node_mem_size == 0) { + continue; + } + gb_nums += (mig_mem[numa_index] / MBS_PER_GB); + tx_ptr = &memory_info->ttt_mems[numa_index]; + ttt_meta_data_used += (tx_ptr->reserved_block_cnt + tx_ptr->extend_blocks_cnt) - + (tx_ptr->reserved_block_free_cnt + tx_ptr->extend_blocks_free_cnt); + } + + if (ttt_meta_data_used > VIRTCCA_MAX_ENTRY_NUM) { + pr_err("tmm_driver: error ttt_meta_data_used %llu", ttt_meta_data_used); + return -EINVAL; + } + + if (gb_nums >= 1) + gb_nums--; + _2mb_nums = (total_memory - (gb_nums * MBS_PER_GB)) / 2 + _2M_UEFI; + _4kb_nums = _4K_UEFI; + meta_data_need = gb_nums + _2mb_nums + _4kb_nums; + + if (meta_data_need > VIRTCCA_MAX_ENTRY_NUM - ttt_meta_data_used) { + pr_info("tmm_driver: Attention! Dst meta data resource may not enough for migration."); + return ENOMEM; + } + + return 0; +} + +static int get_mig_info(const char *buf, tmm_memory_info_s *memory_info, uint64_t *mig_mem) +{ + uint64_t val = 0; + char *input_base = NULL; + char *input = NULL; + char *token = NULL; + int numa_index = 0; + int i = 0; + int ret = 0; + + memset(mig_mem, 0, sizeof(uint64_t) * TMM_MAX_NODE_NUM); + input = kstrdup(buf, GFP_KERNEL); + if (!input) + return -ENOMEM; + input_base = input; + pr_info("tmm_driver: migration memory: %s\n", input); + token = strsep(&input, " \t\n"); + while (token) { + if (strlen(token) == 0) { + token = strsep(&input, " \t\n"); + continue; + } else { + ret = kstrtoull(token, DECIMAL, &val); + if (ret != 0 || val < 0) { + pr_err("tmm_driver: Invalid input at position %d %s\n", i + 1, token); + ret = -EINVAL; + goto out; + } + if (i >= TMM_MAX_NODE_NUM) { + pr_err("tmm_driver: Reached max node number %d\n", i); + ret = -EINVAL; + goto out; + } + mig_mem[i] = val; + } + + token = strsep(&input, " \t\n"); + i++; + } + + if (i == 0) { + pr_err("tmm_driver: Unable to get valid input\n"); + ret = -EINVAL; + goto out; + } + + for (numa_index = 0; numa_index < TMM_MAX_NODE_NUM; numa_index++) { + pr_info("%d: mig_mem = %llu\n", numa_index, mig_mem[numa_index]); + } + + ret = get_tmm_memory_info(memory_info); + if (ret) + pr_err("tmm_driver: unable to get memory_info\n"); + +out: + if (input_base) + kfree(input_base); + + return ret; +} + +static ssize_t mig_check_src_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", atomic_read(&g_mig_check_src_ok)); +} + +static ssize_t mig_check_src_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + tmm_memory_info_s *memory_info = NULL; + uint64_t mig_mem[TMM_MAX_NODE_NUM]; + + atomic_set(&g_mig_check_src_ok, 0); + memory_info = kzalloc(sizeof(tmm_memory_info_s), GFP_KERNEL_ACCOUNT); + if (!memory_info) { + pr_err("tmm_driver: memory_info alloc failed\n"); + return -ENOMEM; + } + + ret = get_mig_info(buf, memory_info, mig_mem); + if (ret) { + pr_err("tmm_driver: get_mig_info failed\n"); + kfree(memory_info); + return ret; + } + + ret = mig_src_meta_data_check(memory_info, mig_mem); + if (ret != 0) + pr_err("tmm_driver: migration check failed\n"); + + kfree(memory_info); + if (ret == 0) { + pr_info("tmm_driver: src migration check pass\n"); + atomic_set(&g_mig_check_src_ok, 1); + } + + return count; +} + +static ssize_t mig_check_dst_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%d\n", atomic_read(&g_mig_check_dst_ok)); +} + +static ssize_t mig_check_dst_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + uint64_t mig_mem[TMM_MAX_NODE_NUM]; + tmm_memory_info_s *memory_info = NULL; + + atomic_set(&g_mig_check_dst_ok, 0); + memory_info = kzalloc(sizeof(tmm_memory_info_s), GFP_KERNEL_ACCOUNT); + if (!memory_info) { + pr_err("tmm_driver: memory_info alloc failed\n"); + return -ENOMEM; + } + + ret = get_mig_info(buf, memory_info, mig_mem); + if (ret) { + pr_err("tmm_driver: get_mig_info failed\n"); + kfree(memory_info); + return ret; + } + + ret = mig_dst_mem_check(memory_info, mig_mem); + if (ret) { + pr_err("tmm_driver: migration check failed\n"); + goto out; + } + + ret = mig_dst_page_check(memory_info, mig_mem); + if (ret) { + pr_err("tmm_driver: migration check failed\n"); + goto out; + } + + ret = mig_dst_meta_data_check(memory_info, mig_mem); + if (ret) { + pr_err("tmm_driver: migration check failed\n"); + goto out; + } + +out: + if (memory_info) + kfree(memory_info); + if (ret == 0) { + pr_info("tmm_driver: dst migration check pass\n"); + atomic_set(&g_mig_check_dst_ok, 1); + } + + return count; +} + static struct kobj_attribute virtcca_enabled_attr = __ATTR_RO(virtcca_enabled); static struct kobj_attribute memory_info_attr = __ATTR_RO(memory_info); static struct kobj_attribute slab_info_attr = __ATTR_RO(slab_info); @@ -426,6 +772,11 @@ static struct kobj_attribute buddy_info_attr = __ATTR_RO(buddy_info); static struct kobj_attribute kae_vf_nums_attr = __ATTR_RO(kae_vf_nums); static struct kobject *tmm_kobj; +/* helper sysfs for cvm live migration */ +static struct kobj_attribute mig_check_src_attr = __ATTR_RW(mig_check_src); +static struct kobj_attribute mig_check_dst_attr = __ATTR_RW(mig_check_dst); +static struct kobject *tmm_mig_kobj; + static struct attribute *tmm_attrs[] = { &virtcca_enabled_attr.attr, &memory_info_attr.attr, @@ -435,6 +786,15 @@ static struct attribute *tmm_attrs[] = { NULL, }; +static struct attribute *tmm_mig_attrs[] = { + &mig_check_src_attr.attr, + &mig_check_dst_attr.attr, + NULL, +}; + +static int tmm_mig_driver_init(void); +static void tmm_mig_driver_exit(void); + static int __init tmm_driver_init(void) { int rc, i; @@ -453,6 +813,10 @@ static int __init tmm_driver_init(void) } } + rc = tmm_mig_driver_init(); + if (rc) + goto err; + rc = get_symbol_from_kernel(); if (rc) goto err; @@ -460,6 +824,7 @@ static int __init tmm_driver_init(void) return 0; err: + tmm_mig_driver_exit(); for (--i; i >= 0; i--) { sysfs_remove_file(tmm_kobj, tmm_attrs[i]); } @@ -473,6 +838,7 @@ static void __exit tmm_driver_exit(void) { int i; + tmm_mig_driver_exit(); for (i = 0; tmm_attrs[i] != NULL; i++) { sysfs_remove_file(tmm_kobj, tmm_attrs[i]); } @@ -481,5 +847,39 @@ static void __exit tmm_driver_exit(void) printk(KERN_INFO "tmm_driver_exit!\n"); } +static int tmm_mig_driver_init(void) +{ + int rc = 0; + + if (tmm_kobj == NULL) { + pr_err("tmm_driver: create tmm_mig kobject failed\n"); + return -EINVAL; + } + + tmm_mig_kobj = kobject_create_and_add("migration", tmm_kobj); + if (!tmm_mig_kobj) { + pr_err("tmm_driver: create tmm_mig kobject failed\n"); + return -ENOMEM; + } + + for (int i = 0; tmm_mig_attrs[i] != NULL; i++) { + rc = sysfs_create_file(tmm_mig_kobj, tmm_mig_attrs[i]); + if (rc) { + pr_err("tmm_driver: unable to create sysfs file (%d)\n", rc); + return rc; + } + } + + return 0; +} + +static void tmm_mig_driver_exit(void) +{ + for (int i = 0; tmm_mig_attrs[i] != NULL; i++) { + sysfs_remove_file(tmm_mig_kobj, tmm_mig_attrs[i]); + } + + kobject_put(tmm_mig_kobj); +} module_init(tmm_driver_init); module_exit(tmm_driver_exit);