Skip to content

Commit

Permalink
[SPARC64]: Add hypervisor API negotiation and fix console bugs.
Browse files Browse the repository at this point in the history
Hypervisor interfaces need to be negotiated in order to use
some API calls reliably.  So add a small set of interfaces
to request API versions and query current settings.

This allows us to fix some bugs in the hypervisor console:

1) If we can negotiate API group CORE of at least major 1
   minor 1 we can use con_read and con_write which can improve
   console performance quite a bit.

2) When we do a console write request, we should hold the
   spinlock around the whole request, not a byte at a time.
   What would happen is that it's easy for output from
   different cpus to get mixed with each other.

3) Use consistent udelay() based polling, udelay(1) each
   loop with a limit of 1000 polls to handle stuck hypervisor
   console.

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
David S. Miller committed May 16, 2007
1 parent 7b104bc commit c7754d4
Show file tree
Hide file tree
Showing 6 changed files with 574 additions and 73 deletions.
2 changes: 1 addition & 1 deletion arch/sparc64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \
irq.o ptrace.o time.o sys_sparc.o signal.o \
unaligned.o central.o pci.o starfire.o semaphore.o \
power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \
visemul.o prom.o of_device.o
visemul.o prom.o of_device.o hvapi.o

obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
Expand Down
94 changes: 94 additions & 0 deletions arch/sparc64/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -1843,3 +1843,97 @@ sun4v_cpu_state:
mov %o1, %o0
1: retl
nop

/* %o0: API group number
* %o1: pointer to unsigned long major number storage
* %o2: pointer to unsigned long minor number storage
*
* returns %o0: status
*/
.globl sun4v_get_version
sun4v_get_version:
mov HV_CORE_GET_VER, %o5
mov %o1, %o3
mov %o2, %o4
ta HV_CORE_TRAP
stx %o1, [%o3]
retl
stx %o2, [%o4]

/* %o0: API group number
* %o1: desired major number
* %o2: desired minor number
* %o3: pointer to unsigned long actual minor number storage
*
* returns %o0: status
*/
.globl sun4v_set_version
sun4v_set_version:
mov HV_CORE_SET_VER, %o5
mov %o3, %o4
ta HV_CORE_TRAP
retl
stx %o1, [%o4]

/* %o0: pointer to unsigned long status
*
* returns %o0: signed character
*/
.globl sun4v_con_getchar
sun4v_con_getchar:
mov %o0, %o4
mov HV_FAST_CONS_GETCHAR, %o5
clr %o0
clr %o1
ta HV_FAST_TRAP
stx %o0, [%o4]
retl
sra %o1, 0, %o0

/* %o0: signed long character
*
* returns %o0: status
*/
.globl sun4v_con_putchar
sun4v_con_putchar:
mov HV_FAST_CONS_PUTCHAR, %o5
ta HV_FAST_TRAP
retl
sra %o0, 0, %o0

/* %o0: buffer real address
* %o1: buffer size
* %o2: pointer to unsigned long bytes_read
*
* returns %o0: status
*/
.globl sun4v_con_read
sun4v_con_read:
mov %o2, %o4
mov HV_FAST_CONS_READ, %o5
ta HV_FAST_TRAP
brnz %o0, 1f
cmp %o1, -1 /* break */
be,a,pn %icc, 1f
mov %o1, %o0
cmp %o1, -2 /* hup */
be,a,pn %icc, 1f
mov %o1, %o0
stx %o1, [%o4]
1: retl
nop

/* %o0: buffer real address
* %o1: buffer size
* %o2: pointer to unsigned long bytes_written
*
* returns %o0: status
*/
.globl sun4v_con_write
sun4v_con_write:
mov %o2, %o4
mov HV_FAST_CONS_WRITE, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
189 changes: 189 additions & 0 deletions arch/sparc64/kernel/hvapi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/* hvapi.c: Hypervisor API management.
*
* Copyright (C) 2007 David S. Miller <[email protected]>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>

#include <asm/hypervisor.h>
#include <asm/oplib.h>

/* If the hypervisor indicates that the API setting
* calls are unsupported, by returning HV_EBADTRAP or
* HV_ENOTSUPPORTED, we assume that API groups with the
* PRE_API flag set are major 1 minor 0.
*/
struct api_info {
unsigned long group;
unsigned long major;
unsigned long minor;
unsigned int refcnt;
unsigned int flags;
#define FLAG_PRE_API 0x00000001
};

static struct api_info api_table[] = {
{ .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API },
{ .group = HV_GRP_CORE, .flags = FLAG_PRE_API },
{ .group = HV_GRP_INTR, },
{ .group = HV_GRP_SOFT_STATE, },
{ .group = HV_GRP_PCI, .flags = FLAG_PRE_API },
{ .group = HV_GRP_LDOM, },
{ .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API },
{ .group = HV_GRP_NCS, .flags = FLAG_PRE_API },
{ .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API },
{ .group = HV_GRP_FIRE_PERF, },
{ .group = HV_GRP_DIAG, .flags = FLAG_PRE_API },
};

static DEFINE_SPINLOCK(hvapi_lock);

static struct api_info *__get_info(unsigned long group)
{
int i;

for (i = 0; i < ARRAY_SIZE(api_table); i++) {
if (api_table[i].group == group)
return &api_table[i];
}
return NULL;
}

static void __get_ref(struct api_info *p)
{
p->refcnt++;
}

static void __put_ref(struct api_info *p)
{
if (--p->refcnt == 0) {
unsigned long ignore;

sun4v_set_version(p->group, 0, 0, &ignore);
p->major = p->minor = 0;
}
}

/* Register a hypervisor API specification. It indicates the
* API group and desired major+minor.
*
* If an existing API registration exists '0' (success) will
* be returned if it is compatible with the one being registered.
* Otherwise a negative error code will be returned.
*
* Otherwise an attempt will be made to negotiate the requested
* API group/major/minor with the hypervisor, and errors returned
* if that does not succeed.
*/
int sun4v_hvapi_register(unsigned long group, unsigned long major,
unsigned long *minor)
{
struct api_info *p;
unsigned long flags;
int ret;

spin_lock_irqsave(&hvapi_lock, flags);
p = __get_info(group);
ret = -EINVAL;
if (p) {
if (p->refcnt) {
ret = -EINVAL;
if (p->major == major) {
*minor = p->minor;
ret = 0;
}
} else {
unsigned long actual_minor;
unsigned long hv_ret;

hv_ret = sun4v_set_version(group, major, *minor,
&actual_minor);
ret = -EINVAL;
if (hv_ret == HV_EOK) {
*minor = actual_minor;
p->major = major;
p->minor = actual_minor;
ret = 0;
} else if (hv_ret == HV_EBADTRAP ||
HV_ENOTSUPPORTED) {
if (p->flags & FLAG_PRE_API) {
if (major == 1) {
p->major = 1;
p->minor = 0;
*minor = 0;
ret = 0;
}
}
}
}

if (ret == 0)
__get_ref(p);
}
spin_unlock_irqrestore(&hvapi_lock, flags);

return ret;
}
EXPORT_SYMBOL(sun4v_hvapi_register);

void sun4v_hvapi_unregister(unsigned long group)
{
struct api_info *p;
unsigned long flags;

spin_lock_irqsave(&hvapi_lock, flags);
p = __get_info(group);
if (p)
__put_ref(p);
spin_unlock_irqrestore(&hvapi_lock, flags);
}
EXPORT_SYMBOL(sun4v_hvapi_unregister);

int sun4v_hvapi_get(unsigned long group,
unsigned long *major,
unsigned long *minor)
{
struct api_info *p;
unsigned long flags;
int ret;

spin_lock_irqsave(&hvapi_lock, flags);
ret = -EINVAL;
p = __get_info(group);
if (p && p->refcnt) {
*major = p->major;
*minor = p->minor;
ret = 0;
}
spin_unlock_irqrestore(&hvapi_lock, flags);

return ret;
}
EXPORT_SYMBOL(sun4v_hvapi_get);

void __init sun4v_hvapi_init(void)
{
unsigned long group, major, minor;

group = HV_GRP_SUN4V;
major = 1;
minor = 0;
if (sun4v_hvapi_register(group, major, &minor))
goto bad;

group = HV_GRP_CORE;
major = 1;
minor = 1;
if (sun4v_hvapi_register(group, major, &minor))
goto bad;

return;

bad:
prom_printf("HVAPI: Cannot register API group "
"%lx with major(%u) minor(%u)\n",
group, major, minor);
prom_halt();
}
3 changes: 3 additions & 0 deletions arch/sparc64/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ void __init per_cpu_patch(void)

void __init sun4v_patch(void)
{
extern void sun4v_hvapi_init(void);
struct sun4v_1insn_patch_entry *p1;
struct sun4v_2insn_patch_entry *p2;

Expand Down Expand Up @@ -300,6 +301,8 @@ void __init sun4v_patch(void)

p2++;
}

sun4v_hvapi_init();
}

#ifdef CONFIG_SMP
Expand Down
Loading

0 comments on commit c7754d4

Please sign in to comment.