blob: cb346e9a72b95fca8237a5db258a2d0d957a6ac1 [file] [log] [blame]
/* 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 */
for (int i = 0; i < num_cores; i++) {
if (is_ll_core(i))
continue;
#ifdef CONFIG_DISABLE_SMT
/* Remove all odd cores from consideration for allocation. */
if (i % 2 == 1)
continue;
#endif /* CONFIG_DISABLE_SMT */
TAILQ_INSERT_TAIL(&idlecores, pcoreid2spc(i), alloc_next);
}
}
/* 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]);
}
/* 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);
}