[PATCH] User Level Interrupts

From: Michael Raymond
Date: Wed Mar 23 2005 - 11:45:43 EST


Allow fast (1+us) user notification of device interrupts. This allows
more powerful user I/O applications to be written. The process of porting
to other architectures is straight forward and fully documented. More
information can be found at http://oss.sgi.com/projects/uli/.

Signed-off-by: Michael A Raymond <mraymond@xxxxxxx>

--
Michael A. Raymond Office: (651) 683-3434
Core OS Group Real-Time System Software
diff -urN linux-2.6.11/arch/ia64/kernel/asm-offsets.c linux-2.6.11-uli/arch/ia64/kernel/asm-offsets.c
--- linux-2.6.11/arch/ia64/kernel/asm-offsets.c 2005-03-10 06:44:14.693503599 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/asm-offsets.c 2005-03-10 06:45:49.777264508 -0600
@@ -7,6 +7,7 @@
#include <linux/config.h>

#include <linux/sched.h>
+#include <linux/uli.h>

#include <asm-ia64/processor.h>
#include <asm-ia64/ptrace.h>
@@ -236,4 +237,19 @@
DEFINE(IA64_TIME_SOURCE_MMIO64, TIME_SOURCE_MMIO64);
DEFINE(IA64_TIME_SOURCE_MMIO32, TIME_SOURCE_MMIO32);
DEFINE(IA64_TIMESPEC_TV_NSEC_OFFSET, offsetof (struct timespec, tv_nsec));
+
+#ifdef CONFIG_ULI
+ BLANK();
+ DEFINE(ULI_GP_OFFSET, offsetof(struct uli, uli_gp));
+ DEFINE(ULI_SP_OFFSET, offsetof(struct uli, uli_sp));
+ DEFINE(ULI_BSPSTORE_OFFSET, offsetof(struct uli, uli_arch0));
+ DEFINE(ULI_TP_OFFSET, offsetof(struct uli, uli_arch1));
+ DEFINE(ULI_PC_OFFSET, offsetof(struct uli, uli_pc));
+ DEFINE(ULI_ARG_OFFSET, offsetof(struct uli, uli_funcarg));
+ DEFINE(ULI_TSTAMP_OFFSET, offsetof(struct uli, uli_tstamp));
+ DEFINE(ULI_DOUBLE_OFFSET, offsetof(struct uli, uli_double));
+ DEFINE(ULI_INTR_SP_OFFSET, offsetof(struct uli, uli_intr_sp));
+ DEFINE(ULI_INTR_BSPSTORE_OFFSET, offsetof(struct uli, uli_intr_arch0));
+ DEFINE(ULI_SAVED_EPC_OFFSET, offsetof(struct uli, uli_saved_epc));
+#endif /* CONFIG_ULI */
}
diff -urN linux-2.6.11/arch/ia64/kernel/entry.S linux-2.6.11-uli/arch/ia64/kernel/entry.S
--- linux-2.6.11/arch/ia64/kernel/entry.S 2005-03-02 01:37:50.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/entry.S 2005-03-22 08:41:17.597377762 -0600
@@ -698,6 +698,16 @@
(pUStk) cmp.eq.unc p6,p0=r0,r0 // p6 <- pUStk
#endif
.work_processed_syscall:
+#ifdef CONFIG_ULI
+ addl r22 = THIS_CPU(uli_cur),r0 // uli_cur
+ ;;
+ ld8 r22 = [r22] // load current ULI
+ ;;
+(p6) cmp.eq p6,p0=r0,r22 // If would do work && no ULI, then can still check
+(pUStk) cmp.eq.unc p7,p0=r0,r22 // Don't store to on_ustack if there's a ULI
+#else /* CONFIG_ULI */
+(pUStk) cmp.eq.unc p7,p0=r0,r0
+#endif /* CONFIG_ULI */
adds r2=PT(LOADRS)+16,r12
adds r3=PT(AR_BSPSTORE)+16,r12
adds r18=TI_FLAGS+IA64_TASK_SIZE,r13
@@ -760,7 +770,7 @@
addl r3=THIS_CPU(ia64_phys_stacked_size_p8),r0
;;
(pUStk) ld4 r3=[r3] // r3 = cpu_data->phys_stacked_size_p8
-(pUStk) st1 [r14]=r17
+(p7) st1 [r14]=r17
mov b6=r18 // I0 restore b6
;;
mov r14=r0 // clear r14
@@ -814,6 +824,14 @@
(pUStk) cmp.eq.unc p6,p0=r0,r0 // p6 <- pUStk
#endif
.work_processed_kernel:
+#ifdef CONFIG_ULI
+ addl r22 = THIS_CPU(uli_cur),r0 // uli_cur
+ ;;
+ ld8 r22 = [r22] // load current ULI
+ ;;
+(p6) cmp.eq p6,p0=r0,r22 // If would do work && no ULI, then can still check
+ cmp.eq p9,p7=r0,r22 // Set p7 if there's a current ULI
+#endif /* CONFIG_ULI */
adds r17=TI_FLAGS+IA64_TASK_SIZE,r13
;;
(p6) ld4 r31=[r17] // load current_thread_info()->flags
@@ -842,13 +860,44 @@
;;
ld8 r31=[r2],16 // load ar.ssd
ld8.fill r8=[r3],16
+#ifdef CONFIG_ULI
+(p7) add r19 = ULI_DOUBLE_OFFSET,r22 /* &cur_uli->uli_double */
+#endif /* CONFIG_ULI */
;;
ld8.fill r9=[r2],16
ld8.fill r10=[r3],PT(R17)-PT(R10)
+#ifdef CONFIG_ULI
+(p7) add r18 = ULI_TSTAMP_OFFSET,r22
+#endif /* CONFIG_ULI */
;;
ld8.fill r11=[r2],PT(R18)-PT(R11)
ld8.fill r17=[r3],16
+(pUStk) cmp.eq.unc p6,p0=r0,r0 /* p6 was false, set to pUStk */
+ ;;
+#ifdef CONFIG_ULI
+/* We need to check if the current ULI has been running too long */
+(p9) br.cond.sptk.many 1f /* No current ULI, skip the check */
+ ;;
+ ld4 r22=[r19] /* *cur_uli->uli_double, # of double intrs */
+ ld8 r18=[r18] /* *tstamp */
+ cmp.eq p0,p6=r0,r0 /* If there's a current ULI, p6 becomes false */
+ ;;
+ mov r23 = ar.itc /* Get the current time */
+ sub r22=r22,r0,1 /* One less nested level above ULI handler */
+ ;;
+ cmp.eq p8,p9=r22,r0 /* Returning into a handler? */
+ st4 [r19] = r22 /* store lower # of doubles */
+ ;;
+(p8) cmp.ge p0,p9=r23,r18 /* Returning into a handler && overrun? */
+ ;;
+(p9) br.cond.sptk.many 1f /* No overrun, continue exiting */
+ alloc r0=ar.pfs,0,0,2,0 /* There's been an overrun, abort the ULI */
;;
+ mov out0 = 24 /* SIGXCPU */
+ mov out1 = r0 /* &pt_regs */
+ br.call.spnt.few b6=uli_return /* Go to the start pt, this won't ret */
+1:
+#endif /* CONFIG_ULI */
ld8.fill r18=[r2],16
ld8.fill r19=[r3],16
;;
@@ -930,7 +979,7 @@
(pUStk) mov r17=1
;;
ld8.fill r3=[r16]
-(pUStk) st1 [r18]=r17 // restore current->thread.on_ustack
+(p6) st1 [r18]=r17 // restore current->thread.on_ustack
shr.u r18=r19,16 // get byte size of existing "dirty" partition
;;
mov r16=ar.bsp // get existing backing store pointer
diff -urN linux-2.6.11/arch/ia64/kernel/fsys.S linux-2.6.11-uli/arch/ia64/kernel/fsys.S
--- linux-2.6.11/arch/ia64/kernel/fsys.S 2005-03-02 01:38:34.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/fsys.S 2005-03-22 08:45:29.832958277 -0600
@@ -579,6 +579,9 @@
mov psr.l=r9 // slam the door (17 cyc to srlz.i)
or r29=r8,r29 // construct cr.ipsr value to save
addl r22=IA64_RBS_OFFSET,r2 // compute base of RBS
+#ifdef CONFIG_ULI
+ addl r17 = THIS_CPU(uli_cur),r0 // &uli_cur[this cpu]
+#endif /* CONFIG_ULI */
;;
// GAS reports a spurious RAW hazard on the read of ar.rnat because it thinks
// we may be reading ar.itc after writing to psr.l. Avoid that message with
@@ -587,6 +590,9 @@
mov.m r24=ar.rnat // read ar.rnat (5 cyc lat)
lfetch.fault.excl.nt1 [r22]
adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r2
+#ifdef CONFIG_ULI
+ ld8 r3 = [r17] // Get the address of the current ULI
+#endif /* CONFIG_ULI */

// ensure previous insn group is issued before we stall for srlz.i:
;;
@@ -595,6 +601,14 @@
////////// from this point on, execution is not interruptible anymore
/////////////////////////////////////////////////////////////////////////////
addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2 // compute base of memory stack
+#ifdef CONFIG_ULI
+ cmp.eq p7,p20=r0,r3 // ULI?
+ add r17 = ULI_INTR_SP_OFFSET, r3 // Former stack pointer
+ add r30 = ULI_INTR_BSPSTORE_OFFSET, r3 // Former BSP
+ ;;
+(p20) ld8 r1 = [r17] // sp
+(p20) ld8 r22 = [r30] // bspstore
+#endif /* CONFIG_ULI */
cmp.ne pKStk,pUStk=r0,r0 // set pKStk <- 0, pUStk <- 1
;;
st1 [r16]=r0 // clear current->thread.on_ustack flag
@@ -613,6 +627,9 @@
mov rp=r2 // set the real return addr
tbit.z p8,p0=r3,TIF_SYSCALL_TRACE
;;
+#ifdef CONFIG_ULI
+(p20) br.call.spnt.few b6=uli_syscall
+#endif /* CONFIG_ULI */
(p10) br.cond.spnt.many ia64_ret_from_syscall // p10==true means out registers are more than 8
(p8) br.call.sptk.many b6=b6 // ignore this return addr
br.cond.sptk ia64_trace_syscall
diff -urN linux-2.6.11/arch/ia64/kernel/ivt.S linux-2.6.11-uli/arch/ia64/kernel/ivt.S
--- linux-2.6.11/arch/ia64/kernel/ivt.S 2005-03-02 01:37:49.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/ivt.S 2005-03-21 08:53:52.611672980 -0600
@@ -704,7 +704,21 @@
cmp.eq p0,p7=r18,r17 // is this a system call? (p7 <- false, if so)
(p7) br.cond.spnt non_syscall
;;
+#ifdef CONFIG_ULI
+ /*
+ * If we're in a ULI doing a system call, we need to start on the stack
+ * where we switched out to handle the ULI.
+ */
+ addl r17 = THIS_CPU(uli_cur),r0 // &uli_cur[this cpu]
+ ;;
+ ld8 r22 = [r17] // r22 = current ULI, MINSTATE expects r22
+ ;;
+ cmp.eq pUStk,pLvSys=r0,r22 // ULI? MINSTATE expects a valid pLvSys
+ ;;
+(pUStk) ld1 r17=[r16] // load current->thread.on_ustack flag
+#else /* CONFIG_ULI */
ld1 r17=[r16] // load current->thread.on_ustack flag
+#endif /* CONFIG_ULI */
st1 [r16]=r0 // clear current->thread.on_ustack flag
add r1=-IA64_TASK_THREAD_ON_USTACK_OFFSET,r16 // set r1 for MINSTATE_START_SAVE_MIN_VIRT
;;
@@ -721,6 +735,7 @@
(p6) adds r28=16,r28 // switch cr.iip to next bundle cr.ipsr.ei wrapped
(p7) adds r8=1,r8 // increment ei to next slot
;;
+ /* If we're in a ULI then r17 will be non-zero and we'll get pUStk */
cmp.eq pKStk,pUStk=r0,r17 // are we in kernel mode already?
dep r29=r8,r29,41,2 // insert new ei into cr.ipsr
;;
@@ -760,6 +775,9 @@
cmp.eq p8,p0=r2,r0
mov b6=r20
;;
+#ifdef CONFIG_ULI
+(pLvSys) br.call.spnt.few b6=uli_syscall
+#endif /* CONFIG_ULI */
(p8) br.call.sptk.many b6=b6 // ignore this return addr
br.cond.sptk ia64_trace_syscall
// NOT REACHED
diff -urN linux-2.6.11/arch/ia64/kernel/Makefile linux-2.6.11-uli/arch/ia64/kernel/Makefile
--- linux-2.6.11/arch/ia64/kernel/Makefile 2005-03-10 06:44:22.122120706 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/Makefile 2005-03-10 06:47:33.250570322 -0600
@@ -21,6 +21,7 @@
obj-$(CONFIG_IA64_CYCLONE) += cyclone.o
obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o
mca_recovery-y += mca_drv.o mca_drv_asm.o
+obj-$(CONFIG_ULI) += uli_asm.o

# The gate DSO image is built using a special linker script.
targets += gate.so gate-syms.o
diff -urN linux-2.6.11/arch/ia64/kernel/minstate.h linux-2.6.11-uli/arch/ia64/kernel/minstate.h
--- linux-2.6.11/arch/ia64/kernel/minstate.h 2005-03-02 01:38:25.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/minstate.h 2005-03-18 15:31:09.001118433 -0600
@@ -4,15 +4,41 @@

#include "entry.h"

+#ifdef CONFIG_ULI
+#include <asm/percpu.h>
+
+#define MINSTATE_LOAD_OFFSETS \
+(pLvSys) add r17 = ULI_INTR_SP_OFFSET, r22; /* prev sp - size(pt_regs) */ \
+(pLvSys) add r18 = ULI_INTR_BSPSTORE_OFFSET, r22; /* previous bspstore */
+
+#define MINSTATE_LOAD_STACK \
+(pLvSys) ld8 r1 = [r17]; /* sp is prev mod'd sp */ \
+(pLvSys) ld8 r22 = [r18]; /* bspstore is prev bspstore */
+
+# else /* CONFIG_ULI */
+
+#define MINSTATE_LOAD_OFFSETS
+#define MINSTATE_LOAD_STACK
+
+#endif /* CONFIG_ULI */
+
/*
* For ivt.s we want to access the stack virtually so we don't have to disable translation
* on interrupts.
*
* On entry:
* r1: pointer to current task (ar.k6)
+ *
+ * For ULI code:
+ * If we came from user space, pUStk is true.
+ * If there is no current ULI and we took this trap while in the kernel,
+ * OR if we're well above a ULI, pKStk is true.
+ * If we're right on top of a ULI, pLvSys is true and we use it to override
+ * some of pUStk's values to get the appropriate kernel stacks.
*/
#define MINSTATE_START_SAVE_MIN_VIRT \
(pUStk) mov ar.rsc=0; /* set enforced lazy mode, pl 0, little-endian, loadrs=0 */ \
+MINSTATE_LOAD_OFFSETS \
;; \
(pUStk) mov.m r24=ar.rnat; \
(pUStk) addl r22=IA64_RBS_OFFSET,r1; /* compute base of RBS */ \
@@ -25,6 +51,8 @@
(pUStk) mov ar.bspstore=r22; /* switch to kernel RBS */ \
(pKStk) addl r1=-IA64_PT_REGS_SIZE,r1; /* if in kernel mode, use sp (r12) */ \
;; \
+MINSTATE_LOAD_STACK \
+ ;; \
(pUStk) mov r18=ar.bsp; \
(pUStk) mov ar.rsc=0x3; /* set eager mode, pl 0, little-endian, loadrs=0 */ \

@@ -61,7 +89,34 @@
;;

#ifdef MINSTATE_VIRT
+#ifdef CONFIG_ULI
+
+/*
+ * We leave with pUStk true if we're not on a ULI OR we're well above one.
+ * We leave with pKStk true if there's a current ULI
+ * We leave with PLvSys true if we're the first above a ULI.
+ */
+#define MINSTATE_GET_CURRENT(reg) \
+ addl r20 = THIS_CPU(uli_cur),r0; \
+ ;; \
+ ld8 r22 = [r20]; /* r22 = cur_uli, needed by START_SAVE_MIN_VIRT */ \
+ ;; \
+ cmp.eq pUStk,pKStk=r0,r22; /* cur == NULL? */ \
+ add r20 = ULI_DOUBLE_OFFSET,r22; /* r20 = &cur_uli.uli_double */ \
+ ;; \
+(pKStk) ld4 r21 = [r20]; /* load # of doubles */ \
+(pUStk) cmp.eq pUStk,pLvSys=r0,r0; /* init pLvSys to false */ \
+ ;; \
+(pKStk) cmp.eq pLvSys,pUStk=r0,r21; /* First nested intr? */ \
+ ;; \
+ mov reg=IA64_KR(CURRENT); /* If normal, use actual current */ \
+(pKStk) add r21 = 1,r21; /* Increment the nested count */ \
+ ;; \
+(pKStk) st4 [r20] = r21; /* Mark that we're above a ULI */
+
+#else /* CONFIG_ULI */
# define MINSTATE_GET_CURRENT(reg) mov reg=IA64_KR(CURRENT)
+#endif /* CONFIG_ULI */
# define MINSTATE_START_SAVE_MIN MINSTATE_START_SAVE_MIN_VIRT
# define MINSTATE_END_SAVE_MIN MINSTATE_END_SAVE_MIN_VIRT
#endif
@@ -72,6 +127,21 @@
# define MINSTATE_END_SAVE_MIN MINSTATE_END_SAVE_MIN_PHYS
#endif

+#if defined(CONFIG_ULI) && !defined(MINSTATE_PHYS)
+/*
+ * pLvSys is true if we're right on top of a ULI
+ * pUStk is true if (we're not in a ULI) OR (well above a ULI)
+ * pLvSys and pUStk are opposites
+ */
+#define CHECK_USR \
+(pLvSys) mov r17 = 0x1; /* Pretend we're on the user stack */ \
+(pUStk) ld1 r17=[r16]; /* load current->thread.on_ustack flag */
+
+#else /* CONFIG_ULI */
+#define CHECK_USR \
+ ld1 r17=[r16]; /* load current->thread.on_ustack flag */
+#endif /* CONFIG_ULI */
+
/*
* DO_SAVE_MIN switches to the kernel stacks (if necessary) and saves
* the minimum state necessary that allows us to turn psr.ic back
@@ -110,7 +180,7 @@
;; \
adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r16; \
;; \
- ld1 r17=[r16]; /* load current->thread.on_ustack flag */ \
+ CHECK_USR; \
st1 [r16]=r0; /* clear current->thread.on_ustack flag */ \
adds r1=-IA64_TASK_THREAD_ON_USTACK_OFFSET,r16 \
/* switch from user to kernel RBS: */ \
diff -urN linux-2.6.11/arch/ia64/kernel/traps.c linux-2.6.11-uli/arch/ia64/kernel/traps.c
--- linux-2.6.11/arch/ia64/kernel/traps.c 2005-03-02 01:38:26.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/traps.c 2005-03-10 06:45:50.655183125 -0600
@@ -15,6 +15,7 @@
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <linux/hardirq.h>
+#include <linux/uli.h>

#include <asm/fpswa.h>
#include <asm/ia32.h>
@@ -181,6 +182,10 @@
sig = SIGTRAP; code = TRAP_BRKPT;
}
}
+
+ /* If a ULI caused this, abort it */
+ uli_trap(sig, regs);
+
siginfo.si_signo = sig;
siginfo.si_errno = 0;
siginfo.si_code = code;
@@ -375,6 +380,8 @@
return rv;
}
#endif
+ /* If a ULI caused this abort it */
+ uli_trap(SIGILL, &regs);

sprintf(buf, "IA-64 Illegal operation fault");
die_if_kernel(buf, &regs, 0);
@@ -408,6 +415,9 @@
"Unknown fault 13", "Unknown fault 14", "Unknown fault 15"
};

+ /* If a ULI caused this abort it */
+ uli_trap(SIGILL, &regs);
+
if ((isr & IA64_ISR_NA) && ((isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH)) {
/*
* This fault was due to lfetch.fault, set "ed" bit in the psr to cancel
diff -urN linux-2.6.11/arch/ia64/kernel/uli_asm.S linux-2.6.11-uli/arch/ia64/kernel/uli_asm.S
--- linux-2.6.11/arch/ia64/kernel/uli_asm.S 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/kernel/uli_asm.S 2005-03-21 08:51:01.522668631 -0600
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ * Michael A. Raymond <mraymond@xxxxxxx>
+ */
+
+/*
+ * This file is part of the User Level Interrupt (ULI) feature.
+ *
+ * ULI 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.
+ *
+ * ULI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ULI; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <asm/asmmacro.h>
+#include <asm/offsets.h>
+
+/*
+ * We save the current state of the CPU and jump into
+ * uli_setup_eret.
+ */
+GLOBAL_ENTRY(uli_goto_user)
+ alloc r14=ar.pfs,1,0,1,0
+ flushrs
+ ;;
+ mov r2=in0 // Index into ULI context save area
+ add r3=8,in0 // Ditto
+ mov r24=pr // Save the predicate register
+ mov r8=ar.unat
+ mov r15=ar.fpsr // Save the FP status reg since the user might modify it
+ mov r9=in0 // Use ULI storage spot for UNAT calc
+ mov r23=ar.bsp // Get the BSP return it
+ mov r16=rp // b0
+ mov r17=b1
+ ;;
+.mem.offset 0,0; st8.spill.nta [r2]=gp,16 // r1 (gp)
+.mem.offset 8,0; st8.spill.nta [r3]=r4,16 // r4
+ mov r18=b2
+ ;;
+.mem.offset 0,0; st8.spill.nta [r2]=r5,16 // r5
+.mem.offset 8,0; st8.spill.nta [r3]=r6,16 // r6
+ extr.u r9=r9,3,6 // Start calculating post spill ar.unat
+ ;;
+.mem.offset 0,0; st8.spill.nta [r2]=r7,16 // r7
+.mem.offset 8,0; st8.spill.nta [r3]=sp,16 // r12 (sp)
+ sub r10=64,r9
+ ;;
+ mov ar.rsc = 0 // Put in lazy mode so we can read the folowing ar's
+ mov r25=ar.unat // Get UNAT after spill
+ mov out0=in0 // Pass on the ULI to use
+ ;;
+ mov r22=ar.rnat
+ mov ar.rsc = 3 // Return to regular kernel mode
+ shr.u r11=r25,r9
+ ;;
+ mov ar.unat=r8 // Restore the original UNAT after gr spill
+ ;;
+ st8.nta [r2]=r8,16 // save caller's unat
+ st8.nta [r3]=r15,16 // save fpsr
+ mov r19=b3
+ ;;
+ st8.nta [r2]=r16,16 // b0
+ st8.nta [r3]=r17,16 // b1
+ mov r20=b4
+ ;;
+ st8.nta [r2]=r18,16 // b2
+ st8.nta [r3]=r19,16 // b3
+ mov r21=b5
+ ;;
+ st8.nta [r2]=r20,16 // b4
+ st8.nta [r3]=r21,16 // b5
+ shl r25=r25,r10
+ ;;
+ st8.nta [r2]=r14,16 // ar.pfs
+ st8.nta [r3]=r22,16 // ar.rnat
+ or r25=r25,r11 // Arive at post spill ar.unat
+ ;;
+ st8.nta [r2]=r24,16 // pr
+ st8.nta [r3]=r23,16 // ar.bsp
+ mov r26=ar.lc
+ ;;
+ st8.nta [r2]=r25 // ar.unat - post save
+ st8.nta [r3]=r26 // ar.lc
+ ;;
+ br.call.sptk.many b6=uli_setup_eret
+ /*NOTREACHED*/
+END(uli_goto_user)
+
+/*
+ * Longjmp back to the state saved in uli_goto_user. From there
+ * return to uli_goto_user's calling function.
+ */
+GLOBAL_ENTRY(uli_return_from_user)
+ alloc r8=ar.pfs,1,0,0,0
+ invala // virt. -> phys. regnum mapping may change
+ add r4=0x90,in0 // &post unat
+ ;;
+ ld8 r11=[r4] // load post unat
+ mov r27=ar.rsc // Save this for restoring later
+ extr.u r9=in0, 3, 6 // Start converting the ar.unat to use
+ ;;
+ sub r8 = 64, r9
+ shl r10 = r11, r9
+ mov r2=in0
+ ;;
+ shr.u r11=r11, r8
+ add r3=8,in0 // r3 <- &jmpbuf.r4
+ ;;
+ or r11 = r11, r10 // Complete converting the ar.unat
+ ;;
+ mov ar.unat = r11
+ add r8 = ULI_SAVED_EPC_OFFSET, in0
+ ;;
+ ld8.fill.nta gp=[r2],16 // r1 (gp)
+ ld8.fill.nta r4=[r3],16 // r4
+ ;;
+ ld8.fill.nta r5=[r2],16 // r5
+ ld8.fill.nta r6=[r3],16 // r6
+ ;;
+ ld8.fill.nta r7=[r2],16 // r7
+ ld8.fill.nta sp=[r3],16 // r12 (sp)
+ ;;
+ ld8.nta r28=[r2],16 // caller's unat
+ ld8.nta r29=[r3],16 // fpsr
+ ;;
+ ld8.nta r16=[r2],16 // b0
+ ld8.nta r17=[r3],16 // b1
+ ;;
+ ld8.nta r18=[r2],16 // b2
+ ld8.nta r19=[r3],16 // b3
+ mov b0=r16
+ ;;
+ ld8.nta r20=[r2],16 // b4
+ ld8.nta r21=[r3],16 // b5
+ mov b1=r17
+ ;;
+ ld8.nta r10=[r2],16 // ar.pfs
+ ld8.nta r22=[r3],16 // ar.rnat
+ mov b2=r18
+ ;;
+ ld8.nta r24=[r2],16 // pr
+ ld8.nta r23=[r3],16 // ar.bsp
+ mov b3=r19
+ ;;
+ ld8.nta r26=[r3] // ar.lc
+ ld8 r8=[r8] // cr.iip
+ mov b4=r20
+ ;;
+ rsm psr.ic | psr.i // Allow cr.iip to be set later
+ mov ar.pfs=r10
+ mov b5=r21
+ ;;
+ srlz.d // Make sure the disabling of interrupts is seen
+ mov ar.fpsr=r29 // restore fpsr
+ ;;
+ mov ar.rsc=0 // Put RSE into lazy mode
+ loadrs
+ ;;
+ mov cr.iip = r8 // restored originally preempted PC
+ mov ar.lc = r26
+ ;;
+ mov.m ar.bspstore=r23 // restore ar.bspstore
+ mov.m ar.unat=r28 // restore caller's unat
+ ;;
+ mov.m ar.rnat=r22 // restore ar.rnat
+ mov.m ar.rsc=r27 // restore ar.rsc
+ ;;
+ ssm psr.ic | psr.i // Reenable interrupts
+ ;;
+ srlz.d // Make sure that the above enabling of interrupts is seen
+ mov pr=r24,-1
+ br.ret.sptk.many rp
+END(uli_return_from_user)
+
+/*
+ * This is called from uli_setup_eret to do an rfi into the owning process's
+ * ULI handler. We have to:
+ * - Save the actual current's PC when this interrupt occured
+ * - Save the actual current's sp & bspstore when this interrupt occured
+ * - Set the pc, sp, bspstore, gp, etc
+ * - Reenable interrupts on our way out
+ */
+GLOBAL_ENTRY(uli_eret)
+ mov r20 = ar.bsp // Save for bspstore loading on doubly nested intrs
+ alloc r21=ar.pfs,2,0,0,0 // We have two inputs
+ mov r22 = IA64_PT_REGS_SIZE // later subtraction wants general regs
+ ;;
+ flushrs // Done after the alloc, allow switching of the bspstore
+ movl r21 = 0x8000000000000000 // valid & empty frame marker
+ ;;
+ sub r22 = sp, r22 // sp - PT_REGS Make's handling nested intrs easier
+ rsm psr.ic // Turn off so that we can change cr.ifs
+ add r17 = ULI_INTR_SP_OFFSET, in0 // &uli_intr_sp
+ ;;
+ srlz.d // make sure everyone has seen the status change
+ st8 [r17] = r22 // save mod'd sp for use in doubly nested interrupts
+ add r18 = ULI_INTR_BSPSTORE_OFFSET, in0 // &uli_intr_bspstore
+ ;;
+ mov cr.ifs = r21 // Initialize their current frame marker
+ st8 [r18] = r20 // save parent's bspstore
+ add r17 = ULI_PC_OFFSET, in0 // &uli_pc
+ ;;
+ mov r22 = cr.iip // Get PC of when we took this interrupt
+ ld8 r21 = [r17] // Load ULI handler's PC
+ add r18 = ULI_SAVED_EPC_OFFSET, in0 // &uli_epc
+ ;;
+ mov cr.iip = r21 // rfi will load this as handler's PC
+ mov cr.ipsr = in1 // User space status register values to use
+ add r19 = ULI_SP_OFFSET, in0 // &uli_sp
+ ;;
+ st8 [r18] = r22 // Save PC of when we took this interrupt
+ ld8 r12 = [r19] // Set the user's sp
+ add r20 = ULI_GP_OFFSET, in0 // &uli_gp
+ ;;
+ ld8 r1 = [r20] // Set the user's gp
+ add r17 = ULI_ARG_OFFSET, in0 // &uli_funcarg
+ add r18 = ULI_BSPSTORE_OFFSET,in0 // &uli_bspstore
+ ;;
+ ld8 r20 = [r17] // Load the argument for the ULI handler
+ ld8 r21 = [r18] // Load user's bspstore to use
+ add r19 = ULI_TP_OFFSET,in0 // &uli_threadp
+ ;;
+ mov ar.rsc = 0 // lazy mode for bspstore switch
+ invala // virtual->physical mapping changed, invalidate the ALAT
+ ;;
+ mov ar.bspstore = r21 // Set the user's bspstore
+ mov ar.rsc = 0xf // standard user mode
+ mov r32 = r20 // handler's argument
+ ;;
+ ssm psr.i | psr.ic // reenable interrupts
+ ld8 r13 = [r19] // Set the user's thread pointer
+ ;;
+ rfi
+END(uli_eret)
diff -urN linux-2.6.11/arch/ia64/mm/fault.c linux-2.6.11-uli/arch/ia64/mm/fault.c
--- linux-2.6.11/arch/ia64/mm/fault.c 2005-03-02 01:38:32.000000000 -0600
+++ linux-2.6.11-uli/arch/ia64/mm/fault.c 2005-03-10 06:45:51.190332627 -0600
@@ -9,6 +9,7 @@
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
+#include <linux/uli.h>

#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -84,6 +85,9 @@
struct siginfo si;
unsigned long mask;

+ /* If a ULI caused this abort it */
+ uli_trap(SIGSEGV, regs);
+
/*
* If we're in an interrupt or have no user context, we must not take the fault..
*/
diff -urN linux-2.6.11/drivers/base/Kconfig linux-2.6.11-uli/drivers/base/Kconfig
--- linux-2.6.11/drivers/base/Kconfig 2005-03-02 01:37:49.000000000 -0600
+++ linux-2.6.11-uli/drivers/base/Kconfig 2005-03-10 06:45:51.386619214 -0600
@@ -37,4 +37,14 @@

If you are unsure about this, say N here.

+config ULI
+ bool "User Level Interrupt Support"
+ depends on IA64 && EXPERIMENTAL
+ default n
+ help
+ This enables hardware interrupts to be handled by software in
+ user space. This gives user processes more control over hardware
+ and can enable driver development with reduced risk to system
+ stability.
+
endmenu
diff -urN linux-2.6.11/drivers/base/Makefile linux-2.6.11-uli/drivers/base/Makefile
--- linux-2.6.11/drivers/base/Makefile 2005-03-02 01:38:20.000000000 -0600
+++ linux-2.6.11-uli/drivers/base/Makefile 2005-03-10 06:45:51.631733311 -0600
@@ -7,6 +7,7 @@
obj-y += power/
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
+obj-$(CONFIG_ULI) += uli.o

ifeq ($(CONFIG_DEBUG_DRIVER),y)
EXTRA_CFLAGS += -DDEBUG
diff -urN linux-2.6.11/drivers/base/uli.c linux-2.6.11-uli/drivers/base/uli.c
--- linux-2.6.11/drivers/base/uli.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11-uli/drivers/base/uli.c 2005-03-23 09:13:31.137340683 -0600
@@ -0,0 +1,687 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ * Michael A. Raymond <mraymond@xxxxxxx>
+ */
+
+/*
+ * This file is part of the User Level Interrupt (ULI) feature.
+ *
+ * ULI 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.
+ *
+ * ULI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ULI; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * User Level Interrupts (ULIs) are a method of handling interrupts in
+ * user space that would normally be entirely handled within the kernel.
+ * They are useful for fault tolerant interrupt handling code and for
+ * programs that do a lot of memory mapped I/O.
+ *
+ * ULIs work by doing a return-from-interrupt (RFI) from the kernel
+ * interrupt handler to a user space function specified by the user. The
+ * code is run entirely in user space and so faults can be contained and
+ * it can access all of the user's address space. The only limitation is
+ * that system calls cannot be made from the handler function. The
+ * supporting library, libuli, does use system calls to return to the
+ * kernel, but this is the only allowed or possible usage.
+ *
+ * -------
+ * -------- ------------------------ | User |
+ * |Shared | | User -------- <-|-|Process|
+ * |Memory /| |Process <-->| ULI | | -------
+ * |Mapped |<------|----------->|Function| | -------
+ * |Memory | | -------- <-|-----| User |
+ * -------- |Memory Access A | |Process|
+ * ------------------------ -------
+ * User Level |
+ * --------------------------------------------------------------------
+ * Interrupt Level |
+ * ------
+ * |Kernel|
+ * |ISR | <--- Interrupt
+ * ------
+ *
+ * The specific details of the ULI implementation are as follows. When
+ * a kernel interrupt handler that has been registered with by the user
+ * to use a ULI is run, it calls into uli_handler. This function switches
+ * the virtual memory settings around so that the address space of the
+ * registering program is used. A function called by uli_handler does an rfi
+ * into the user's address space after first saving the system state. When
+ * the user's handler is completed, libuli does a write() call on the file
+ * descriptor representing its /dev/uli instance, which results in uli_write
+ * executing. uli_write restores the state saved previously along with the
+ * proper address space. uli_handler can then return to the previously
+ * executing kernel code.
+ *
+ * Special care must be taken when an interrupt occurs while a ULI handler
+ * is already running. Even though the system was running in user space,
+ * it must not start from the top of the kernel stack; it needs to start
+ * from where the kernel stack was before it did the rfi. This is kept track
+ * of through uli_cur[] and each ULI's uli_double field. If a ULI is
+ * currently running on a CPU then uli_cur[cpu] will point to the top level
+ * one. When an interrupt occurs during a ULI, the ULI's uli_double field is
+ * incremented. If the field is already >= 1, then no special steps need to
+ * be taken. If a ULI happens to run nested above another ULI, then for each
+ * new nested interrupt checks and updates will only be made against the top
+ * most ULI.
+ *
+ * libuli is a very simple wrapper library to make ULI usage easier. It
+ * is by no means required though.
+ *
+ * -------
+ * -> |handler|->write()---->----------
+ * | ------- | User Space
+ *--------------------------------------------------------------------
+ * | ------ | Kernel Space
+ * | <-| | uli_return_from_user |
+ * rfi | | | |
+ * | l ------ |
+ * | o | | uli_return |
+ * ------ n | | |
+ * uli_eret | | g ------ |
+ * | | j | | uli_write |
+ * ------ u | | |
+ * uli_setup_eret| | m ------ V
+ * | | p | | uli_syscall |
+ * ------ | | | |
+ * uli_goto_user | |< ------ |
+ * | | |ptregs| |
+ * A A |
+ * \-> ------ -----------<-------------
+ * uli_handler | |
+ * | |
+ * ------
+ * Kernel Handler | |
+ * | |
+ *
+ * Locks
+ * There is one lock in the ULI which is called through ULILOCK and
+ * ULIUNLOCK. This lock protects ULI creation / destruction. Each handler
+ * can only run on one CPU. To destroy a ULI, the function should run on
+ * its CPU, raise the interrupt level to keep the ULI from running, and then
+ * remove the ULI from the list of ULIs that can be run from that CPU.
+ *
+ * Because ULIs are referenced through the file system we rely on the file
+ * system's standard methods for reference counting and the like. Only
+ * processes which have a copy of a ULI's file open can block on it. The ULI
+ * keeps a reference to its registering process's address space.
+ *
+ * Porting
+ * To port ULI to a new architecture the following steps should be taken:
+ * - Add any architecture specific changes for the handler to libuli
+ * - Create a new <asm/uli_plat.h>. This file must define:
+ * - uli_context - System state
+ * - uli_sr - CPU status register
+ * - ULI_IRQS - # of lines to support
+ * - uli_delay - Calculate cut off time for handler
+ * - uli_setup_args - One time arch specific set up routine
+ * - uli_enable_fpu - Reenable the kernel's FPU usage (optional)
+ * - uli_disable_fpu - Disable the handler's FPU usage (optional)
+ * - Add uli_goto_user, uli_return_from_user, and uli_eret functions.
+ * uli_goto_user saves system state and uli_return_from user restores it.
+ * uli_eret prepares the CPU and does the rfi into the user's handler.
+ * - Modify the syscall entry and interrupt state saving / restoring code
+ * - Modify the architecture's IRQ processing code to check if it should
+ * call into the ULI code.
+ */
+
+#include <linux/elfcore.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/uli.h>
+
+#include <asm/mmu_context.h>
+#include <asm/semaphore.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/uli_plat.h>
+
+#define ULI_NAME "uli"
+
+static struct uli * uli_table[MAX_ULIS]; /* Points to all allocated ULIs */
+
+static spinlock_t uli_lock; /* Protects uli_table[] */
+
+#define ULILOCK(_flags) spin_lock_irqsave(&uli_lock, _flags)
+#define ULIUNLOCK(_flags) spin_unlock_irqrestore(&uli_lock, _flags)
+
+/* The ULI currently executing on each CPU */
+DEFINE_PER_CPU(struct uli *, uli_cur);
+
+static void uli_free(struct uli *);
+static int uli_ioctl(struct inode *, struct file *, unsigned int,
+ unsigned long);
+static int uli_release(struct inode *, struct file *);
+static ssize_t uli_write(struct file *, const char *, size_t, loff_t *);
+
+/* The file operations for /dev/uli */
+static struct file_operations uli_fops = {
+ ioctl: uli_ioctl,
+ release: uli_release,
+ write: uli_write,
+};
+
+/* The registration structure for /dev/uli */
+static struct miscdevice uli_miscdev = {
+ MISC_DYNAMIC_MINOR,
+ ULI_NAME,
+ &uli_fops,
+};
+
+/*
+ * Stop the passed ULI from running any more.
+ */
+static void
+uli_irq_teardown(struct uli * uli)
+{
+ /* Give up the IRQ */
+ free_irq((int)uli->teardownarg0, uli);
+}
+
+/*
+ * Help get the CPU ready to RFI into the ULI handler. uli_eret
+ * will do the remainder of the work.
+ */
+void
+uli_setup_eret(struct uli * uli)
+{
+ struct uli ** cur_uli = &per_cpu(uli_cur, smp_processor_id());
+ union uli_sr sr;
+
+ /* Turn off all interrupts and get the CPU status */
+ local_irq_save(sr.psr_long);
+ uli->uli_saved_sr.psr_long = sr.psr_long;
+
+ /* Save previous ULI */
+ uli->uli_prev = *cur_uli;
+
+ /* Tell interrupt handling code that we're in a ULI */
+ uli->uli_double = 0;
+
+ /*
+ * Set the current ULI. We do this after setting uli_prev and
+ * uli_double so that if another ULI comes in it will see a
+ * consistent view.
+ */
+ *cur_uli = uli;
+
+ /* Set up ULI thread's address space */
+ if (likely(current->active_mm != uli->uli_mm)) {
+ activate_mm(current->active_mm, uli->uli_mm);
+ }
+
+ /*
+ * Set a time stamp so that we can tell if the ULI has
+ * been running for too long and abort it.
+ */
+ uli->uli_tstamp = uli_delay();
+
+ /* Reeanble interrupts and RFI into the ULI handler */
+ uli_eret(uli, uli->uli_sr.psr_long);
+
+ /*NOTREACHED*/
+ return;
+}
+
+/*
+ * This is called by a low level interrupt handler to call into
+ * the user's ULI handler. During de-registration the
+ * specific device type code guarantees proper locking and that
+ * this uli won't be free'd out from under us.
+ */
+void
+uli_handler(struct uli * uli)
+{
+ struct uli ** cur_uli = &per_cpu(uli_cur, smp_processor_id());
+
+ /* Make sure this ULI is running where it's supposed to */
+ if (unlikely(uli->uli_cpu != smp_processor_id())) {
+ panic("ULI %p running on %d instead of %d\n",
+ uli,
+ smp_processor_id(),
+ uli->uli_cpu);
+ return;
+ }
+
+ /* Save the current context and call uli_setup_eret to do the rfi */
+ uli_goto_user(uli);
+
+ /* The ULI is finished, restore VM info */
+ if (unlikely(!current->mm)) {
+ enter_lazy_tlb(current->active_mm, current);
+ } else if (likely(current->active_mm != uli->uli_mm)) {
+ activate_mm(uli->uli_mm, current->active_mm);
+ }
+
+ /* Reenable the FPU and interrupts */
+ uli_enable_fpu();
+ local_irq_restore(uli->uli_saved_sr.psr_long);
+
+ /* Restore any preempted ULI */
+ *cur_uli = uli->uli_prev;
+
+ /*
+ * Check if we're expected to signal the ULI's creating process.
+ * If we can't find it anymore then we destroy the ULI in order to
+ * free up the affected address space and avoid weird situations.
+ */
+ if (uli->uli_sig &&
+ kill_proc_info(uli->uli_sig, SEND_SIG_FORCED,
+ uli->uli_pid)) {
+ uli_free(uli);
+ }
+
+ return;
+}
+
+/*
+ * Run all the ULIs registered with the specified device. This is
+ * assumed to be run at interrupt level so no locking of the ULI
+ * chain is needed.
+ */
+/*ARGSUSED*/
+static irqreturn_t
+uli_IRQ_handler(int dev, void * dev_id, struct pt_regs * regs)
+{
+ struct uli * uli = (struct uli *) dev_id;
+
+ /* Call the handler */
+ if (uli->uli_cpu == smp_processor_id()) {
+ uli_handler(uli);
+ }
+
+ return 0;
+}
+
+/*
+ * This is called at ULI completion time to return to the initiating
+ * code. A ULI is considered to have completed when it successfully
+ * returns to the kernel or is aborted due to some fault.
+ */
+void
+uli_return(int sig, struct pt_regs * regs)
+{
+ struct uli * uli = per_cpu(uli_cur, local_cpu_data->cpu);
+
+ /* Save sig to be sent to ULI thread, if any */
+ uli->uli_sig = sig;
+
+ /*
+ * If we were passed a particular context to store, copy them
+ * into the ULI structure. This currently only happens during
+ * ULI abort because it's "too hard" to store the context data
+ * into uli_eframe during the initial context save.
+ */
+ if (regs) {
+ memcpy(&uli->uli_eframe, regs, sizeof(struct pt_regs));
+ }
+
+ /* Return to where we fired off the ULI in uli_callup. */
+ uli_return_from_user(uli);
+
+ /*NOTREACHED*/
+}
+
+/*
+ * Generic set up code.
+ */
+int
+uli_new(struct uli ** uli_spot, struct uliargs * uargs)
+{
+ struct uli * uli;
+ int i;
+ unsigned long flags;
+
+ /* Validate the number of semaphores requested */
+ if (uargs->nsemas > ULI_MAX_SEMAS) {
+ return -EINVAL;
+ }
+
+ /* Allocate per-ULI data */
+ uli = kmalloc(ULI_SIZE(uargs->nsemas), GFP_KERNEL);
+ memset(uli, 0, ULI_SIZE(uargs->nsemas));
+ *uli_spot = uli;
+
+ /* Get a reference to the current address space */
+ if (!get_task_mm(current)) {
+ kfree(uli);
+ return -EBUSY;
+ }
+
+ /* Protect the global ULI table */
+ ULILOCK(flags);
+
+ /* Find a free spot from which to look up the ULI */
+ for (i = 0; i < MAX_ULIS; i++) {
+ if (uli_table[i] == NULL)
+ break;
+ }
+
+ /* No space left */
+ if (i == MAX_ULIS) {
+ ULIUNLOCK(flags);
+ kfree(uli);
+ return -EBUSY;
+ }
+
+ /* Record in the master table */
+ uli_table[i] = uli;
+ uli->uli_index = i;
+
+ /* Get the current context to use. */
+ uli->uli_pid = current->group_leader->pid;
+ uli->uli_mm = current->mm;
+
+ /* Set up the handler code */
+ uli->uli_sp = uargs->sp;
+ uli->uli_pc = uargs->pc;
+ uli->uli_gp = uargs->gp;
+ uli->uli_funcarg = uargs->funcarg;
+
+ /* Set up platform specific handler values */
+ uli_setup_args(uli, uargs);
+
+ /*
+ * Disable the handler from using the FPU. We don't save FPU
+ * state for speed reasons, so this is necessary. Anyone
+ * nested above a ULI handler will either be another ULI or
+ * other kernel code.
+ */
+ uli_disable_fpu(uli->uli_sr);
+
+ /* Set up the semaphores */
+ uli->uli_nsemas = uargs->nsemas;
+ for (i = 0; i < uli->uli_nsemas; i++) {
+ sema_init(&uli->uli_sema[i], 0);
+ }
+
+ ULIUNLOCK(flags);
+
+ return 0;
+}
+
+/*
+ * Sleep on the passed semaphore of the passed ULI.
+ */
+static int
+uli_down(struct uli * uli, int sema)
+{
+ /* Check for a valid semaphore */
+ if (sema < 0 || sema >= uli->uli_nsemas) {
+ return -EINVAL;
+ }
+
+ /* Block allowing signals to wake us */
+ return down_interruptible(&uli->uli_sema[sema]);
+}
+
+/*
+ * Wake up the next thread on the passed semaphore of the passed ULI.
+ */
+static int
+uli_up(struct uli * uli, int sema)
+{
+ /* Check for a valid semaphore */
+ if (sema < 0 || sema >= uli->uli_nsemas) {
+ return -EINVAL;
+ }
+
+ /* Wake the next thread */
+ up(&uli->uli_sema[sema]);
+
+ return 0;
+}
+
+/*
+ * Destroy the passed ULI structure. It first disconnects the ULI
+ * from its interrupt source then frees it.
+ */
+void
+uli_free(struct uli * uli)
+{
+ unsigned long flags;
+ cpumask_t oldmask;
+
+ /* Reschedule onto the ULI's CPU */
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(uli->uli_cpu));
+
+ /* Block all interrupts so we know that the ULI isn't running */
+ ULILOCK(flags);
+
+ /* Disable the ULI */
+ uli_table[uli->uli_index] = NULL;
+ if (uli->uli_teardown) {
+ uli->uli_teardown(uli);
+ }
+
+ /* Reenable interrupts */
+ ULIUNLOCK(flags);
+
+ /* Reschedule */
+ set_cpus_allowed(current, oldmask);
+
+ /* Release the reference to its address space */
+ mmput(uli->uli_mm);
+
+ /* No longer need the per-ULI data */
+ kfree(uli);
+}
+
+/*
+ * When the per-interrupt opened file of /dev/uli is closed,
+ * this gets called so that we can free up any associated state.
+ */
+/*ARGSUSED*/
+static int
+uli_release(struct inode * inode, struct file * filp)
+{
+ struct uli * uli = filp->private_data;
+
+ /* If /dev/uli was opened but a ULI was never registered */
+ if (!uli) {
+ return 0;
+ }
+
+ /* For debugging purposes, clear the pointer to the ULI */
+ filp->private_data = NULL;
+
+ /* Call the generic free'ing function */
+ uli_free(uli);
+
+ return 0;
+}
+
+/*
+ * Handle ULI control operations
+ */
+static int
+uli_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct uliargs uargs;
+ struct uli * uli;
+ int err;
+ unsigned long flags;
+
+ switch (cmd) {
+ case IOC_ULI_REG_IRQ:
+ /* We need to use the private_data field ourselves */
+ if (filp->private_data) {
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&uargs, (void*)arg, sizeof(uargs))) {
+ return -EFAULT;
+ }
+
+ /* Validate the line requested */
+ if (uargs.id < 0 || uargs.id > ULI_IRQS) {
+ return -EINVAL;
+ }
+
+ /* Validate the CPU */
+ if (uargs.intarg > num_online_cpus()) {
+ return -EINVAL;
+ }
+
+ /* Create the ULI */
+ if ((err = uli_new(&uli, &uargs))) {
+ return err;
+ }
+
+ /* Store which line we registered with */
+ uli->teardownarg0 = uargs.id;
+
+ /* Mark which CPU the ULI can occur on */
+ uli->uli_cpu = uargs.intarg;
+
+ /* Request the IRQ */
+ err = request_irq(uargs.id, uli_IRQ_handler, SA_SHIRQ,
+ "ULI", uli);
+ if (err) {
+ uli_free(uli);
+ return err;
+ }
+
+ /* Store the ULI for later teardown */
+ filp->private_data = uli;
+
+ /* Link the ULI into the interrupt line structure */
+ ULILOCK(flags);
+ uli->uli_teardown = uli_irq_teardown;
+ ULIUNLOCK(flags);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Handle fast ULI operations. We don't use ioctl because of
+ * the BKL.
+ */
+/*ARGSUSED*/
+static ssize_t
+uli_write(struct file * file, const char * buf, size_t count,
+ loff_t * f_pos)
+{
+ struct uli * uli = (struct uli *)file->private_data;
+
+ /* The particular command was passed as the buffer address */
+ switch ((long)buf) {
+ case ULI_SLEEP_ADDR:
+ if (uli) {
+ return uli_down(uli, count);
+ }
+ return -EINVAL;
+ break;
+ case ULI_WAKEUP_ADDR:
+ if (uli) {
+ return uli_up(uli, count);
+ }
+ return -EINVAL;
+ break;
+ case ULI_RESOLV_ADDR: /* No action needed */
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Handle fast ULI operations. We make this look like sys_write()
+ * because libuli uses write() to return to the kernel from the handler.
+ * We can't always use uli_write() because the "current" running when
+ * the ULI handler does may not have the same file descriptor table.
+ */
+/*ARGSUSED*/
+asmlinkage ssize_t
+uli_syscall(int fd, const char * buf, size_t count,
+ loff_t * f_pos)
+{
+ struct uli * uli = per_cpu(uli_cur, local_cpu_data->cpu);
+
+ /* The particular command was passed as the buffer address */
+ switch ((long)buf) {
+ case ULI_RETURN_ADDR: /* First for speed */
+ uli_return(0, NULL);
+ /*NOTREACHED*/
+ break;
+ case ULI_WAKEUP_ADDR:
+ return uli_up(uli, count);
+ default:
+ /*
+ * No actual syscalls or any other ULI commands should be
+ * called from a ULI handler.
+ */
+ uli_return(SIGILL, (struct pt_regs *)uli->uli_intr_sp);
+ /*NOTREACHED*/
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * This is called at startup time. It creates the /dev
+ * ULI entry and handles setting up any other initial state.
+ */
+static int __init
+uli_init(void)
+{
+ int res, i;
+
+ /* They should already be NULL, but it can't hurt to be thorough */
+ for (i = 0; i < NR_CPUS; i++) {
+ per_cpu(uli_cur, i) = NULL;
+ }
+
+ /* Initialize the storage table */
+ for (i = 0; i < MAX_ULIS; i++) {
+ uli_table[i] = NULL;
+ }
+
+ /* Initialize the create / destroy lock */
+ spin_lock_init(&uli_lock);
+
+ /* Create the /dev ULI entry */
+ if ((res = misc_register(&uli_miscdev)) < 0) {
+ printk(KERN_ERR "%s: failed to register device, %d\n",
+ ULI_NAME, res);
+ return res;
+ }
+
+ return 0;
+}
+
+__initcall(uli_init);
+
+/*
+ * Should another kernel module wish to use ULIs, these are the minimum
+ * needed routines.
+ */
+EXPORT_SYMBOL(uli_free);
+EXPORT_SYMBOL(uli_handler);
+EXPORT_SYMBOL(uli_new);
+EXPORT_SYMBOL(uli_return);
diff -urN linux-2.6.11/include/asm-ia64/uli_plat.h linux-2.6.11-uli/include/asm-ia64/uli_plat.h
--- linux-2.6.11/include/asm-ia64/uli_plat.h 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11-uli/include/asm-ia64/uli_plat.h 2005-03-10 06:45:52.621955200 -0600
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ * Michael A. Raymond <mraymond@xxxxxxx>
+ */
+
+/*
+ * This file is part of the User Level Interrupt (ULI) feature.
+ *
+ * ULI 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.
+ *
+ * ULI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ULI; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ARCH_ULI_H
+#define __ARCH_ULI_H
+
+#include <linux/ptrace.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/processor.h>
+
+/* Buffer to save minimum CPU context in */
+#define ULI_CTXTLEN 20
+typedef struct {
+ long _data[ULI_CTXTLEN];
+} uli_context __attribute__((__aligned__(16)));
+
+/* Simple union to make dealing with the processor state easier */
+union uli_sr {
+ struct ia64_psr psr_struct;
+ unsigned long psr_long;
+};
+
+#define ULI_IRQS NR_IRQS
+
+/* Some arbitrary point in the future */
+#define uli_delay() (ia64_get_itc() + 10000)
+
+/*
+ * Set up IA64 specific ULI handler values. We must align the
+ * stack, get r13, and set the IP to the first instruction in the
+ * passed bundle.
+ */
+#define uli_setup_args(uli, uargs) \
+{ \
+ struct pt_regs * regs = ia64_task_regs(current); \
+ unsigned long addr = (unsigned long)uli->uli_sp - \
+ uargs->stacksize; \
+\
+ /* The BSP must be 8-byte aligned */ \
+ addr = (addr + 0x7) & ~0x7; \
+ uli->uli_arch0 = (caddr_t)addr; \
+\
+ /* The sp must be 16-byte aligned */ \
+ addr = (unsigned long)uli->uli_sp & ~0xF; \
+ uli->uli_sp = (caddr_t)addr; \
+\
+ /* Set its user thread pointer */ \
+ uli->uli_arch1 = (void*)regs->r13; \
+\
+ /* Get the status register value to use */ \
+ uli->uli_sr.psr_long = regs->cr_ipsr; \
+\
+ /* Start with the first instruction in the bundle */ \
+ uli->uli_sr.psr_struct.ri = 0; \
+}
+
+/*
+ * Reenable the FPU when the ULI handler is completed
+ */
+#define uli_enable_fpu()
+
+/*
+ * Platform specific disabling of FPU
+ */
+#define uli_disable_fpu(sr) \
+ sr.psr_struct.dfl = 1; \
+ sr.psr_struct.dfh = 1;
+
+#endif /* __ARCH_ULI_H */
diff -urN linux-2.6.11/include/linux/uli.h linux-2.6.11-uli/include/linux/uli.h
--- linux-2.6.11/include/linux/uli.h 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11-uli/include/linux/uli.h 2005-03-23 09:15:07.875463206 -0600
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All rights reserved.
+ *
+ * Michael A. Raymond <mraymond@xxxxxxx>
+ */
+
+/*
+ * This file is part of the User Level Interrupt (ULI) feature.
+ *
+ * ULI 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.
+ *
+ * ULI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ULI; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SYS_ULI_H
+#define __SYS_ULI_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* This is the ULI /dev entry */
+#define ULI_DEV "/dev/uli"
+
+/*
+ * This structure contains the arguments that must be passed to
+ * register ULIs.
+ */
+struct uliargs {
+ /* device independent fields */
+ caddr_t pc; /* PC of handler function */
+ caddr_t gp; /* GP for handler function */
+ caddr_t sp; /* Base SP of stack to use */
+ size_t stacksize; /* Size of stack (needed for IA64) */
+ void * funcarg; /* argument to handler function */
+ unsigned int nsemas; /* ULI's semaphores to sleep on */
+
+ int id; /* Interrupt device specifier */
+ unsigned long intarg; /* Special device parameter */
+};
+
+/* /dev/uli ioctl arguments */
+#define IOC_ULI_REG_IRQ 0x0010 /* Register a ULI for an IRQ line */
+
+/* /dev/uli I/O addresses */
+#define ULI_SLEEP_ADDR 0x100 /* ULI_sleep */
+#define ULI_WAKEUP_ADDR 0x200 /* ULI_wakeup */
+#define ULI_RETURN_ADDR 0x300 /* return from intr */
+#define ULI_RESOLV_ADDR 0x500 /* For pre-resolving symbols */
+
+#define ULI_MAX_SEMAS 32 /* Maximum number supported */
+
+/********************************************************************/
+#if defined(__KERNEL__)
+#if defined(CONFIG_ULI)
+
+#include <linux/config.h>
+#include <linux/elf.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <asm/mmu.h>
+#include <asm/percpu.h>
+#include <asm/semaphore.h>
+#include <asm/uli_plat.h>
+
+#define MAX_ULIS 64 /* Max number of ULIs supported */
+
+/*
+ * This kernel per-ULI structure keeps track off all its state.
+ */
+struct uli {
+ /* This must be first */
+ uli_context uli_context; /* State before user handler */
+
+ caddr_t uli_gp; /* Handler's GP */
+ caddr_t uli_sp; /* Handler's SP */
+ union uli_sr uli_sr; /* Handler's SR */
+ caddr_t uli_pc; /* Handler's PC */
+ void * uli_funcarg; /* Argument to handler */
+ caddr_t uli_arch0; /* Arch specific user register */
+ caddr_t uli_arch1; /* Arch specific user register */
+
+ pid_t uli_pid; /* The registering process */
+ struct mm_struct * uli_mm; /* MM of owning process */
+ unsigned long uli_tstamp; /* Time ULI started */
+
+ unsigned int uli_double; /* In doubly nested interrupt? */
+ unsigned long uli_intr_sp; /* SP at time of goto user */
+ union uli_sr uli_saved_sr; /* SR at time of goto user */
+ caddr_t uli_saved_epc; /* PC interrupted by ULI handled intr */
+ unsigned long uli_intr_arch0; /* Arch reg at goto user */
+
+ short uli_cpu; /* Only this calls the handler */
+ struct uli * uli_next; /* per-dev list of registered ULIs */
+ struct uli * uli_prev; /* ULI preempted by this one */
+ int uli_sig; /* Error status of ULI handler */
+ int uli_index; /* ULI's spot in uli_table[] */
+
+ struct pt_regs uli_eframe; /* Context of failed ULI */
+
+ /* Device dependent teardown func to disconnect interrupt */
+ void (*uli_teardown)(struct uli*); /* Teardown from reg'd dev */
+ unsigned long teardownarg0; /* Per-device teardown data */
+ unsigned long teardownarg1; /* to be referenced from the */
+ unsigned long teardownarg2; /* teardown handler */
+
+ int uli_nsemas; /* # of semaphores in uli_sema[] */
+
+ /* Must be last element */
+ struct semaphore uli_sema[1]; /* So threads can queue off this ULI */
+};
+
+/* Size of the total ULI struct given number of semaphores */
+#define ULI_SIZE(nsemas) ((size_t)&(((struct uli*)0)->uli_sema[nsemas]))
+
+extern void uli_callup(unsigned int);
+extern void uli_eret(struct uli *, unsigned long);
+extern void uli_goto_user(struct uli *);
+extern void uli_handler(struct uli *);
+extern void uli_return(int, struct pt_regs *);
+extern void uli_return_from_user(struct uli *);
+extern void uli_setup_eret(struct uli *);
+
+/*
+ * If we're in a ULI and we commit any kind of illegal
+ * instruction, we abort the ULI and send a signal to the
+ * owning process.
+ */
+static inline void
+uli_trap(int sig, struct pt_regs * regs)
+{
+ extern struct uli * per_cpu__uli_cur;
+ struct uli * uli = per_cpu(uli_cur, local_cpu_data->cpu);
+
+ /*
+ * If uli_double > 1 then the fault wasn't the fault of the
+ * ULI handler.
+ */
+ if (uli &&
+ uli->uli_double == 1) {
+ uli_return(sig, regs);
+ }
+}
+
+#else /* CONFIG_ULI */
+
+#define uli_trap(sig, regs) do {} while (0)
+
+#endif /* CONFIG_ULI */
+#endif /* __KERNEL__ */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __SYS_ULI_H */