ItLpQueue.c 4.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * ItLpQueue.c
 * Copyright (C) 2001 Mike Corrigan  IBM Corporation
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/paca.h>
#include <asm/iSeries/ItLpQueue.h>
#include <asm/iSeries/HvLpEvent.h>
#include <asm/iSeries/HvCallEvent.h>

20
static __inline__ int set_inUse(void)
L
Linus Torvalds 已提交
21 22
{
	int t;
23
	u32 * inUseP = &xItLpQueue.xInUseWord;
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33

	__asm__ __volatile__("\n\
1:	lwarx	%0,0,%2		\n\
	cmpwi	0,%0,0		\n\
	li	%0,0		\n\
	bne-	2f		\n\
	addi	%0,%0,1		\n\
	stwcx.	%0,0,%2		\n\
	bne-	1b		\n\
2:	eieio"
34 35
	: "=&r" (t), "=m" (xItLpQueue.xInUseWord)
	: "r" (inUseP), "m" (xItLpQueue.xInUseWord)
L
Linus Torvalds 已提交
36 37 38 39 40
	: "cc");

	return t;
}

41
static __inline__ void clear_inUse(void)
L
Linus Torvalds 已提交
42
{
43
	xItLpQueue.xInUseWord = 0;
L
Linus Torvalds 已提交
44 45 46 47 48 49
}

/* Array of LpEvent handler functions */
extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
unsigned long ItLpQueueInProcess = 0;

50
struct HvLpEvent * ItLpQueue_getNextLpEvent(void)
L
Linus Torvalds 已提交
51 52
{
	struct HvLpEvent * nextLpEvent = 
53
		(struct HvLpEvent *)xItLpQueue.xSlicCurEventPtr;
L
Linus Torvalds 已提交
54 55 56 57
	if ( nextLpEvent->xFlags.xValid ) {
		/* rmb() needed only for weakly consistent machines (regatta) */
		rmb();
		/* Set pointer to next potential event */
58
		xItLpQueue.xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 +
L
Linus Torvalds 已提交
59 60 61 62
				      LpEventAlign ) /
				      LpEventAlign ) *
				      LpEventAlign;
		/* Wrap to beginning if no room at end */
63 64
		if (xItLpQueue.xSlicCurEventPtr > xItLpQueue.xSlicLastValidEventPtr)
			xItLpQueue.xSlicCurEventPtr = xItLpQueue.xSlicEventStackPtr;
L
Linus Torvalds 已提交
65 66 67 68 69 70 71
	}
	else 
		nextLpEvent = NULL;

	return nextLpEvent;
}

72
static unsigned long spread_lpevents = NR_CPUS;
73

74
int ItLpQueue_isLpIntPending(void)
L
Linus Torvalds 已提交
75
{
76 77 78 79 80
	struct HvLpEvent *next_event;

	if (smp_processor_id() >= spread_lpevents)
		return 0;

81 82
	next_event = (struct HvLpEvent *)xItLpQueue.xSlicCurEventPtr;
	return next_event->xFlags.xValid | xItLpQueue.xPlicOverflowIntPending;
L
Linus Torvalds 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
}

void ItLpQueue_clearValid( struct HvLpEvent * event )
{
	/* Clear the valid bit of the event
	 * Also clear bits within this event that might
	 * look like valid bits (on 64-byte boundaries)
   	 */
	unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) /
						 LpEventAlign ) - 1;
	switch ( extra ) {
	  case 3:
	   ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0;
	  case 2:
	   ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0;
	  case 1:
	   ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0;
	  case 0:
	   ;	
	}
	mb();
	event->xFlags.xValid = 0;
}

107
unsigned ItLpQueue_process(struct pt_regs *regs)
L
Linus Torvalds 已提交
108 109 110 111 112
{
	unsigned numIntsProcessed = 0;
	struct HvLpEvent * nextLpEvent;

	/* If we have recursed, just return */
113
	if ( !set_inUse() )
L
Linus Torvalds 已提交
114 115 116 117 118 119 120 121
		return 0;
	
	if (ItLpQueueInProcess == 0)
		ItLpQueueInProcess = 1;
	else
		BUG();

	for (;;) {
122
		nextLpEvent = ItLpQueue_getNextLpEvent();
L
Linus Torvalds 已提交
123 124
		if ( nextLpEvent ) {
			/* Count events to return to caller
125
			 * and count processed events in xItLpQueue
L
Linus Torvalds 已提交
126 127
 			 */
			++numIntsProcessed;
128
			xItLpQueue.xLpIntCount++;
L
Linus Torvalds 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141 142
			/* Call appropriate handler here, passing 
			 * a pointer to the LpEvent.  The handler
			 * must make a copy of the LpEvent if it
			 * needs it in a bottom half. (perhaps for
			 * an ACK)
			 *	
			 *  Handlers are responsible for ACK processing 
			 *
			 * The Hypervisor guarantees that LpEvents will
			 * only be delivered with types that we have
			 * registered for, so no type check is necessary
			 * here!
  			 */
			if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes )
143
				xItLpQueue.xLpIntCountByType[nextLpEvent->xType]++;
L
Linus Torvalds 已提交
144 145 146 147 148 149 150
			if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes &&
			     lpEventHandler[nextLpEvent->xType] ) 
				lpEventHandler[nextLpEvent->xType](nextLpEvent, regs);
			else
				printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType );
			
			ItLpQueue_clearValid( nextLpEvent );
151
		} else if ( xItLpQueue.xPlicOverflowIntPending )
L
Linus Torvalds 已提交
152 153 154 155
			/*
			 * No more valid events. If overflow events are
			 * pending process them
			 */
156
			HvCallEvent_getOverflowLpEvents( xItLpQueue.xIndex);
L
Linus Torvalds 已提交
157 158 159 160 161 162
		else
			break;
	}

	ItLpQueueInProcess = 0;
	mb();
163
	clear_inUse();
L
Linus Torvalds 已提交
164 165 166 167 168

	get_paca()->lpevent_count += numIntsProcessed;

	return numIntsProcessed;
}
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

static int set_spread_lpevents(char *str)
{
	unsigned long val = simple_strtoul(str, NULL, 0);

	/*
	 * The parameter is the number of processors to share in processing
	 * lp events.
	 */
	if (( val > 0) && (val <= NR_CPUS)) {
		spread_lpevents = val;
		printk("lpevent processing spread over %ld processors\n", val);
	} else {
		printk("invalid spread_lpevents %ld\n", val);
	}

	return 1;
}
__setup("spread_lpevents=", set_spread_lpevents);