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 | |
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')
-rw-r--r-- | arch/sparc64/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/sparc64/kernel/entry.S | 94 | ||||
-rw-r--r-- | arch/sparc64/kernel/hvapi.c | 189 | ||||
-rw-r--r-- | arch/sparc64/kernel/setup.c | 3 |
4 files changed, 287 insertions, 1 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 6bf6fb65bc20..c749dccacc32 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile | |||
@@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ | |||
12 | irq.o ptrace.o time.o sys_sparc.o signal.o \ | 12 | irq.o ptrace.o time.o sys_sparc.o signal.o \ |
13 | unaligned.o central.o pci.o starfire.o semaphore.o \ | 13 | unaligned.o central.o pci.o starfire.o semaphore.o \ |
14 | power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ | 14 | power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ |
15 | visemul.o prom.o of_device.o | 15 | visemul.o prom.o of_device.o hvapi.o |
16 | 16 | ||
17 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 17 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
18 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ | 18 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ |
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index c15a3edcb826..732b77cb71f8 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S | |||
@@ -1843,3 +1843,97 @@ sun4v_cpu_state: | |||
1843 | mov %o1, %o0 | 1843 | mov %o1, %o0 |
1844 | 1: retl | 1844 | 1: retl |
1845 | nop | 1845 | nop |
1846 | |||
1847 | /* %o0: API group number | ||
1848 | * %o1: pointer to unsigned long major number storage | ||
1849 | * %o2: pointer to unsigned long minor number storage | ||
1850 | * | ||
1851 | * returns %o0: status | ||
1852 | */ | ||
1853 | .globl sun4v_get_version | ||
1854 | sun4v_get_version: | ||
1855 | mov HV_CORE_GET_VER, %o5 | ||
1856 | mov %o1, %o3 | ||
1857 | mov %o2, %o4 | ||
1858 | ta HV_CORE_TRAP | ||
1859 | stx %o1, [%o3] | ||
1860 | retl | ||
1861 | stx %o2, [%o4] | ||
1862 | |||
1863 | /* %o0: API group number | ||
1864 | * %o1: desired major number | ||
1865 | * %o2: desired minor number | ||
1866 | * %o3: pointer to unsigned long actual minor number storage | ||
1867 | * | ||
1868 | * returns %o0: status | ||
1869 | */ | ||
1870 | .globl sun4v_set_version | ||
1871 | sun4v_set_version: | ||
1872 | mov HV_CORE_SET_VER, %o5 | ||
1873 | mov %o3, %o4 | ||
1874 | ta HV_CORE_TRAP | ||
1875 | retl | ||
1876 | stx %o1, [%o4] | ||
1877 | |||
1878 | /* %o0: pointer to unsigned long status | ||
1879 | * | ||
1880 | * returns %o0: signed character | ||
1881 | */ | ||
1882 | .globl sun4v_con_getchar | ||
1883 | sun4v_con_getchar: | ||
1884 | mov %o0, %o4 | ||
1885 | mov HV_FAST_CONS_GETCHAR, %o5 | ||
1886 | clr %o0 | ||
1887 | clr %o1 | ||
1888 | ta HV_FAST_TRAP | ||
1889 | stx %o0, [%o4] | ||
1890 | retl | ||
1891 | sra %o1, 0, %o0 | ||
1892 | |||
1893 | /* %o0: signed long character | ||
1894 | * | ||
1895 | * returns %o0: status | ||
1896 | */ | ||
1897 | .globl sun4v_con_putchar | ||
1898 | sun4v_con_putchar: | ||
1899 | mov HV_FAST_CONS_PUTCHAR, %o5 | ||
1900 | ta HV_FAST_TRAP | ||
1901 | retl | ||
1902 | sra %o0, 0, %o0 | ||
1903 | |||
1904 | /* %o0: buffer real address | ||
1905 | * %o1: buffer size | ||
1906 | * %o2: pointer to unsigned long bytes_read | ||
1907 | * | ||
1908 | * returns %o0: status | ||
1909 | */ | ||
1910 | .globl sun4v_con_read | ||
1911 | sun4v_con_read: | ||
1912 | mov %o2, %o4 | ||
1913 | mov HV_FAST_CONS_READ, %o5 | ||
1914 | ta HV_FAST_TRAP | ||
1915 | brnz %o0, 1f | ||
1916 | cmp %o1, -1 /* break */ | ||
1917 | be,a,pn %icc, 1f | ||
1918 | mov %o1, %o0 | ||
1919 | cmp %o1, -2 /* hup */ | ||
1920 | be,a,pn %icc, 1f | ||
1921 | mov %o1, %o0 | ||
1922 | stx %o1, [%o4] | ||
1923 | 1: retl | ||
1924 | nop | ||
1925 | |||
1926 | /* %o0: buffer real address | ||
1927 | * %o1: buffer size | ||
1928 | * %o2: pointer to unsigned long bytes_written | ||
1929 | * | ||
1930 | * returns %o0: status | ||
1931 | */ | ||
1932 | .globl sun4v_con_write | ||
1933 | sun4v_con_write: | ||
1934 | mov %o2, %o4 | ||
1935 | mov HV_FAST_CONS_WRITE, %o5 | ||
1936 | ta HV_FAST_TRAP | ||
1937 | stx %o1, [%o4] | ||
1938 | retl | ||
1939 | nop | ||
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 | } | ||
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 451028341c75..dea9c3c9ec5f 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c | |||
@@ -269,6 +269,7 @@ void __init per_cpu_patch(void) | |||
269 | 269 | ||
270 | void __init sun4v_patch(void) | 270 | void __init sun4v_patch(void) |
271 | { | 271 | { |
272 | extern void sun4v_hvapi_init(void); | ||
272 | struct sun4v_1insn_patch_entry *p1; | 273 | struct sun4v_1insn_patch_entry *p1; |
273 | struct sun4v_2insn_patch_entry *p2; | 274 | struct sun4v_2insn_patch_entry *p2; |
274 | 275 | ||
@@ -300,6 +301,8 @@ void __init sun4v_patch(void) | |||
300 | 301 | ||
301 | p2++; | 302 | p2++; |
302 | } | 303 | } |
304 | |||
305 | sun4v_hvapi_init(); | ||
303 | } | 306 | } |
304 | 307 | ||
305 | #ifdef CONFIG_SMP | 308 | #ifdef CONFIG_SMP |