Skip to content

Commit

Permalink
Implement PAPR VPA functions for pSeries shared processor partitions
Browse files Browse the repository at this point in the history
Shared-processor partitions are those where a CPU is time-sliced between
partitions, rather than being permanently dedicated to a single
partition.  qemu emulated partitions, since they are just scheduled with
the qemu user process, behave mostly like shared processor partitions.

In order to better support shared processor partitions (splpar), PAPR
defines the "VPA" (Virtual Processor Area), a shared memory communication
channel between the hypervisor and partitions.  There are also two
additional shared memory communication areas for specialized purposes
associated with the VPA.

A VPA is not essential for operating an splpar, though it can be necessary
for obtaining accurate performance measurements in the presence of
runtime partition switching.

Most importantly, however, the VPA is a prerequisite for PAPR's H_CEDE,
hypercall, which allows a partition OS to give up it's shared processor
timeslices to other partitions when idle.

This patch implements the VPA and H_CEDE hypercalls in qemu.  We don't
implement any of the more advanced statistics which can be communicated
through the VPA.  However, this is enough to make normal pSeries kernels
do an effective power-save idle on an emulated pSeries, significantly
reducing the host load of a qemu emulated pSeries running an idle guest OS.

Signed-off-by: David Gibson <[email protected]>
Signed-off-by: Alexander Graf <[email protected]>
  • Loading branch information
dgibson authored and agraf committed Apr 1, 2011
1 parent 08942ac commit ed12005
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 1 deletion.
2 changes: 1 addition & 1 deletion hw/spapr.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
"\0hcall-tce\0hcall-vio";
"\0hcall-tce\0hcall-vio\0hcall-splpar";
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
int i;
char *modelname;
Expand Down
192 changes: 192 additions & 0 deletions hw/spapr_hcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "sysemu.h"
#include "qemu-char.h"
#include "exec-all.h"
#include "exec.h"
#include "helper_regs.h"
#include "hw/spapr.h"

#define HPTES_PER_GROUP 8
Expand Down Expand Up @@ -255,6 +257,192 @@ static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr,
return H_HARDWARE;
}

#define FLAGS_REGISTER_VPA 0x0000200000000000ULL
#define FLAGS_REGISTER_DTL 0x0000400000000000ULL
#define FLAGS_REGISTER_SLBSHADOW 0x0000600000000000ULL
#define FLAGS_DEREGISTER_VPA 0x0000a00000000000ULL
#define FLAGS_DEREGISTER_DTL 0x0000c00000000000ULL
#define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL

#define VPA_MIN_SIZE 640
#define VPA_SIZE_OFFSET 0x4
#define VPA_SHARED_PROC_OFFSET 0x9
#define VPA_SHARED_PROC_VAL 0x2

static target_ulong register_vpa(CPUState *env, target_ulong vpa)
{
uint16_t size;
uint8_t tmp;

if (vpa == 0) {
hcall_dprintf("Can't cope with registering a VPA at logical 0\n");
return H_HARDWARE;
}

if (vpa % env->dcache_line_size) {
return H_PARAMETER;
}
/* FIXME: bounds check the address */

size = lduw_phys(vpa + 0x4);

if (size < VPA_MIN_SIZE) {
return H_PARAMETER;
}

/* VPA is not allowed to cross a page boundary */
if ((vpa / 4096) != ((vpa + size - 1) / 4096)) {
return H_PARAMETER;
}

env->vpa = vpa;

tmp = ldub_phys(env->vpa + VPA_SHARED_PROC_OFFSET);
tmp |= VPA_SHARED_PROC_VAL;
stb_phys(env->vpa + VPA_SHARED_PROC_OFFSET, tmp);

return H_SUCCESS;
}

static target_ulong deregister_vpa(CPUState *env, target_ulong vpa)
{
if (env->slb_shadow) {
return H_RESOURCE;
}

if (env->dispatch_trace_log) {
return H_RESOURCE;
}

env->vpa = 0;
return H_SUCCESS;
}

static target_ulong register_slb_shadow(CPUState *env, target_ulong addr)
{
uint32_t size;

if (addr == 0) {
hcall_dprintf("Can't cope with SLB shadow at logical 0\n");
return H_HARDWARE;
}

size = ldl_phys(addr + 0x4);
if (size < 0x8) {
return H_PARAMETER;
}

if ((addr / 4096) != ((addr + size - 1) / 4096)) {
return H_PARAMETER;
}

if (!env->vpa) {
return H_RESOURCE;
}

env->slb_shadow = addr;

return H_SUCCESS;
}

static target_ulong deregister_slb_shadow(CPUState *env, target_ulong addr)
{
env->slb_shadow = 0;
return H_SUCCESS;
}

static target_ulong register_dtl(CPUState *env, target_ulong addr)
{
uint32_t size;

if (addr == 0) {
hcall_dprintf("Can't cope with DTL at logical 0\n");
return H_HARDWARE;
}

size = ldl_phys(addr + 0x4);

if (size < 48) {
return H_PARAMETER;
}

if (!env->vpa) {
return H_RESOURCE;
}

env->dispatch_trace_log = addr;
env->dtl_size = size;

return H_SUCCESS;
}

static target_ulong deregister_dtl(CPUState *emv, target_ulong addr)
{
env->dispatch_trace_log = 0;
env->dtl_size = 0;

return H_SUCCESS;
}

static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong flags = args[0];
target_ulong procno = args[1];
target_ulong vpa = args[2];
target_ulong ret = H_PARAMETER;
CPUState *tenv;

for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) {
if (tenv->cpu_index == procno) {
break;
}
}

if (!tenv) {
return H_PARAMETER;
}

switch (flags) {
case FLAGS_REGISTER_VPA:
ret = register_vpa(tenv, vpa);
break;

case FLAGS_DEREGISTER_VPA:
ret = deregister_vpa(tenv, vpa);
break;

case FLAGS_REGISTER_SLBSHADOW:
ret = register_slb_shadow(tenv, vpa);
break;

case FLAGS_DEREGISTER_SLBSHADOW:
ret = deregister_slb_shadow(tenv, vpa);
break;

case FLAGS_REGISTER_DTL:
ret = register_dtl(tenv, vpa);
break;

case FLAGS_DEREGISTER_DTL:
ret = deregister_dtl(tenv, vpa);
break;
}

return ret;
}

static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
env->msr |= (1ULL << MSR_EE);
hreg_compute_hflags(env);
if (!cpu_has_work(env)) {
env->halted = 1;
}
return H_SUCCESS;
}

static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
Expand Down Expand Up @@ -327,6 +515,10 @@ static void hypercall_init(void)
/* hcall-dabr */
spapr_register_hypercall(H_SET_DABR, h_set_dabr);

/* hcall-splpar */
spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
spapr_register_hypercall(H_CEDE, h_cede);

/* qemu/KVM-PPC specific hcalls */
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
}
Expand Down
7 changes: 7 additions & 0 deletions target-ppc/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,13 @@ struct CPUPPCState {
uint32_t flags;
uint64_t insns_flags;

#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
target_phys_addr_t vpa;
target_phys_addr_t slb_shadow;
target_phys_addr_t dispatch_trace_log;
uint32_t dtl_size;
#endif /* TARGET_PPC64 */

int error_code;
uint32_t pending_interrupts;
#if !defined(CONFIG_USER_ONLY)
Expand Down

0 comments on commit ed12005

Please sign in to comment.