blob: 4231861288a3cb5b9fe88e6219fa7cedc19ec730 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * ItLpQueue.c
3 * Copyright (C) 2001 Mike Corrigan IBM Corporation
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +10004 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11#include <linux/stddef.h>
12#include <linux/kernel.h>
13#include <linux/sched.h>
Michael Ellerman512d31d2005-06-30 15:08:27 +100014#include <linux/bootmem.h>
Michael Ellerman7b013282005-06-30 15:08:44 +100015#include <linux/seq_file.h>
16#include <linux/proc_fs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <asm/system.h>
18#include <asm/paca.h>
19#include <asm/iSeries/ItLpQueue.h>
20#include <asm/iSeries/HvLpEvent.h>
21#include <asm/iSeries/HvCallEvent.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Michael Ellermanab354b62005-06-30 15:12:21 +100023/*
24 * The LpQueue is used to pass event data from the hypervisor to
25 * the partition. This is where I/O interrupt events are communicated.
26 *
27 * It is written to by the hypervisor so cannot end up in the BSS.
28 */
Michael Ellermana6187462005-06-30 15:15:32 +100029struct hvlpevent_queue hvlpevent_queue __attribute__((__section__(".data")));
Michael Ellermanab354b62005-06-30 15:12:21 +100030
Michael Ellermaned094152005-06-30 15:16:09 +100031DEFINE_PER_CPU(unsigned long[HvLpEvent_Type_NumTypes], hvlpevent_counts);
32
33static char *event_types[HvLpEvent_Type_NumTypes] = {
Michael Ellerman9b047022005-06-30 15:16:18 +100034 "Hypervisor",
35 "Machine Facilities",
36 "Session Manager",
37 "SPD I/O",
38 "Virtual Bus",
39 "PCI I/O",
40 "RIO I/O",
41 "Virtual Lan",
42 "Virtual I/O"
Michael Ellerman7b013282005-06-30 15:08:44 +100043};
44
Linus Torvalds1da177e2005-04-16 15:20:36 -070045/* Array of LpEvent handler functions */
46extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
Michael Ellerman937b31b2005-06-30 15:15:42 +100048static struct HvLpEvent * get_next_hvlpevent(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049{
Michael Ellermanffe1b7e2005-06-30 15:16:48 +100050 struct HvLpEvent * event;
51 event = (struct HvLpEvent *)hvlpevent_queue.xSlicCurEventPtr;
52
53 if (event->xFlags.xValid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 /* rmb() needed only for weakly consistent machines (regatta) */
55 rmb();
56 /* Set pointer to next potential event */
Michael Ellermanffe1b7e2005-06-30 15:16:48 +100057 hvlpevent_queue.xSlicCurEventPtr += ((event->xSizeMinus1 +
58 LpEventAlign) / LpEventAlign) * LpEventAlign;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Michael Ellermanffe1b7e2005-06-30 15:16:48 +100060 /* Wrap to beginning if no room at end */
61 if (hvlpevent_queue.xSlicCurEventPtr >
62 hvlpevent_queue.xSlicLastValidEventPtr) {
63 hvlpevent_queue.xSlicCurEventPtr =
64 hvlpevent_queue.xSlicEventStackPtr;
65 }
66 } else {
67 event = NULL;
68 }
69
70 return event;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071}
72
Michael Ellerman0c885c12005-06-30 15:07:33 +100073static unsigned long spread_lpevents = NR_CPUS;
Michael Ellermanbea248f2005-06-30 15:07:09 +100074
Michael Ellerman937b31b2005-06-30 15:15:42 +100075int hvlpevent_is_pending(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076{
Michael Ellermanbea248f2005-06-30 15:07:09 +100077 struct HvLpEvent *next_event;
78
79 if (smp_processor_id() >= spread_lpevents)
80 return 0;
81
Michael Ellermana6187462005-06-30 15:15:32 +100082 next_event = (struct HvLpEvent *)hvlpevent_queue.xSlicCurEventPtr;
Michael Ellermanffe1b7e2005-06-30 15:16:48 +100083
84 return next_event->xFlags.xValid |
85 hvlpevent_queue.xPlicOverflowIntPending;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086}
87
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +100088static void hvlpevent_clear_valid(struct HvLpEvent * event)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089{
Michael Ellermanffe1b7e2005-06-30 15:16:48 +100090 /* Tell the Hypervisor that we're done with this event.
91 * Also clear bits within this event that might look like valid bits.
92 * ie. on 64-byte boundaries.
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +100093 */
Michael Ellermanffe1b7e2005-06-30 15:16:48 +100094 struct HvLpEvent *tmp;
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +100095 unsigned extra = ((event->xSizeMinus1 + LpEventAlign) /
96 LpEventAlign) - 1;
Michael Ellermanffe1b7e2005-06-30 15:16:48 +100097
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +100098 switch (extra) {
99 case 3:
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000100 tmp = (struct HvLpEvent*)((char*)event + 3 * LpEventAlign);
101 tmp->xFlags.xValid = 0;
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000102 case 2:
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000103 tmp = (struct HvLpEvent*)((char*)event + 2 * LpEventAlign);
104 tmp->xFlags.xValid = 0;
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000105 case 1:
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000106 tmp = (struct HvLpEvent*)((char*)event + 1 * LpEventAlign);
107 tmp->xFlags.xValid = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 }
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000109
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 mb();
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000111
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 event->xFlags.xValid = 0;
113}
114
Michael Ellerman74889802005-06-30 15:15:53 +1000115void process_hvlpevents(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116{
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000117 struct HvLpEvent * event;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119 /* If we have recursed, just return */
Michael Ellerman719d1cd2005-06-30 15:17:02 +1000120 if (!spin_trylock(&hvlpevent_queue.lock))
Michael Ellerman74889802005-06-30 15:15:53 +1000121 return;
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 for (;;) {
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000124 event = get_next_hvlpevent();
125 if (event) {
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000126 /* Call appropriate handler here, passing
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 * a pointer to the LpEvent. The handler
128 * must make a copy of the LpEvent if it
129 * needs it in a bottom half. (perhaps for
130 * an ACK)
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000131 *
132 * Handlers are responsible for ACK processing
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 *
134 * The Hypervisor guarantees that LpEvents will
135 * only be delivered with types that we have
136 * registered for, so no type check is necessary
137 * here!
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000138 */
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000139 if (event->xType < HvLpEvent_Type_NumTypes)
140 __get_cpu_var(hvlpevent_counts)[event->xType]++;
141 if (event->xType < HvLpEvent_Type_NumTypes &&
142 lpEventHandler[event->xType])
143 lpEventHandler[event->xType](event, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 else
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000145 printk(KERN_INFO "Unexpected Lp Event type=%d\n", event->xType );
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000146
Michael Ellermanffe1b7e2005-06-30 15:16:48 +1000147 hvlpevent_clear_valid(event);
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000148 } else if (hvlpevent_queue.xPlicOverflowIntPending)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 /*
150 * No more valid events. If overflow events are
151 * pending process them
152 */
Michael Ellerman38fcdcfe2005-06-30 15:16:28 +1000153 HvCallEvent_getOverflowLpEvents(hvlpevent_queue.xIndex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 else
155 break;
156 }
157
Michael Ellerman719d1cd2005-06-30 15:17:02 +1000158 spin_unlock(&hvlpevent_queue.lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159}
Michael Ellerman0c885c12005-06-30 15:07:33 +1000160
161static int set_spread_lpevents(char *str)
162{
163 unsigned long val = simple_strtoul(str, NULL, 0);
164
165 /*
166 * The parameter is the number of processors to share in processing
167 * lp events.
168 */
169 if (( val > 0) && (val <= NR_CPUS)) {
170 spread_lpevents = val;
171 printk("lpevent processing spread over %ld processors\n", val);
172 } else {
173 printk("invalid spread_lpevents %ld\n", val);
174 }
175
176 return 1;
177}
178__setup("spread_lpevents=", set_spread_lpevents);
179
Michael Ellerman512d31d2005-06-30 15:08:27 +1000180void setup_hvlpevent_queue(void)
181{
182 void *eventStack;
183
184 /*
185 * Allocate a page for the Event Stack. The Hypervisor needs the
186 * absolute real address, so we subtract out the KERNELBASE and add
187 * in the absolute real address of the kernel load area.
188 */
189 eventStack = alloc_bootmem_pages(LpEventStackSize);
190 memset(eventStack, 0, LpEventStackSize);
191
192 /* Invoke the hypervisor to initialize the event stack */
193 HvCallEvent_setLpEventStack(0, eventStack, LpEventStackSize);
194
Michael Ellermana6187462005-06-30 15:15:32 +1000195 hvlpevent_queue.xSlicEventStackPtr = (char *)eventStack;
196 hvlpevent_queue.xSlicCurEventPtr = (char *)eventStack;
197 hvlpevent_queue.xSlicLastValidEventPtr = (char *)eventStack +
Michael Ellerman512d31d2005-06-30 15:08:27 +1000198 (LpEventStackSize - LpEventMaxSize);
Michael Ellermana6187462005-06-30 15:15:32 +1000199 hvlpevent_queue.xIndex = 0;
Michael Ellerman512d31d2005-06-30 15:08:27 +1000200}
Michael Ellerman7b013282005-06-30 15:08:44 +1000201
202static int proc_lpevents_show(struct seq_file *m, void *v)
203{
Michael Ellermaned094152005-06-30 15:16:09 +1000204 int cpu, i;
205 unsigned long sum;
206 static unsigned long cpu_totals[NR_CPUS];
207
208 /* FIXME: do we care that there's no locking here? */
209 sum = 0;
210 for_each_online_cpu(cpu) {
211 cpu_totals[cpu] = 0;
212 for (i = 0; i < HvLpEvent_Type_NumTypes; i++) {
213 cpu_totals[cpu] += per_cpu(hvlpevent_counts, cpu)[i];
214 }
215 sum += cpu_totals[cpu];
216 }
Michael Ellerman7b013282005-06-30 15:08:44 +1000217
218 seq_printf(m, "LpEventQueue 0\n");
Michael Ellermaned094152005-06-30 15:16:09 +1000219 seq_printf(m, " events processed:\t%lu\n", sum);
Michael Ellerman7b013282005-06-30 15:08:44 +1000220
Michael Ellermaned094152005-06-30 15:16:09 +1000221 for (i = 0; i < HvLpEvent_Type_NumTypes; ++i) {
222 sum = 0;
223 for_each_online_cpu(cpu) {
224 sum += per_cpu(hvlpevent_counts, cpu)[i];
225 }
226
Michael Ellerman9b047022005-06-30 15:16:18 +1000227 seq_printf(m, " %-20s %10lu\n", event_types[i], sum);
Michael Ellermaned094152005-06-30 15:16:09 +1000228 }
Michael Ellerman7b013282005-06-30 15:08:44 +1000229
230 seq_printf(m, "\n events processed by processor:\n");
231
Michael Ellermaned094152005-06-30 15:16:09 +1000232 for_each_online_cpu(cpu) {
233 seq_printf(m, " CPU%02d %10lu\n", cpu, cpu_totals[cpu]);
234 }
Michael Ellerman7b013282005-06-30 15:08:44 +1000235
236 return 0;
237}
238
239static int proc_lpevents_open(struct inode *inode, struct file *file)
240{
241 return single_open(file, proc_lpevents_show, NULL);
242}
243
244static struct file_operations proc_lpevents_operations = {
245 .open = proc_lpevents_open,
246 .read = seq_read,
247 .llseek = seq_lseek,
248 .release = single_release,
249};
250
251static int __init proc_lpevents_init(void)
252{
253 struct proc_dir_entry *e;
254
255 e = create_proc_entry("iSeries/lpevents", S_IFREG|S_IRUGO, NULL);
256 if (e)
257 e->proc_fops = &proc_lpevents_operations;
258
259 return 0;
260}
261__initcall(proc_lpevents_init);
262