diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-05-15 20:03:54 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-05-15 23:23:02 -0400 |
commit | c7754d465b1feade85b5f1c4492781a30f6652a2 (patch) | |
tree | 9a3b6ccb18983c1ea389377028ca51c8170730ef /arch/sparc64/kernel/hvapi.c | |
parent | 7b104bcb8e460e45a1aebe3da9b86aacdb4cab12 (diff) |
[SPARC64]: Add hypervisor API negotiation and fix console bugs.
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 <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/hvapi.c')
-rw-r--r-- | arch/sparc64/kernel/hvapi.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc64/kernel/hvapi.c new file mode 100644 index 000000000000..f03ffc829c7a --- /dev/null +++ b/arch/sparc64/kernel/hvapi.c | |||
@@ -0,0 +1,189 @@ | |||
1 | /* hvapi.c: Hypervisor API management. | ||
2 | * | ||
3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
4 | */ | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/slab.h> | ||
9 | |||
10 | #include <asm/hypervisor.h> | ||
11 | #include <asm/oplib.h> | ||
12 | |||
13 | /* If the hypervisor indicates that the API setting | ||
14 | * calls are unsupported, by returning HV_EBADTRAP or | ||
15 | * HV_ENOTSUPPORTED, we assume that API groups with the | ||
16 | * PRE_API flag set are major 1 minor 0. | ||
17 | */ | ||
18 | struct api_info { | ||
19 | unsigned long group; | ||
20 | unsigned long major; | ||
21 | unsigned long minor; | ||
22 | unsigned int refcnt; | ||
23 | unsigned int flags; | ||
24 | #define FLAG_PRE_API 0x00000001 | ||
25 | }; | ||
26 | |||
27 | static struct api_info api_table[] = { | ||
28 | { .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API }, | ||
29 | { .group = HV_GRP_CORE, .flags = FLAG_PRE_API }, | ||
30 | { .group = HV_GRP_INTR, }, | ||
31 | { .group = HV_GRP_SOFT_STATE, }, | ||
32 | { .group = HV_GRP_PCI, .flags = FLAG_PRE_API }, | ||
33 | { .group = HV_GRP_LDOM, }, | ||
34 | { .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API }, | ||
35 | { .group = HV_GRP_NCS, .flags = FLAG_PRE_API }, | ||
36 | { .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API }, | ||
37 | { .group = HV_GRP_FIRE_PERF, }, | ||
38 | { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API }, | ||
39 | }; | ||
40 | |||
41 | static DEFINE_SPINLOCK(hvapi_lock); | ||
42 | |||
43 | static struct api_info *__get_info(unsigned long group) | ||
44 | { | ||
45 | int i; | ||
46 | |||
47 | for (i = 0; i < ARRAY_SIZE(api_table); i++) { | ||
48 | if (api_table[i].group == group) | ||
49 | return &api_table[i]; | ||
50 | } | ||
51 | return NULL; | ||
52 | } | ||
53 | |||
54 | static void __get_ref(struct api_info *p) | ||
55 | { | ||
56 | p->refcnt++; | ||
57 | } | ||
58 | |||
59 | static void __put_ref(struct api_info *p) | ||
60 | { | ||
61 | if (--p->refcnt == 0) { | ||
62 | unsigned long ignore; | ||
63 | |||
64 | sun4v_set_version(p->group, 0, 0, &ignore); | ||
65 | p->major = p->minor = 0; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | /* Register a hypervisor API specification. It indicates the | ||
70 | * API group and desired major+minor. | ||
71 | * | ||
72 | * If an existing API registration exists '0' (success) will | ||
73 | * be returned if it is compatible with the one being registered. | ||
74 | * Otherwise a negative error code will be returned. | ||
75 | * | ||
76 | * Otherwise an attempt will be made to negotiate the requested | ||
77 | * API group/major/minor with the hypervisor, and errors returned | ||
78 | * if that does not succeed. | ||
79 | */ | ||
80 | int sun4v_hvapi_register(unsigned long group, unsigned long major, | ||
81 | unsigned long *minor) | ||
82 | { | ||
83 | struct api_info *p; | ||
84 | unsigned long flags; | ||
85 | int ret; | ||
86 | |||
87 | spin_lock_irqsave(&hvapi_lock, flags); | ||
88 | p = __get_info(group); | ||
89 | ret = -EINVAL; | ||
90 | if (p) { | ||
91 | if (p->refcnt) { | ||
92 | ret = -EINVAL; | ||
93 | if (p->major == major) { | ||
94 | *minor = p->minor; | ||
95 | ret = 0; | ||
96 | } | ||
97 | } else { | ||
98 | unsigned long actual_minor; | ||
99 | unsigned long hv_ret; | ||
100 | |||
101 | hv_ret = sun4v_set_version(group, major, *minor, | ||
102 | &actual_minor); | ||
103 | ret = -EINVAL; | ||
104 | if (hv_ret == HV_EOK) { | ||
105 | *minor = actual_minor; | ||
106 | p->major = major; | ||
107 | p->minor = actual_minor; | ||
108 | ret = 0; | ||
109 | } else if (hv_ret == HV_EBADTRAP || | ||
110 | HV_ENOTSUPPORTED) { | ||
111 | if (p->flags & FLAG_PRE_API) { | ||
112 | if (major == 1) { | ||
113 | p->major = 1; | ||
114 | p->minor = 0; | ||
115 | *minor = 0; | ||
116 | ret = 0; | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
122 | if (ret == 0) | ||
123 | __get_ref(p); | ||
124 | } | ||
125 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
126 | |||
127 | return ret; | ||
128 | } | ||
129 | EXPORT_SYMBOL(sun4v_hvapi_register); | ||
130 | |||
131 | void sun4v_hvapi_unregister(unsigned long group) | ||
132 | { | ||
133 | struct api_info *p; | ||
134 | unsigned long flags; | ||
135 | |||
136 | spin_lock_irqsave(&hvapi_lock, flags); | ||
137 | p = __get_info(group); | ||
138 | if (p) | ||
139 | __put_ref(p); | ||
140 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
141 | } | ||
142 | EXPORT_SYMBOL(sun4v_hvapi_unregister); | ||
143 | |||
144 | int sun4v_hvapi_get(unsigned long group, | ||
145 | unsigned long *major, | ||
146 | unsigned long *minor) | ||
147 | { | ||
148 | struct api_info *p; | ||
149 | unsigned long flags; | ||
150 | int ret; | ||
151 | |||
152 | spin_lock_irqsave(&hvapi_lock, flags); | ||
153 | ret = -EINVAL; | ||
154 | p = __get_info(group); | ||
155 | if (p && p->refcnt) { | ||
156 | *major = p->major; | ||
157 | *minor = p->minor; | ||
158 | ret = 0; | ||
159 | } | ||
160 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
161 | |||
162 | return ret; | ||
163 | } | ||
164 | EXPORT_SYMBOL(sun4v_hvapi_get); | ||
165 | |||
166 | void __init sun4v_hvapi_init(void) | ||
167 | { | ||
168 | unsigned long group, major, minor; | ||
169 | |||
170 | group = HV_GRP_SUN4V; | ||
171 | major = 1; | ||
172 | minor = 0; | ||
173 | if (sun4v_hvapi_register(group, major, &minor)) | ||
174 | goto bad; | ||
175 | |||
176 | group = HV_GRP_CORE; | ||
177 | major = 1; | ||
178 | minor = 1; | ||
179 | if (sun4v_hvapi_register(group, major, &minor)) | ||
180 | goto bad; | ||
181 | |||
182 | return; | ||
183 | |||
184 | bad: | ||
185 | prom_printf("HVAPI: Cannot register API group " | ||
186 | "%lx with major(%u) minor(%u)\n", | ||
187 | group, major, minor); | ||
188 | prom_halt(); | ||
189 | } | ||