#include #include #include #include #include "lve_internal.h" #include "lve_debug.h" #include "light_ve.h" #include "resource.h" rwlock_t lvp_lock; LIST_HEAD(lvp_list); struct lvp_ve_private *root_lvp; static struct kmem_cache *lvp_cache; void lvp_fini(struct lvp_ve_private *lvp) { LVE_ENTER("\n"); if (lvp == NULL) { LVE_ERR("lvp is NULL\n"); return; } lvp_exec_fini(lvp); lve_unlink(lvp, LVE_UNLINK_ALL, 0); /* remove dirs. XXX simplify init/fini with other lve */ lvp_proc_fini(lvp); write_lock(&lvp_lock); list_del_init(&lvp->lvp_link); write_unlock(&lvp_lock); os_lvp_fini(lvp); LVE_DBG("lvp put %d\n", atomic_read(&lvp->lvp_default->lve_refcnt)); light_ve_put(lvp->lvp_default); } void lvp_free(struct lvp_ve_private *lvp) { LVE_ENTER("%px\n", lvp); if (lvp == NULL) return; /* for ROOT_LVP we already released the reference in lve_lvp_init */ if (lvp->lvp_id != ROOT_LVP) module_put(THIS_MODULE); kmem_cache_free(lvp_cache, lvp); } /* XXX should have same protection as lve list ? */ static struct lvp_ve_private *__lvp_find(uint32_t id) { struct lvp_ve_private *lvp; list_for_each_entry(lvp, &lvp_list, lvp_link) { if (lvp->lvp_id == id) { LVE_DBG("lvp id=%u already exists list\n", lvp->lvp_id); return lvp; } } return NULL; } #ifndef LVE_PER_VE struct lvp_ve_private *lvp_find(uint32_t id) { struct lvp_ve_private *ret; read_lock(&lvp_lock); ret = __lvp_find(id); if (ret != NULL) lvp_get(ret); read_unlock(&lvp_lock); if (ret == NULL) return NULL; LVE_FAIL_RACE(LVE_FAIL_LVP_CREATE_RACE); wait_for_completion(&ret->lvp_init); if (ret->lvp_init_status != 0 || ret->lvp_destroy) { lvp_put(ret); ret = NULL; } return ret; } #else struct lvp_ve_private *lvp_find(uint32_t id) { struct lvp_ve_private *lvp = TASK_VE_PRIVATE(current); if (id != lvp->lvp_id) return NULL; lvp_get(root_lvp); return root_lvp; } #endif struct lvp_ve_private *lvp_alloc(uint32_t id, void *data) { struct lvp_ve_private *lvp; struct lvp_ve_private *lvp_old; int rc = 0; lvp = lve_call(kmem_cache_alloc(lvp_cache, GFP_KERNEL), LVE_FAIL_ALLOC_LVP, NULL); if (lvp == NULL) { LVE_ERR("can't allocate LVP #%u\n", id); lvp = ERR_PTR(-ENOMEM); goto trace; } if (!try_module_get(THIS_MODULE)) { LVE_ERR("Can't get module !\n"); kmem_cache_free(lvp_cache, lvp); lvp = ERR_PTR(-EINVAL); goto trace; } memset(lvp, 0, sizeof(struct lvp_ve_private) + os_lvp_private_sz()); lvp->lvp_default = lve_alloc(lvp, SELF_LVE); if (IS_ERR(lvp->lvp_default)) { lvp = (void *)lvp->lvp_default; kmem_cache_free(lvp_cache, lvp); goto trace; } INIT_LIST_HEAD(&lvp->lvp_lve_list); INIT_LIST_HEAD(&lvp->lvp_link); init_completion(&lvp->lvp_init); init_rwsem(&lvp->lvp_destroy_lock); lvp->lvp_id = id; write_lock(&lvp_lock); lvp_old = __lvp_find(id); if (lvp_old != NULL) { write_unlock(&lvp_lock); lvp_put(lvp); LVE_FAIL_RACE(LVE_FAIL_LVP_CREATE_RACE); lvp = ERR_PTR(-EEXIST); goto trace; } list_add_tail(&lvp->lvp_link, &lvp_list); write_unlock(&lvp_lock); LVE_FAIL_RACE(LVE_FAIL_LVP_CREATE_RACE); /* lve_alloc don't able to take a ref due lvp_default NULL, * let's do it after assing */ lvp_get(lvp); /* lvp_fini balance, avoid free with lve_unlink */ light_ve_get(lvp->lvp_default); #ifndef LVE_PER_VE if (id == ROOT_LVP) { INIT_RADIX_TREE(&lvp_tree(lvp), GFP_ATOMIC); init_rwsem(&lvp_tree_lock(lvp)); } #else INIT_RADIX_TREE(&lvp_tree(lvp), GFP_ATOMIC); init_rwsem(&lvp_tree_lock(lvp)); #endif lvp->lvp_def_limits[LIM_CPU] = MAX_CPU/10; /* 10% */ lvp->lvp_def_limits[LIM_IO] = 0; lvp->lvp_def_limits[LIM_IOPS] = 0; lvp->lvp_def_limits[LIM_ENTER] = 10; lvp->lvp_def_limits[LIM_MEMORY] = 0; lvp->lvp_def_limits[LIM_CPU_WEIGHT] = 100; lvp_exec_init(lvp); lve_namespace_init(lvp->lvp_default); lve_stat_init(&lvp->lvp_default->lve_stats); lve_net_init(lvp->lvp_default); if ((rc = lvp_proc_init(lvp)) < 0) goto err; /* XXX why lve_default isn't init as other lve ? * lve_resource_init + ... */ if ((rc = os_lvp_init(lvp, data)) < 0) goto err; lve_stats_dir_init(lvp->lvp_default); #ifndef LVE_PER_VE if (lvp->lvp_id != ROOT_LVP) if ((rc = lve_namespace_setup(root_lvp, lvp->lvp_default)) != 0) goto err; #endif /* balanced with put in lvp_alloc ioctl */ lvp_get(lvp); out: lvp->lvp_init_status = rc; if (lvp->lvp_default) { light_ve_get(lvp->lvp_default); // for finish_init lve_finish_init(lvp->lvp_default, rc); } complete_all(&lvp->lvp_init); trace: LVE_DBG("alloc return %px\n", lvp); trace_lvp_alloc(id, lvp); return lvp; err: lvp_fini(lvp); goto out; } int lve_lvp_init() { int ret; rwlock_init(&lvp_lock); lvp_cache = lve_call(kmem_cache_create("lvp_cache", sizeof(struct lvp_ve_private) + os_lvp_private_sz(), 0, 0, NULL), LVE_FAIL_ALLOC_LVP_CACHE, NULL); if (lvp_cache == NULL) return -ENOMEM; root_lvp = lvp_alloc(ROOT_LVP, NULL); if (IS_ERR(root_lvp)) return PTR_ERR(root_lvp); ret = root_lvp->lvp_init_status; lvp_put(root_lvp); /* Decrement the reference for root_lvp */ module_put(THIS_MODULE); return ret; } void lvp_root_fini() { if (root_lvp) { lvp_fini(root_lvp); lvp_put(root_lvp); } } void lve_lvp_fini() { if (lvp_cache == NULL) { LVE_ERR("lvp_cache is NULL\n"); return; } kmem_cache_destroy(lvp_cache); } int lvp_destroy(uint32_t id) { #ifndef LVE_PER_VE struct lvp_ve_private *lvp = NULL; bool must_fini; if (id == ROOT_LVP) return -EINVAL; lvp = lvp_find(id); if (lvp == NULL) return -ESRCH; LVE_FAIL_RACE(LVE_FAIL_LVP_DESTROY_RACE); /* sync between destroy's */ /* sync with init thread */ down_write(&lvp->lvp_destroy_lock); must_fini = lvp->lvp_destroy == 0; lvp->lvp_destroy = 1; up_write(&lvp->lvp_destroy_lock); if (must_fini) { lvp_fini(lvp); lvp_put(lvp); // from list } lvp_put(lvp); // from find to free. return 0; #else return -ENOSYS; #endif }