| /* Copyright (c) 2009, 2012, 2015 The Regents of the University of California |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * Valmon Leymarie <leymariv@berkeley.edu> |
| * Kevin Klues <klueska@cs.berkeley.edu> |
| * See LICENSE for details. |
| */ |
| |
| #include <arch/topology.h> |
| #include <sys/queue.h> |
| #include <env.h> |
| #include <corerequest.h> |
| #include <kmalloc.h> |
| |
| /* The pcores in the system. (array gets alloced in init()). */ |
| struct sched_pcore *all_pcores; |
| |
| /* TAILQ of all unallocated, idle (CG) cores */ |
| struct sched_pcore_tailq idlecores = TAILQ_HEAD_INITIALIZER(idlecores); |
| |
| /* Initialize any data assocaited with doing core allocation. */ |
| void corealloc_init(void) |
| { |
| /* Allocate all of our pcores. */ |
| all_pcores = kzmalloc(sizeof(struct sched_pcore) * num_cores, 0); |
| /* init the idlecore list. if they turned off hyperthreading, give them the |
| * odds from 1..max-1. otherwise, give them everything by 0 (default mgmt |
| * core). TODO: (CG/LL) better LL/CG mgmt */ |
| #ifndef CONFIG_DISABLE_SMT |
| for (int i = 0; i < num_cores; i++) |
| if (!is_ll_core(i)) |
| TAILQ_INSERT_TAIL(&idlecores, pcoreid2spc(i), alloc_next); |
| #else |
| assert(!(num_cores % 2)); |
| /* TODO: rethink starting at 1 here. If SMT is really disabled, the entire |
| * core of an "ll" core shouldn't be available. */ |
| for (int i = 1; i < num_cores; i += 2) |
| if (!is_ll_core(i)) |
| TAILQ_INSERT_TAIL(&idlecores, pcoreid2spc(i), alloc_next); |
| #endif /* CONFIG_DISABLE_SMT */ |
| } |
| |
| /* Initialize any data associated with allocating cores to a process. */ |
| void corealloc_proc_init(struct proc *p) |
| { |
| TAILQ_INIT(&p->ksched_data.crd.prov_alloc_me); |
| TAILQ_INIT(&p->ksched_data.crd.prov_not_alloc_me); |
| } |
| |
| /* Find the best core to allocate to a process as dictated by the core |
| * allocation algorithm. This code assumes that the scheduler that uses it |
| * holds a lock for the duration of the call. */ |
| uint32_t __find_best_core_to_alloc(struct proc *p) |
| { |
| struct sched_pcore *spc_i = NULL; |
| |
| spc_i = TAILQ_FIRST(&p->ksched_data.crd.prov_not_alloc_me); |
| if (!spc_i) |
| spc_i = TAILQ_FIRST(&idlecores); |
| if (!spc_i) |
| return -1; |
| return spc2pcoreid(spc_i); |
| } |
| |
| /* Track the pcore properly when it is allocated to p. This code assumes that |
| * the scheduler that uses it holds a lock for the duration of the call. */ |
| void __track_core_alloc(struct proc *p, uint32_t pcoreid) |
| { |
| struct sched_pcore *spc; |
| |
| assert(pcoreid < num_cores); /* catch bugs */ |
| spc = pcoreid2spc(pcoreid); |
| assert(spc->alloc_proc != p); /* corruption or double-alloc */ |
| spc->alloc_proc = p; |
| /* if the pcore is prov to them and now allocated, move lists */ |
| if (spc->prov_proc == p) { |
| TAILQ_REMOVE(&p->ksched_data.crd.prov_not_alloc_me, spc, prov_next); |
| TAILQ_INSERT_TAIL(&p->ksched_data.crd.prov_alloc_me, spc, prov_next); |
| } |
| /* Actually allocate the core, removing it from the idle core list. */ |
| TAILQ_REMOVE(&idlecores, spc, alloc_next); |
| } |
| |
| /* Track the pcore properly when it is deallocated from p. This code assumes |
| * that the scheduler that uses it holds a lock for the duration of the call. |
| * */ |
| void __track_core_dealloc(struct proc *p, uint32_t pcoreid) |
| { |
| struct sched_pcore *spc; |
| |
| assert(pcoreid < num_cores); /* catch bugs */ |
| spc = pcoreid2spc(pcoreid); |
| spc->alloc_proc = 0; |
| /* if the pcore is prov to them and now deallocated, move lists */ |
| if (spc->prov_proc == p) { |
| TAILQ_REMOVE(&p->ksched_data.crd.prov_alloc_me, spc, prov_next); |
| /* this is the victim list, which can be sorted so that we pick the |
| * right victim (sort by alloc_proc reverse priority, etc). In this |
| * case, the core isn't alloc'd by anyone, so it should be the first |
| * victim. */ |
| TAILQ_INSERT_HEAD(&p->ksched_data.crd.prov_not_alloc_me, spc, |
| prov_next); |
| } |
| /* Actually dealloc the core, putting it back on the idle core list. */ |
| TAILQ_INSERT_TAIL(&idlecores, spc, alloc_next); |
| } |
| |
| /* Bulk interface for __track_core_dealloc */ |
| void __track_core_dealloc_bulk(struct proc *p, uint32_t *pc_arr, |
| uint32_t nr_cores) |
| { |
| for (int i = 0; i < nr_cores; i++) |
| __track_core_dealloc(p, pc_arr[i]); |
| } |
| |
| /* Get an idle core from our pcore list and return its core_id. Don't |
| * consider the chosen core in the future when handing out cores to a |
| * process. This code assumes that the scheduler that uses it holds a lock |
| * for the duration of the call. This will not give out provisioned cores. */ |
| int __get_any_idle_core(void) |
| { |
| struct sched_pcore *spc; |
| int ret = -1; |
| |
| while ((spc = TAILQ_FIRST(&idlecores))) { |
| /* Don't take cores that are provisioned to a process */ |
| if (spc->prov_proc) |
| continue; |
| assert(!spc->alloc_proc); |
| TAILQ_REMOVE(&idlecores, spc, alloc_next); |
| ret = spc2pcoreid(spc); |
| break; |
| } |
| return ret; |
| } |
| |
| /* Detect if a pcore is idle or not. */ |
| /* TODO: if we end up using this a lot, track CG-idleness as a property of |
| * the SPC instead of doing a linear search. */ |
| static bool __spc_is_idle(struct sched_pcore *spc) |
| { |
| struct sched_pcore *i; |
| |
| TAILQ_FOREACH(i, &idlecores, alloc_next) { |
| if (spc == i) |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| /* Same as __get_any_idle_core() except for a specific core id. */ |
| int __get_specific_idle_core(int coreid) |
| { |
| struct sched_pcore *spc = pcoreid2spc(coreid); |
| int ret = -1; |
| |
| assert((coreid >= 0) && (coreid < num_cores)); |
| if (__spc_is_idle(pcoreid2spc(coreid)) && !spc->prov_proc) { |
| assert(!spc->alloc_proc); |
| TAILQ_REMOVE(&idlecores, spc, alloc_next); |
| ret = coreid; |
| } |
| return ret; |
| } |
| |
| /* Reinsert a core obtained via __get_any_idle_core() or |
| * __get_specific_idle_core() back into the idlecore map. This code assumes |
| * that the scheduler that uses it holds a lock for the duration of the call. |
| * This will not give out provisioned cores. */ |
| void __put_idle_core(int coreid) |
| { |
| struct sched_pcore *spc = pcoreid2spc(coreid); |
| |
| assert((coreid >= 0) && (coreid < num_cores)); |
| TAILQ_INSERT_TAIL(&idlecores, spc, alloc_next); |
| } |
| |
| /* One off function to make 'pcoreid' the next core chosen by the core |
| * allocation algorithm (so long as no provisioned cores are still idle). |
| * This code assumes that the scheduler that uses it holds a lock for the |
| * duration of the call. */ |
| void __next_core_to_alloc(uint32_t pcoreid) |
| { |
| struct sched_pcore *spc_i; |
| bool match = FALSE; |
| |
| TAILQ_FOREACH(spc_i, &idlecores, alloc_next) { |
| if (spc2pcoreid(spc_i) == pcoreid) { |
| match = TRUE; |
| break; |
| } |
| } |
| if (match) { |
| TAILQ_REMOVE(&idlecores, spc_i, alloc_next); |
| TAILQ_INSERT_HEAD(&idlecores, spc_i, alloc_next); |
| printk("Pcore %d will be given out next (from the idles)\n", pcoreid); |
| } |
| } |
| |
| /* One off function to sort the idle core list for debugging in the kernel |
| * monitor. This code assumes that the scheduler that uses it holds a lock |
| * for the duration of the call. */ |
| void __sort_idle_cores(void) |
| { |
| struct sched_pcore *spc_i, *spc_j, *temp; |
| struct sched_pcore_tailq sorter = TAILQ_HEAD_INITIALIZER(sorter); |
| bool added; |
| |
| TAILQ_CONCAT(&sorter, &idlecores, alloc_next); |
| TAILQ_FOREACH_SAFE(spc_i, &sorter, alloc_next, temp) { |
| TAILQ_REMOVE(&sorter, spc_i, alloc_next); |
| added = FALSE; |
| /* don't need foreach_safe since we break after we muck with the list */ |
| TAILQ_FOREACH(spc_j, &idlecores, alloc_next) { |
| if (spc_i < spc_j) { |
| TAILQ_INSERT_BEFORE(spc_j, spc_i, alloc_next); |
| added = TRUE; |
| break; |
| } |
| } |
| if (!added) |
| TAILQ_INSERT_TAIL(&idlecores, spc_i, alloc_next); |
| } |
| } |
| |
| /* Print the map of idle cores that are still allocatable through our core |
| * allocation algorithm. */ |
| void print_idle_core_map(void) |
| { |
| struct sched_pcore *spc_i; |
| /* not locking, so we can look at this without deadlocking. */ |
| printk("Idle cores (unlocked!):\n"); |
| TAILQ_FOREACH(spc_i, &idlecores, alloc_next) |
| printk("Core %d, prov to %d (%p)\n", spc2pcoreid(spc_i), |
| spc_i->prov_proc ? spc_i->prov_proc->pid : 0, spc_i->prov_proc); |
| } |