diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2005-09-27 10:03:46 -0400 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2005-09-27 10:03:46 -0400 |
commit | 2952bc7c896ec76a20e18321e2be40a694a73a78 (patch) | |
tree | 291b2208855d2941274548aa1b4e12ac1712757b /arch/powerpc | |
parent | c8b84976f86adcd10c221d398e1d0be2b778f3c8 (diff) |
powerpc: move ItLpQueue.c to powerpc/platforms/iseries
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/platforms/iseries/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/lpevents.c | 261 |
2 files changed, 262 insertions, 1 deletions
diff --git a/arch/powerpc/platforms/iseries/Makefile b/arch/powerpc/platforms/iseries/Makefile index f5e11907cab1..a5e91c9db5c7 100644 --- a/arch/powerpc/platforms/iseries/Makefile +++ b/arch/powerpc/platforms/iseries/Makefile | |||
@@ -1 +1 @@ | |||
obj-y += hvcall.o hvlpconfig.o lpardata.o setup.o mf.o | obj-y += hvcall.o hvlpconfig.o lpardata.o setup.o mf.o lpevents.o | ||
diff --git a/arch/powerpc/platforms/iseries/lpevents.c b/arch/powerpc/platforms/iseries/lpevents.c new file mode 100644 index 000000000000..819298a8a4db --- /dev/null +++ b/arch/powerpc/platforms/iseries/lpevents.c | |||
@@ -0,0 +1,261 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Mike Corrigan IBM Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include <linux/stddef.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/bootmem.h> | ||
14 | #include <linux/seq_file.h> | ||
15 | #include <linux/proc_fs.h> | ||
16 | #include <asm/system.h> | ||
17 | #include <asm/paca.h> | ||
18 | #include <asm/iSeries/ItLpQueue.h> | ||
19 | #include <asm/iSeries/HvLpEvent.h> | ||
20 | #include <asm/iSeries/HvCallEvent.h> | ||
21 | |||
22 | /* | ||
23 | * The LpQueue is used to pass event data from the hypervisor to | ||
24 | * the partition. This is where I/O interrupt events are communicated. | ||
25 | * | ||
26 | * It is written to by the hypervisor so cannot end up in the BSS. | ||
27 | */ | ||
28 | struct hvlpevent_queue hvlpevent_queue __attribute__((__section__(".data"))); | ||
29 | |||
30 | DEFINE_PER_CPU(unsigned long[HvLpEvent_Type_NumTypes], hvlpevent_counts); | ||
31 | |||
32 | static char *event_types[HvLpEvent_Type_NumTypes] = { | ||
33 | "Hypervisor", | ||
34 | "Machine Facilities", | ||
35 | "Session Manager", | ||
36 | "SPD I/O", | ||
37 | "Virtual Bus", | ||
38 | "PCI I/O", | ||
39 | "RIO I/O", | ||
40 | "Virtual Lan", | ||
41 | "Virtual I/O" | ||
42 | }; | ||
43 | |||
44 | /* Array of LpEvent handler functions */ | ||
45 | extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; | ||
46 | |||
47 | static struct HvLpEvent * get_next_hvlpevent(void) | ||
48 | { | ||
49 | struct HvLpEvent * event; | ||
50 | event = (struct HvLpEvent *)hvlpevent_queue.xSlicCurEventPtr; | ||
51 | |||
52 | if (event->xFlags.xValid) { | ||
53 | /* rmb() needed only for weakly consistent machines (regatta) */ | ||
54 | rmb(); | ||
55 | /* Set pointer to next potential event */ | ||
56 | hvlpevent_queue.xSlicCurEventPtr += ((event->xSizeMinus1 + | ||
57 | LpEventAlign) / LpEventAlign) * LpEventAlign; | ||
58 | |||
59 | /* Wrap to beginning if no room at end */ | ||
60 | if (hvlpevent_queue.xSlicCurEventPtr > | ||
61 | hvlpevent_queue.xSlicLastValidEventPtr) { | ||
62 | hvlpevent_queue.xSlicCurEventPtr = | ||
63 | hvlpevent_queue.xSlicEventStackPtr; | ||
64 | } | ||
65 | } else { | ||
66 | event = NULL; | ||
67 | } | ||
68 | |||
69 | return event; | ||
70 | } | ||
71 | |||
72 | static unsigned long spread_lpevents = NR_CPUS; | ||
73 | |||
74 | int hvlpevent_is_pending(void) | ||
75 | { | ||
76 | struct HvLpEvent *next_event; | ||
77 | |||
78 | if (smp_processor_id() >= spread_lpevents) | ||
79 | return 0; | ||
80 | |||
81 | next_event = (struct HvLpEvent *)hvlpevent_queue.xSlicCurEventPtr; | ||
82 | |||
83 | return next_event->xFlags.xValid | | ||
84 | hvlpevent_queue.xPlicOverflowIntPending; | ||
85 | } | ||
86 | |||
87 | static void hvlpevent_clear_valid(struct HvLpEvent * event) | ||
88 | { | ||
89 | /* Tell the Hypervisor that we're done with this event. | ||
90 | * Also clear bits within this event that might look like valid bits. | ||
91 | * ie. on 64-byte boundaries. | ||
92 | */ | ||
93 | struct HvLpEvent *tmp; | ||
94 | unsigned extra = ((event->xSizeMinus1 + LpEventAlign) / | ||
95 | LpEventAlign) - 1; | ||
96 | |||
97 | switch (extra) { | ||
98 | case 3: | ||
99 | tmp = (struct HvLpEvent*)((char*)event + 3 * LpEventAlign); | ||
100 | tmp->xFlags.xValid = 0; | ||
101 | case 2: | ||
102 | tmp = (struct HvLpEvent*)((char*)event + 2 * LpEventAlign); | ||
103 | tmp->xFlags.xValid = 0; | ||
104 | case 1: | ||
105 | tmp = (struct HvLpEvent*)((char*)event + 1 * LpEventAlign); | ||
106 | tmp->xFlags.xValid = 0; | ||
107 | } | ||
108 | |||
109 | mb(); | ||
110 | |||
111 | event->xFlags.xValid = 0; | ||
112 | } | ||
113 | |||
114 | void process_hvlpevents(struct pt_regs *regs) | ||
115 | { | ||
116 | struct HvLpEvent * event; | ||
117 | |||
118 | /* If we have recursed, just return */ | ||
119 | if (!spin_trylock(&hvlpevent_queue.lock)) | ||
120 | return; | ||
121 | |||
122 | for (;;) { | ||
123 | event = get_next_hvlpevent(); | ||
124 | if (event) { | ||
125 | /* Call appropriate handler here, passing | ||
126 | * a pointer to the LpEvent. The handler | ||
127 | * must make a copy of the LpEvent if it | ||
128 | * needs it in a bottom half. (perhaps for | ||
129 | * an ACK) | ||
130 | * | ||
131 | * Handlers are responsible for ACK processing | ||
132 | * | ||
133 | * The Hypervisor guarantees that LpEvents will | ||
134 | * only be delivered with types that we have | ||
135 | * registered for, so no type check is necessary | ||
136 | * here! | ||
137 | */ | ||
138 | if (event->xType < HvLpEvent_Type_NumTypes) | ||
139 | __get_cpu_var(hvlpevent_counts)[event->xType]++; | ||
140 | if (event->xType < HvLpEvent_Type_NumTypes && | ||
141 | lpEventHandler[event->xType]) | ||
142 | lpEventHandler[event->xType](event, regs); | ||
143 | else | ||
144 | printk(KERN_INFO "Unexpected Lp Event type=%d\n", event->xType ); | ||
145 | |||
146 | hvlpevent_clear_valid(event); | ||
147 | } else if (hvlpevent_queue.xPlicOverflowIntPending) | ||
148 | /* | ||
149 | * No more valid events. If overflow events are | ||
150 | * pending process them | ||
151 | */ | ||
152 | HvCallEvent_getOverflowLpEvents(hvlpevent_queue.xIndex); | ||
153 | else | ||
154 | break; | ||
155 | } | ||
156 | |||
157 | spin_unlock(&hvlpevent_queue.lock); | ||
158 | } | ||
159 | |||
160 | static int set_spread_lpevents(char *str) | ||
161 | { | ||
162 | unsigned long val = simple_strtoul(str, NULL, 0); | ||
163 | |||
164 | /* | ||
165 | * The parameter is the number of processors to share in processing | ||
166 | * lp events. | ||
167 | */ | ||
168 | if (( val > 0) && (val <= NR_CPUS)) { | ||
169 | spread_lpevents = val; | ||
170 | printk("lpevent processing spread over %ld processors\n", val); | ||
171 | } else { | ||
172 | printk("invalid spread_lpevents %ld\n", val); | ||
173 | } | ||
174 | |||
175 | return 1; | ||
176 | } | ||
177 | __setup("spread_lpevents=", set_spread_lpevents); | ||
178 | |||
179 | void setup_hvlpevent_queue(void) | ||
180 | { | ||
181 | void *eventStack; | ||
182 | |||
183 | /* | ||
184 | * Allocate a page for the Event Stack. The Hypervisor needs the | ||
185 | * absolute real address, so we subtract out the KERNELBASE and add | ||
186 | * in the absolute real address of the kernel load area. | ||
187 | */ | ||
188 | eventStack = alloc_bootmem_pages(LpEventStackSize); | ||
189 | memset(eventStack, 0, LpEventStackSize); | ||
190 | |||
191 | /* Invoke the hypervisor to initialize the event stack */ | ||
192 | HvCallEvent_setLpEventStack(0, eventStack, LpEventStackSize); | ||
193 | |||
194 | hvlpevent_queue.xSlicEventStackPtr = (char *)eventStack; | ||
195 | hvlpevent_queue.xSlicCurEventPtr = (char *)eventStack; | ||
196 | hvlpevent_queue.xSlicLastValidEventPtr = (char *)eventStack + | ||
197 | (LpEventStackSize - LpEventMaxSize); | ||
198 | hvlpevent_queue.xIndex = 0; | ||
199 | } | ||
200 | |||
201 | static int proc_lpevents_show(struct seq_file *m, void *v) | ||
202 | { | ||
203 | int cpu, i; | ||
204 | unsigned long sum; | ||
205 | static unsigned long cpu_totals[NR_CPUS]; | ||
206 | |||
207 | /* FIXME: do we care that there's no locking here? */ | ||
208 | sum = 0; | ||
209 | for_each_online_cpu(cpu) { | ||
210 | cpu_totals[cpu] = 0; | ||
211 | for (i = 0; i < HvLpEvent_Type_NumTypes; i++) { | ||
212 | cpu_totals[cpu] += per_cpu(hvlpevent_counts, cpu)[i]; | ||
213 | } | ||
214 | sum += cpu_totals[cpu]; | ||
215 | } | ||
216 | |||
217 | seq_printf(m, "LpEventQueue 0\n"); | ||
218 | seq_printf(m, " events processed:\t%lu\n", sum); | ||
219 | |||
220 | for (i = 0; i < HvLpEvent_Type_NumTypes; ++i) { | ||
221 | sum = 0; | ||
222 | for_each_online_cpu(cpu) { | ||
223 | sum += per_cpu(hvlpevent_counts, cpu)[i]; | ||
224 | } | ||
225 | |||
226 | seq_printf(m, " %-20s %10lu\n", event_types[i], sum); | ||
227 | } | ||
228 | |||
229 | seq_printf(m, "\n events processed by processor:\n"); | ||
230 | |||
231 | for_each_online_cpu(cpu) { | ||
232 | seq_printf(m, " CPU%02d %10lu\n", cpu, cpu_totals[cpu]); | ||
233 | } | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int proc_lpevents_open(struct inode *inode, struct file *file) | ||
239 | { | ||
240 | return single_open(file, proc_lpevents_show, NULL); | ||
241 | } | ||
242 | |||
243 | static struct file_operations proc_lpevents_operations = { | ||
244 | .open = proc_lpevents_open, | ||
245 | .read = seq_read, | ||
246 | .llseek = seq_lseek, | ||
247 | .release = single_release, | ||
248 | }; | ||
249 | |||
250 | static int __init proc_lpevents_init(void) | ||
251 | { | ||
252 | struct proc_dir_entry *e; | ||
253 | |||
254 | e = create_proc_entry("iSeries/lpevents", S_IFREG|S_IRUGO, NULL); | ||
255 | if (e) | ||
256 | e->proc_fops = &proc_lpevents_operations; | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | __initcall(proc_lpevents_init); | ||
261 | |||