diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/include/linux/init_task.h 150-pagg-job.patch/include/linux/init_task.h
--- 090-fakeprom-update.patch/include/linux/init_task.h	Tue Jan 20 19:49:22 2004
+++ 150-pagg-job.patch/include/linux/init_task.h	Wed Jan 21 09:21:48 2004
@@ -108,6 +108,10 @@
 	.proc_lock	= SPIN_LOCK_UNLOCKED,				\
 	.switch_lock	= SPIN_LOCK_UNLOCKED,				\
 	.journal_info	= NULL,						\
+	.pagg_list	= {						\
+		.head	= LIST_HEAD_INIT(tsk.pagg_list.head),		\
+		.sem	= __RWSEM_INITIALIZER(tsk.pagg_list.sem)	\
+	},								\
 }
 
 
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/include/linux/job.h 150-pagg-job.patch/include/linux/job.h
--- 090-fakeprom-update.patch/include/linux/job.h	Wed Dec 31 16:00:00 1969
+++ 150-pagg-job.patch/include/linux/job.h	Wed Jan 21 09:21:48 2004
@@ -0,0 +1,127 @@
+/*
+ * PAGG Job kernel definitions & interfaces
+ *
+ *
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of version 2 of the GNU General Public License 
+ * as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ * 
+ * Further, this software is distributed without any warranty that it is 
+ * free of the rightful claim of any third person regarding infringement 
+ * or the like.  Any license provided herein, whether implied or 
+ * otherwise, applies only to this software file.  Patent licenses, if 
+ * any, provided herein do not apply to combinations of this program with 
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program; if not, write the Free Software 
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
+ */
+
+/*
+ * Description:  This file, include/linux/job.h, contains the data 
+ * 		 structure definitions and functions prototypes used
+ * 		 by other kernel bits that communicate with the job
+ * 		 module.  One such example is Comprehensive System 
+ * 		 Accounting  (CSA).
+ */
+
+#ifndef _LINUX_JOB_H
+#define _LINUX_JOB_H
+
+/* 
+ * ================
+ * GENERAL USE INFO
+ * ================
+ */
+
+/* 
+ * The job start/stop events: These will identify the 
+ * the reason the jobstart and jobend callbacks are being 
+ * called.
+ */
+enum {
+    JOB_EVENT_IGNORE =  0,
+    JOB_EVENT_START =   1,
+    JOB_EVENT_RESTART = 2,
+    JOB_EVENT_END =  3,
+};
+
+
+/* 
+ * =========================================
+ * INTERFACE INFO FOR ACCOUNTING SUBSCRIBERS 
+ * =========================================
+ */
+
+/* To register as a job dependent accounting module */
+struct job_acctmod {
+	int     	type;   /* CSA or something else */
+	int     	(*jobstart)(int event, void *data);
+	int     	(*jobend)(int event, void *data);
+	struct module	*module;
+};
+
+
+/* 
+ * Subscriber type: Each module that registers as a accounting data
+ * "subscriber" has to have a type.  This type will identify the 
+ * the appropriate structs and macros to use when exchanging data.
+ */
+#define JOB_ACCT_CSA	0
+#define JOB_ACCT_COUNT	1 /* Number of entries available */	
+
+
+/*
+ * --------------
+ * CSA ACCOUNTING 
+ * --------------
+ */
+
+/* 
+ * For data exchange betwee job and csa.  The embedded defines
+ * identify the sub-fields
+ */
+struct job_csa {
+#define                 JOB_CSA_JID             001
+	u64		job_id;
+#define                 JOB_CSA_UID             002
+	uid_t		job_uid;
+#define                 JOB_CSA_START           004
+	time_t		job_start;
+#define                 JOB_CSA_COREHIMEM       010
+	u64		job_corehimem;
+#define                 JOB_CSA_VIRTHIMEM       020
+	u64		job_virthimem;
+#define                 JOB_CSA_ACCTFILE        040
+	struct file	*job_acctfile;
+};
+
+
+/* 
+ * ===================
+ * FUNCTION PROTOTYPES
+ * ===================
+ */
+int job_register_acct(struct job_acctmod *);
+int job_unregister_acct(struct job_acctmod *);
+u64 job_getjid(struct task_struct *);
+int job_getacct(u64, int, void *);
+int job_setacct(u64, int, int, void *);
+
+#endif /* _LINUX_JOB_H */
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/include/linux/pagg.h 150-pagg-job.patch/include/linux/pagg.h
--- 090-fakeprom-update.patch/include/linux/pagg.h	Wed Dec 31 16:00:00 1969
+++ 150-pagg-job.patch/include/linux/pagg.h	Wed Jan 21 09:21:48 2004
@@ -0,0 +1,216 @@
+/* 
+ * PAGG (Process Aggregates) interface
+ *
+ * 
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of version 2 of the GNU General Public License 
+ * as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ * 
+ * Further, this software is distributed without any warranty that it is 
+ * free of the rightful claim of any third person regarding infringement 
+ * or the like.  Any license provided herein, whether implied or 
+ * otherwise, applies only to this software file.  Patent licenses, if 
+ * any, provided herein do not apply to combinations of this program with 
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program; if not, write the Free Software 
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
+ */
+
+/*
+ * Description:	This file, include/linux/pagg.h, contains the data
+ *              structure definitions and function prototypes used to
+ *              implement process aggrefates (paggs). Paggs provides a
+ *              generalized was to implement process groupings or
+ *              containers.  Modules use these functions to register
+ *              with the kernel as providers of process aggregation
+ *              containers. The pagg data structures define the
+ *              callback functions and data access pointers back into
+ *              the pagg modules.
+ */
+
+#ifndef _PAGG_H
+#define _PAGG_H
+
+#include <linux/config.h>
+
+/*
+ * Used by task_struct to manage a list of pagg attachments for the task.
+ * The list will be used to hold references to pagg structures.  
+ * These structures define the pagg attachments for the task.  
+ *
+ * STRUCT MEMBERS:
+ * 	list:		The list head pointer for the list of pagg
+ * 			structures.
+ * 	sem:		The semaphore used  to lock the list.
+ */
+struct pagg_list {
+	struct list_head	head;	
+	struct rw_semaphore	sem;
+};
+
+#ifdef CONFIG_PAGG
+
+#define PAGG_NAMELN	32		/* Max chars in PAGG module name */
+
+
+/* Macro used to initialize a pagg_list structure after declaration 
+ *
+ * Macro arguments:
+ * 	l:	the pagg list (struct pagg_list)
+ */
+#define INIT_PAGG_LIST(l)						\
+do {									\
+	INIT_LIST_HEAD(l.head);						\
+	init_rwsem(l.sem);						\
+} while(0)
+	
+
+/*
+ * Used by task_struct to manage list of pagg attachments for the process.  
+ * Each pagg provides the link between the process and the 
+ * correct pagg container.
+ *
+ * STRUCT MEMBERS:
+ *     hook:	Reference to pagg module structure.  That struct
+ *     		holds the name key and function pointers.
+ *     data:	Opaque data pointer - defined by pagg modules.
+ *     entry:	List pointers
+ */
+struct pagg {
+       struct pagg_hook	*hook;
+       void		*data;
+       struct list_head	entry;
+};
+
+/*
+ * Used by pagg modules to define the callback functions into the 
+ * module.
+ *
+ * STRUCT MEMBERS:
+ *     name:           The name of the pagg container type provided by
+ *                     the module. This will be set by the pagg module.
+ *     attach:         Function pointer to function used when attaching
+ *                     a process to the pagg container referenced by 
+ *                     this struct.
+ *     detach:         Function pointer to function used when detaching
+ *                     a process to the pagg container referenced by 
+ *                     this struct.
+ *     init:           Function pointer to initialization function.  This
+ *                     function is used when the module is loaded to attach
+ *                     existing processes to a default container as defined by
+ *                     the pagg module. This is optional and may be set to 
+ *                     NULL if it is not needed by the pagg module.
+ *     data:           Opaque data pointer - defined by pagg modules.
+ *     module:         Pointer to kernel module struct.  Used to increment & 
+ *                     decrement the use count for the module.
+ *     entry:	       List pointers
+ */
+struct pagg_hook {
+       struct module	*module;
+       char		*name;	/* Name Key - restricted to 32 characters */
+       int		(*attach)(struct task_struct *, struct pagg *, void*);
+       int		(*detach)(struct task_struct *, struct pagg *);
+       int		(*init)(struct task_struct *, struct pagg *);
+       void		*data;	/* Opaque module specific data */
+       struct list_head	entry;	/* List pointers */
+};
+
+
+/* Kernel service functions for providing PAGG support */
+extern struct pagg *get_pagg(struct task_struct *task, char *key);
+extern struct pagg *alloc_pagg(struct task_struct *task, 
+				      struct pagg_hook *pt);
+extern void free_pagg(struct pagg *pagg);
+extern int register_pagg_hook(struct pagg_hook *pt_new);
+extern int unregister_pagg_hook(struct pagg_hook *pt_old);
+extern int attach_pagg_list(struct task_struct *to_task, 
+					struct task_struct *from_task);
+extern int detach_pagg_list(struct task_struct *task);
+
+/* 
+ *  Macro used when a child process must inherit attachment to pagg 
+ *  containers from the parent.
+ *
+ *  Arguments:
+ *	ct:	child (struct task_struct *)
+ *	pt:	parent (struct task_struct *)
+ *	cf:	clone_flags
+ */
+#define attach_pagg_list_chk(ct, pt)					\
+do {									\
+	INIT_PAGG_LIST(&ct->pagg_list);					\
+	if (!list_empty(&pt->pagg_list.head)) {				\
+		if (attach_pagg_list(ct, pt) != 0)			\
+			goto bad_fork_cleanup;				\
+	}								\
+} while(0)
+
+/* 
+ * Macro used when a process must detach from pagg containers to which it
+ * is currenlty a member.
+ *
+ * Aguments:
+ * 	t:	task (struct task_struct *)
+ */
+#define detach_pagg_list_chk(t)					\
+do {									\
+	if (!list_empty(&t->pagg_list.head)) {				\
+		detach_pagg_list(t);					\
+	}								\
+} while(0)
+
+
+/*
+ * Utility macros for pagg handling and locking pagg lists.
+ *
+ * Arguments:
+ * 	t:	task  (struct task_list *)
+ * 	p:	pagg  (struct pagg *)
+ * 	d:	data  (ptr to data maintained by the 
+ * 			pagg module - converts to void ptr)
+ */
+	/* Invoke module detach callback for the pagg & task */
+#define detach_pagg(t, p)		p->hook->detach(t, p)
+	/* Invoke module attach callback for the pagg & task */
+#define attach_pagg(t, p, d)  		p->hook->attach(t, p, (void *)d)
+	/* Allows module to set data item for pagg */
+#define set_pagg(p, d)   		p->data = (void *)d
+	/* Down the read semaphore for the task's pagg_list */
+#define read_lock_pagg_list(t)		down_read(&t->pagg_list.sem)
+	/* Up the read semaphore for the task's pagg_list */
+#define read_unlock_pagg_list(t) 	up_read(&t->pagg_list.sem)
+	/* Down the write semaphore for the task's pagg_list */
+#define write_lock_pagg_list(t)		down_write(&t->pagg_list.sem)
+	/* Up the write semaphore for the task's pagg_list */
+#define write_unlock_pagg_list(t) 	up_write(&t->pagg_list.sem)
+
+#else  /* CONFIG_PAGG */
+
+/* 
+ * Replacement macros used when PAGG (Process Aggregates) support is not
+ * compiled into the kernel.
+ */
+#define INIT_PAGG_LIST(l) do { } while(0)
+#define attach_pagg_list_chk(ct, pt)  do { } while(0)
+#define detach_pagg_list_chk(t)  do {  } while(0)     
+
+#endif /* CONFIG_PAGG */
+
+#endif /* _PAGG_H */
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/include/linux/paggctl.h 150-pagg-job.patch/include/linux/paggctl.h
--- 090-fakeprom-update.patch/include/linux/paggctl.h	Wed Dec 31 16:00:00 1969
+++ 150-pagg-job.patch/include/linux/paggctl.h	Wed Jan 21 09:21:48 2004
@@ -0,0 +1,184 @@
+/* 
+ * 
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of version 2 of the GNU General Public License 
+ * as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ * 
+ * Further, this software is distributed without any warranty that it is 
+ * free of the rightful claim of any third person regarding infringement 
+ * or the like.  Any license provided herein, whether implied or 
+ * otherwise, applies only to this software file.  Patent licenses, if 
+ * any, provided herein do not apply to combinations of this program with 
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program; if not, write the Free Software 
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
+ * 
+ * 
+ * Description:   This file, include/linux/paggctl.h, contains the data
+ *       structure definitions used by user-mode programs to
+ *       communicate with the PAGG modules via the paggctl
+ *       function.
+ * 
+ */
+
+#ifndef _LINUX_PAGGCTL_H
+#define _LINUX_PAGGCTL_H
+#ifndef __KERNEL__
+#include <stdint.h>
+#include <sys/types.h>
+#include <asm/unistd.h>
+#endif
+
+#define PAGG_NAMELN  32    /* Max chars in PAGG module name */
+#define PAGG_NAMESTR PAGG_NAMELN+1  /* PAGG mod name string including
+												 * room for end-of-string = '\0' */
+
+/*
+ * ====================
+ * JOB PAGG definitions
+ * ====================
+ */
+#define PAGG_JOB 	"job"	/* PAGG module identifier string */
+
+
+
+/* 
+ * ================
+ * KERNEL INTERFACE
+ * ================
+ */
+#define JOB_PROC_ENTRY	"job"	/* /proc entry name */
+#define JOB_IOCTL_NUM	'A'
+
+
+/*
+ * 
+ * Define ioctl options available in the job module 
+ *
+ */
+
+#define JOB_NOOP	_IOWR(JOB_IOCTL_NUM, 0, void *)	/* No-op options */
+
+#define JOB_CREATE	_IOWR(JOB_IOCTL_NUM, 1, void *)	/* Create a job - uid = 0 only */
+#define JOB_ATTACH	_IOWR(JOB_IOCTL_NUM, 2, void *)	/* RESERVED */
+#define JOB_DETACH	_IOWR(JOB_IOCTL_NUM, 3, void *)	/* RESERVED */
+#define JOB_GETJID	_IOWR(JOB_IOCTL_NUM, 4, void *)	/* Get Job ID for specificed pid */
+#define JOB_WAITJID	_IOWR(JOB_IOCTL_NUM, 5, void *)	/* Wait for job to complete */	
+#define JOB_KILLJID	_IOWR(JOB_IOCTL_NUM, 6, void *)	/* Send signal to job */
+#define JOB_GETJIDCNT	_IOWR(JOB_IOCTL_NUM, 9, void *)	/* Get number of JIDs on system */
+#define JOB_GETJIDLST	_IOWR(JOB_IOCTL_NUM, 10, void *)	/* Get list of JIDs on system */
+#define JOB_GETPIDCNT	_IOWR(JOB_IOCTL_NUM, 11, void *)	/* Get number of PIDs in JID */
+#define JOB_GETPIDLST	_IOWR(JOB_IOCTL_NUM, 12, void *)	/* Get list of PIDs in JID */
+#define JOB_SETJLIMIT	_IOWR(JOB_IOCTL_NUM, 13, void *)	/* Future: set job limits info */
+#define JOB_GETJLIMIT	_IOWR(JOB_IOCTL_NUM, 14, void *)	/* Future: get job limits info */
+#define JOB_GETJUSAGE	_IOWR(JOB_IOCTL_NUM, 15, void *)	/* Future: get job res. usage */
+#define JOB_FREE	_IOWR(JOB_IOCTL_NUM, 16, void *)	/* Future: Free job entry */
+#define JOB_GETUSER	_IOWR(JOB_IOCTL_NUM, 17, void *)	/* Get owner for job */
+#define JOB_GETPRIMEPID	_IOWR(JOB_IOCTL_NUM, 18, void *)	/* Get prime pid for job */
+#define JOB_SETHID	_IOWR(JOB_IOCTL_NUM, 19, void *)	/* Set HID for jid values */
+#define JOB_DETACHJID	_IOWR(JOB_IOCTL_NUM, 20, void *)	/* Detach all tasks from job */
+#define JOB_DETACHPID	_IOWR(JOB_IOCTL_NUM, 21, void *)	/* Detach a task from job */
+#define JOB_OPT_MAX	_IOWR(JOB_IOCTL_NUM, 22 , void *)	/* Should always be highest number */	
+
+
+/*
+ * Define ioctl request structures for job module 
+ */
+
+struct job_create {
+	u64 	r_jid;	/* Return value of JID */
+	u64 	jid;	/* Jid value requested */
+	int 	user;	/* UID of user associated with job */
+	int 	options;/* creation options - unused */
+};
+
+
+struct job_getjid {
+	u64 	r_jid;	/* Returned value of JID */
+	pid_t 	pid;	/* Info requested for PID */
+};
+
+
+struct job_waitjid {
+	u64 	r_jid;	/* Returned value of JID */
+	u64 	jid;	/* Waiting on specified JID */
+	int 	stat;	/* Status information on JID */
+	int 	options;/* Waiting options */ 
+};
+
+
+struct job_killjid {
+	int	r_val;	/* Return value of kill request */
+	u64	jid;	/* Sending signal to all PIDs in JID */
+	int	sig;	/* Signal to send */
+};
+
+
+struct job_jidcnt {
+	int	r_val;	/* Number of JIDs on system */
+};
+
+
+struct job_jidlst {
+	int	r_val;	/* Number of JIDs in list */
+	u64	*jid;	/* List of JIDs */
+};
+
+
+struct job_pidcnt {
+	int	r_val;	/* Number of PIDs in JID */
+	u64	jid;	/* Getting count of JID */
+};
+
+
+struct job_pidlst {
+	int	r_val;	/* Number of PIDs in list */
+	pid_t	*pid;	/* List of PIDs */
+	u64	jid;
+};
+
+
+struct job_user {
+	int	r_user; /* The UID of the owning user */
+	u64	jid;    /* Get the UID for this job */
+};
+
+struct job_primepid {
+	pid_t	r_pid; /* The prime pid */
+	u64	jid;   /* Get the prime pid for this job */
+};
+
+struct job_sethid {
+	unsigned long	r_hid; /* Value that was set */
+	unsigned long	hid;   /* Value to set to */
+};
+
+
+struct job_detachjid {
+	int	r_val; /* Number of tasks detached from job */
+	u64	jid;   /* Job to detach processes from */
+};
+
+struct job_detachpid {
+	u64	r_jid; /* Jod ID task was attached to */
+	pid_t	pid;   /* Task to detach from job */
+};
+
+#endif /* _LINUX_PAGGCTL_H */
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/include/linux/sched.h 150-pagg-job.patch/include/linux/sched.h
--- 090-fakeprom-update.patch/include/linux/sched.h	Tue Jan 20 19:49:22 2004
+++ 150-pagg-job.patch/include/linux/sched.h	Wed Jan 21 09:21:48 2004
@@ -29,6 +29,7 @@
 #include <linux/completion.h>
 #include <linux/pid.h>
 #include <linux/percpu.h>
+#include <linux/pagg.h>
 
 struct exec_domain;
 
@@ -458,11 +459,13 @@ struct task_struct {
 
 	struct dentry *proc_dentry;
 	struct backing_dev_info *backing_dev_info;
-
 	struct io_context *io_context;
 
 	unsigned long ptrace_message;
 	siginfo_t *last_siginfo; /* For ptrace use.  */
+
+/* List of pagg (process aggregate) attachments */
+	struct pagg_list pagg_list;
 };
 
 static inline pid_t process_group(struct task_struct *tsk)
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/init/Kconfig 150-pagg-job.patch/init/Kconfig
--- 090-fakeprom-update.patch/init/Kconfig	Tue Jan 20 19:50:21 2004
+++ 150-pagg-job.patch/init/Kconfig	Wed Jan 21 09:21:48 2004
@@ -104,6 +104,40 @@ config BSD_PROCESS_ACCT
 	  up to the user level program to do useful things with this
 	  information.  This is generally a good idea, so say Y.
 
+config PAGG
+	bool "Support for process aggregates (PAGGs)"
+	help
+     Say Y here if you will be loading modules which provide support
+     for process aggregate containers.  Currently, this option is only
+     applicable to Intel architectures. Examples of such modules include the
+     Linux Jobs module and the Linux Array Sessions module.  If you will not 
+     be using such modules, say N.
+
+config PAGG_JOB
+	tristate "  Process aggregate based jobs"
+	depends on PAGG
+	help
+	  The Job feature implements a type of process aggregate,
+	  or grouping.  A job is the collection of all processes that
+	  are descended from a point-of-entry process.  Examples of such
+	  points-of-entry include telnet, rlogin, and console logins.
+	  A job differs from a session and process group since the job
+	  container (or group) is inescapable.  Only root level processes,
+	  or those with the CAP_SYS_RESOURCE capability, can create new jobs
+	  or escape from a job.
+	
+	  A job is identified by a unique job identifier (jid).  Currently,
+	  that jid can be used to obtain status information about the job
+	  and the processes it contians.  The jid can also be used to send
+	  signals to all processes contained in the job.  In addition,
+	  other processes can wait for the completion of a job - the event
+	  where the last process contained in the job has exited.
+	
+	  If you want to compile support for jobs into the kernel, select
+	  this entry using Y.  If you want the support for jobs provided as
+	  a module, select this entry using M.  If you do not want support
+	  for jobs, select N.
+
 config SYSCTL
 	bool "Sysctl support"
 	---help---
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/kernel/Makefile 150-pagg-job.patch/kernel/Makefile
--- 090-fakeprom-update.patch/kernel/Makefile	Tue Jan 20 19:49:29 2004
+++ 150-pagg-job.patch/kernel/Makefile	Wed Jan 21 09:22:47 2004
@@ -6,7 +6,8 @@ obj-y     = sched.o fork.o exec_domain.o
 	    exit.o itimer.o time.o softirq.o resource.o \
 	    sysctl.o capability.o ptrace.o timer.o user.o \
 	    signal.o sys.o kmod.o workqueue.o pid.o \
-	    rcupdate.o intermodule.o extable.o params.o posix-timers.o
+	    rcupdate.o intermodule.o extable.o params.o posix-timers.o \
+	    pagg.o job.o
 
 obj-$(CONFIG_FUTEX) += futex.o
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
@@ -17,6 +18,8 @@ obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_PM) += power/
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_COMPAT) += compat.o
+obj-$(CONFIG_PAGG) += pagg.o
+obj-$(CONFIG_PAGG_JOB) += job.o
 obj-$(CONFIG_IKCONFIG) += configs.o
 obj-$(CONFIG_IKCONFIG_PROC) += configs.o
 
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/kernel/exit.c 150-pagg-job.patch/kernel/exit.c
--- 090-fakeprom-update.patch/kernel/exit.c	Tue Jan 20 19:50:31 2004
+++ 150-pagg-job.patch/kernel/exit.c	Wed Jan 21 09:21:48 2004
@@ -22,7 +22,7 @@
 #include <linux/profile.h>
 #include <linux/mount.h>
 #include <linux/proc_fs.h>
-
+#include <linux/pagg.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/mmu_context.h>
@@ -786,6 +786,9 @@ NORET_TYPE void do_exit(long code)
 		module_put(tsk->binfmt->module);
 
 	tsk->exit_code = code;
+
+	detach_pagg_list_chk(tsk);
+
 	exit_notify(tsk);
 	schedule();
 	BUG();
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/kernel/fork.c 150-pagg-job.patch/kernel/fork.c
--- 090-fakeprom-update.patch/kernel/fork.c	Tue Jan 20 19:49:22 2004
+++ 150-pagg-job.patch/kernel/fork.c	Wed Jan 21 09:21:48 2004
@@ -30,7 +30,7 @@
 #include <linux/futex.h>
 #include <linux/ptrace.h>
 #include <linux/mount.h>
-
+#include <linux/pagg.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
@@ -230,6 +230,9 @@ void __init fork_init(unsigned long memp
 
 	init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
 	init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
+
+	/* Initialize the pagg list in pid 0 before it can clone itself. */
+	INIT_PAGG_LIST(&current->pagg_list);
 }
 
 static struct task_struct *dup_task_struct(struct task_struct *orig)
@@ -973,6 +976,12 @@ struct task_struct *copy_process(unsigne
 	   
 	p->parent_exec_id = p->self_exec_id;
 
+	/*
+	 * call pagg modules to properly attach new process to the same
+	 * process aggregate containers as the parent process.
+	 */
+	attach_pagg_list_chk(p, current);
+
 	/* ok, now we should be set up.. */
 	p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
 	p->pdeath_signal = 0;
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/kernel/job.c 150-pagg-job.patch/kernel/job.c
--- 090-fakeprom-update.patch/kernel/job.c	Wed Dec 31 16:00:00 1969
+++ 150-pagg-job.patch/kernel/job.c	Wed Jan 21 09:21:48 2004
@@ -0,0 +1,2060 @@
+/*
+ * Linux Job kernel module
+ *
+ *
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of version 2 of the GNU General Public License 
+ * as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ * 
+ * Further, this software is distributed without any warranty that it is 
+ * free of the rightful claim of any third person regarding infringement 
+ * or the like.  Any license provided herein, whether implied or 
+ * otherwise, applies only to this software file.  Patent licenses, if 
+ * any, provided herein do not apply to combinations of this program with 
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program; if not, write the Free Software 
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
+ */
+
+/*
+ * Description:	This file implements a type of process grouping called jobs.
+ * 		For further information about jobs, consult the file
+ * 		Documentation/job.txt. Jobs are implemented as a type of PAGG
+ * 		(process aggregate).  For further information about PAGGs,
+ * 		consult the file Documentation/pagg.txt.
+ */
+
+/*
+ * LOCKING INFO
+ *
+ * There are currently two levels of locking in this module.  So, we
+ * have two classes of locks: 
+ *
+ *	(1) job table lock (always, job_table_sem)
+ *	(2) job entry lock (usually, job->sem)
+ *
+ * Most of the locking used is read/write sempahores.  In  rare cases, a
+ * spinlock is also used.  Those cases requiring a spinlock concern when the
+ * tasklist_lock must be locked (such as when looping over all tasks on the
+ * system).
+ *
+ * There is only one job_table_sem.  There is a job->sem for each job
+ * entry in the job_table.  This job module is a PAGG module (Process
+ * Aggregation).  Each task has a special lock that protects its PAGG
+ * information - this is called the pagg list lock. There are special macros
+ * used to lock/unlock a task's pagg list lock.  The pagg list lock is really
+ * a semaphore.
+ *
+ * Purpose:
+ *
+ *	(1) The job_table_sem protects all entries in the table.
+ *	(2) The job->sem protects all data and task attachments for the job.
+ *
+ * Truths we hold to be self-evident:
+ *
+ * Only the holder of a write lock for the job_table_lock may add or
+ * delete a job entry from the job_table. The job_table includes all job
+ * entries in the hash table and chains off the hash table locations.
+ *
+ * Only the holder of a write lock for a job->lock may attach or detach
+ * processes/tasks from the attached list for the job.
+ *
+ * If you hold a read lock of job_table_lock, you can assume that the
+ * job entries in the table will not change.  The link pointers for
+ * the chains of job entries will not change, the job ID (jid) value
+ * will not change, and data changes will be (mostly) atomic.
+ *
+ * If you hold a read lock of a job->lock, you can assume that the
+ * attachments to the job will not change.  The link pointers for the
+ * attachment list will not change and the attachments will not change.
+ *
+ * If you are going to grab nested locks, the nesting order is:
+ *
+ * 	*_lock_pagg_list(task)		* = read or write
+ *	job_table_sem
+ *	job->sem
+ *
+ * However, it is not strictly necessary to down the job_table_sem
+ * before downing job->sem. 
+ *
+ * Also, the nesting order allows you to lock in this order:
+ *
+ *	*_lock_pagg_list(task)
+ *	job->sem
+ *
+ * without locking job_table_sem between the two.
+ *
+ */
+
+/* standard for kernel modules */
+#include <linux/config.h>
+
+#ifdef CONFIG_PAGG_JOB
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/list.h>
+
+#include <asm/uaccess.h>	/* for get_user & put_user */
+
+#include <linux/sched.h>	/* for current */
+#include <linux/tty.h>		/* for the tty declarations */
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <linux/proc_fs.h>
+
+#include <linux/string.h>
+#include <asm/semaphore.h>
+
+#include <linux/pagg.h>		/* to use pagg hooks */
+#include <linux/job.h>
+#include <linux/paggctl.h>
+
+#define HASH_SIZE	1024
+
+/* The states for a job */ 
+#define FETAL	1	/* being born, not ready for attachments yet */
+#define RUNNING 2	/* Running job */
+#define STOPPED 3  	/* Stopped job */
+#define ZOMBIE  4	/* Dead job */
+
+/* Job creation tags for the job HID (host ID) */ 
+#define DISABLED	0xffffffff	/* New job creation disabled */
+#define LOCAL		0x0		/* Only creating local sys jobs */
+
+
+#ifdef 	__BIG_ENDIAN
+#define		iptr_hid(ll) 	((u32 *)&(ll))
+#define		iptr_sid(ll) 	(((u32 *)(&(ll) + 1)) - 1)
+#else	/* __LITTLE_ENDIAN */
+#define		iptr_hid(ll) 	(((u32 *)(&(ll) + 1)) - 1)
+#define		iptr_sid(ll) 	((u32 *)&(ll))
+#endif	/* __BIG_ENDIAN */
+
+#define		jid_hash(ll) 	(*(iptr_sid(ll)) % HASH_SIZE)
+
+
+/* Job info entry for member tasks */
+struct job_attach {
+	struct task_struct	*task;	/* task we are attaching to job */
+	struct pagg		*pagg;	/* our pagg entry in the task */
+	struct job_entry	*job;	/* the job we are attaching task to */
+	struct list_head	entry; 	/* list stuff */
+};
+
+struct job_waitinfo {
+	int		status;		/* For tasks waiting on job exit */
+};
+
+struct job_csainfo {
+	u64		corehimem;	/* Accounting - highpoint, phys mem */
+	u64		virthimem;	/* Accounting - highpoint, virt mem */
+	struct file	*acctfile;	/* The accounting file for job */
+}; 
+
+/* Job table entry type */
+struct job_entry {
+	u64		    jid;	/* Our job ID */
+	int	    	    refcnt;	/* Number of tasks attached to job */
+	int		    state;	/* State of job - RUNNING,... */
+	struct rw_semaphore sem;	/* lock for the job */
+	uid_t		    user;	/* user that owns the job */
+	time_t		    start;	/* When the job began */
+	struct job_csainfo  csa;	/* CSA accounting info */
+	wait_queue_head_t   zombie;	/* queue last task - during wait */
+	wait_queue_head_t   wait;	/* queue of tasks waiting on job */
+	int		    waitcnt;	/* Number of tasks waiting on job */
+	struct job_waitinfo waitinfo;	/* Status info for waiting tasks */ 
+	struct list_head    attached;	/* List of attached tasks */
+	struct list_head    entry;	/* List of other jobs - same hash */
+};
+
+
+/* Job container tables */
+static struct list_head  job_table[HASH_SIZE];
+static int	    	 job_table_refcnt = 0;
+static 			 DECLARE_RWSEM(job_table_sem);
+
+
+/* Accounting subscriber list */
+static struct job_acctmod 	*acct_list[JOB_ACCT_COUNT];
+static 				DECLARE_RWSEM(acct_list_sem);
+
+
+/* Host ID for the localhost */
+static u32   jid_hid = DISABLED;
+
+static char 	   *hid = NULL;	    
+MODULE_PARM(hid, "s");
+
+/* Function prototypes */
+static int job_sys_create(struct job_create *);
+static int job_sys_getjid(struct job_getjid *);
+static int job_sys_waitjid(struct job_waitjid *);
+static int job_sys_killjid(struct job_killjid *);
+static int job_sys_getjidcnt(struct job_jidcnt *);
+static int job_sys_getjidlst(struct job_jidlst *);
+static int job_sys_getpidcnt(struct job_pidcnt *);
+static int job_sys_getpidlst(struct job_pidlst *);
+static int job_sys_getuser(struct job_user *);
+static int job_sys_getprimepid(struct job_primepid *);
+static int job_sys_sethid(struct job_sethid *);
+static int job_sys_detachjid(struct job_detachjid *);
+static int job_sys_detachpid(struct job_detachpid *);
+static int job_attach(struct task_struct *, struct pagg *, void *);
+static int job_detach(struct task_struct *, struct pagg *);
+static struct job_entry *job_getjob(u64 jid);
+static int job_syscall(unsigned int, unsigned long);
+
+u64 job_getjid(struct task_struct *);
+
+int job_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+/* Job container kernel pagg entry */
+static struct pagg_hook pagg_hook = {
+	.module	= THIS_MODULE,
+	.name	= PAGG_JOB,
+	.attach	= job_attach,
+	.detach	= job_detach,
+	.init	= NULL,
+	.data	= &job_table,
+	.entry	= LIST_HEAD_INIT(pagg_hook.entry)
+};
+
+/* proc dir entry */
+struct proc_dir_entry *job_proc_entry;
+
+/* file operations for proc file */
+static struct file_operations job_file_ops = {
+	.owner	= THIS_MODULE,
+	.ioctl	= job_ioctl
+};
+
+#ifdef DEBUG
+
+#define DBG_PRINTINIT(s)	\
+	char *dbg_fname = s		
+
+#define DBG_PRINTENTRY()					\
+do {								\
+	printk(KERN_DEBUG "job: %s: entry\n", dbg_fname);	\
+} while(0)
+
+#define DBG_PRINTEXIT(c)				 		\
+do {							 		\
+	printk(KERN_DEBUG "job: %s: exit, code = %d\n", dbg_fname, c);	\
+} while(0)
+
+/* write lock semaphore */
+#define JOB_WLOCK(l)					\
+do {							\
+	printk(KERN_DEBUG "job: wlock = %p\n", l);	\
+	down_write(l);					\
+} while(0);
+
+/* write unlock semaphore */
+#define JOB_WUNLOCK(l)					\
+do {							\
+	printk(KERN_DEBUG "job: wunlock = %p\n", l);	\
+	up_write(l);					\
+} while(0);
+
+/* read lock semaphore */
+#define JOB_RLOCK(l)					\
+do {							\
+	printk(KERN_DEBUG "job: rlock = %p\n", l);	\
+	down_read(l);					\
+} while(0);
+
+/* read unlock semaphore */
+#define JOB_RUNLOCK(l)					\
+do {							\
+	printk(KERN_DEBUG "job: runlock = %p\n", l);	\
+	up_read(l);					\
+} while(0);
+
+
+#else /* #ifdef DEBUG */
+
+#define DBG_PRINTINIT(s)	
+
+#define DBG_PRINTENTRY() 	\
+do {				\
+} while(0)
+
+#define DBG_PRINTEXIT(c)	\
+do {				\
+} while(0)
+
+/* write lock semaphore */
+#define JOB_WLOCK(l)	\
+do {			\
+	down_write(l);	\
+} while(0);
+
+/* write unlock semaphore */
+#define JOB_WUNLOCK(l)	\
+do {			\
+	up_write(l);	\
+} while(0);
+
+/* read lock semaphore */
+#define JOB_RLOCK(l)	\
+do {			\
+	down_read(l);	\
+} while(0);
+
+/* read unlock semaphore */
+#define JOB_RUNLOCK(l)	\
+do {			\
+	up_read(l);	\
+} while(0);
+
+
+#endif /* #ifdef DEBUG */
+
+
+
+/* 
+ * job__getjob
+ *
+ * Given a jid value, find the entry in the job_table and return a pointer
+ * to the job entry or NULL if not found.
+ *
+ * You should normally JOB_RLOCK the job_table_sem before calling this 
+ * function. 
+ */
+struct job_entry *
+job_getjob(u64 jid)
+{
+	struct list_head *entry = NULL;
+	struct job_entry *tjob = NULL;
+	struct job_entry *job = NULL;
+
+	list_for_each(entry,  &job_table[ jid_hash(jid) ]) {
+		tjob = list_entry(entry, struct job_entry, entry);
+		if (tjob->jid == jid) {
+			job = tjob;
+			break;
+		}
+	}
+	return job;
+}
+
+	
+/*
+ * job_attach
+ *
+ * Attach the task to the job specified in the target data (old_data).
+ * This function will add the task to the list of attached tasks for the job.
+ * In addition, a link from the task to the job is created and added to the 
+ * task via the data pointer reference.  
+ *
+ * The process that owns the target data should be at least read locked (using
+ * read_lock_pagg_list(task)) during this call.  This help in ensuring
+ * that the job cannot be removed since at least one process will 
+ * still be referencing the job (the one owning the target_data).
+ *
+ * It is expected that this function will be called from within the
+ * attach_pagg_list() function in the kernel, when forking (do_fork) a child
+ * process represented by task.
+ *
+ * If this function is called form some other point, then it is possible that
+ * task and data could be altered while going through this function.  In such
+ * a case, the caller should also lock the pagg list for the task
+ * task_struct.
+ *
+ * the function returns 0 upon success, and -1 upon failure.
+ */
+static int
+job_attach(struct task_struct *task, struct pagg *new_pagg, 
+		void  *old_data)
+{
+	struct job_entry  *job        = ((struct job_attach *)old_data)->job;
+	struct job_attach *attached   = NULL;
+	int          errcode     = 0;
+	DBG_PRINTINIT("job_attach");
+
+	DBG_PRINTENTRY();
+
+	/* 
+	 * Lock the job for writing. The task owning target_data has its
+	 * pagg_list locked, so we know there is at least one active reference
+	 * to the job - therefore, it cannot have been removed before we
+	 * have gotten this write lock established.
+	 */
+	JOB_WLOCK(&job->sem);
+
+	if (job->state == ZOMBIE) {
+		/* If the job is a zombie (dying), bail out of the attach */
+		printk(KERN_WARNING "Attach task(pid=%d) to job"
+				" failed - job is ZOMBIE\n", 
+				task->pid);
+		errcode = -EINPROGRESS;
+		JOB_WUNLOCK(&job->sem);
+		goto error_return;
+	}
+
+
+	/* Allocate memory that we will need */
+
+	attached = (struct job_attach *)kmalloc(sizeof(struct job_attach *), 
+			GFP_KERNEL);
+	if (!attached) {
+		/* error */
+		printk(KERN_ERR "Attach task(pid=%d) to job"
+				" failed on memory error in kernel\n", 
+				task->pid);
+		errcode = -ENOMEM;
+		goto error_return;
+	}
+
+
+	attached->task  = task;
+	attached->pagg  = new_pagg;
+	attached->job   = job;
+	new_pagg->data  = (void *)attached;
+	list_add_tail(&attached->entry, &job->attached);
+	++job->refcnt;  
+
+	JOB_WUNLOCK(&job->sem);  
+
+	DBG_PRINTEXIT(0);
+	return 0;
+
+error_return:
+	DBG_PRINTEXIT(errcode);
+	if (attached) kfree(attached);
+	return errcode;
+}
+
+
+/*
+ * job_detach 
+ *
+ * Detach the task from the job attached to via the pagg reference.
+ * This function will remove the task from the list of attached tasks for the
+ * job specified via the pagg pointer.  In addition, the link to the job
+ * provided via the data pointer will also be removed.
+ *
+ * The pagg_list should be write locked for task before entering
+ * this function (using write_lock_pagg_list(task)).
+ *
+ * the function returns 0 uopn success, and -1 uopn failure.
+ */
+static int
+job_detach(struct task_struct *task, struct pagg *pagg)
+{
+	struct job_attach *attached   = ((struct job_attach *)(pagg->data));
+	struct job_entry  *job        = attached->job;
+	DBG_PRINTINIT("job_detach");
+
+	DBG_PRINTENTRY();
+
+	/*
+	 * Obtain the lock on the the job_table_sem and the job->sem for 
+	 * this job.
+	 */
+	JOB_WLOCK(&job_table_sem);
+	JOB_WLOCK(&job->sem);  
+
+	job->refcnt--;
+	list_del(&attached->entry);
+	pagg->data = NULL;
+	kfree(attached);
+
+	if (job->refcnt == 0) {
+		int waitcnt;
+
+		list_del(&job->entry);
+		--job_table_refcnt;
+
+		/* 
+		 * The job is removed from the job_table.
+		 * We can remove the job_table_sem now since
+		 * nobody can access the job via the table.
+		 */
+		JOB_WUNLOCK(&job_table_sem);
+
+		job->state = ZOMBIE;
+		job->waitinfo.status = task->exit_code;
+
+		waitcnt = job->waitcnt;
+
+		/* 
+		 * Release the job semaphore.  You cannot hold
+		 * this lock if you want the wakeup to work
+		 * properly.
+		 */
+		JOB_WUNLOCK(&job->sem);
+
+		if (waitcnt > 0) {
+			wake_up_interruptible(&job->wait);
+			wait_event(job->zombie, job->waitcnt == 0);
+		} 
+
+		/* 
+		 * Job is exiting, all processes waiting for job to exit
+		 * have been notified.  Now we call the accounting
+		 * subscribers.
+		 */
+
+#if defined(CONFIG_CSA_JOB_ACCT) || defined(CONFIG_CSA_JOB_ACCT_MODULE)
+		/* - CSA accounting */
+		if (acct_list[JOB_ACCT_CSA]) {
+			struct job_acctmod *acct = acct_list[JOB_ACCT_CSA];
+			if (acct->module) {
+				if (try_module_get(acct->module) == 0) {
+					printk(KERN_WARNING
+						"job_detach: Tried to get non-living acct module\n");
+				}
+			}
+			if (acct->jobend) {
+				int res = 0;
+				struct job_csa csa;
+
+				csa.job_id = job->jid;
+				csa.job_uid = job->user;
+				csa.job_start = job->start;
+				csa.job_corehimem = job->csa.corehimem;
+				csa.job_virthimem = job->csa.virthimem;
+				csa.job_acctfile = job->csa.acctfile;
+
+				res = acct->jobend(JOB_EVENT_END,
+						&csa);
+				if (res) {
+					printk(KERN_WARNING
+						"job_detach: CSA -"
+						" jobend failed.\n");
+				}
+			}
+			if (acct->module) 
+				module_put(acct->module);
+		} else {
+			printk(KERN_WARNING "job_detach: CSA - attempt"
+					"to lock CSA modulefailed.\n");
+		}
+#endif /* CONFIG_CSA_JOB_ACCT || defined(CONFIG_CSA_JOB_ACCT_MODULE) */
+
+
+		/* 
+		 * Every process attached or waiting on this job should be
+	         * detached and finished waiting, so now we can free the
+		 * memory for the job.
+		 */
+		kfree(job);
+
+	} else {
+		/* This is case where job->refcnt was greater than 1, so
+		 * we were not going to delete the job after the detach.
+		 * Therefore, only the job->sem is being held - the 
+		 * job_table_sem was released earlier.
+		 */
+		JOB_WUNLOCK(&job->sem);
+		JOB_WUNLOCK(&job_table_sem);
+	}
+
+	DBG_PRINTEXIT(0);
+
+	return 0;
+}
+
+/* 
+ * job_sys_create
+ *
+ * This function is used to create a new job and attache the calling process
+ * to that new job.
+ *
+ * Returns 0 on success, and negative on failure (negative errno value).
+ */
+static int
+job_sys_create(struct job_create *create_args)
+{
+	struct job_create		create;
+	struct job_entry		*job 	      = NULL;
+	struct job_attach		*attached     = NULL;
+	struct pagg		*pagg	      = NULL;
+	struct pagg		*old_pagg	= NULL;
+	int			errcode       = 0;
+	DBG_PRINTINIT("job_sys_create");
+
+	DBG_PRINTENTRY();
+
+	/* 
+	 * if the job ID - host ID segment is set to DISABLED, we will
+	 * not be creating new jobs.  We don't mark it as an error, but
+	 * the jid value returned will be 0.
+	 */
+	if (jid_hid == DISABLED) {
+		errcode = 0;
+		goto error_return;
+	}
+
+
+#if 0	/* XXX - Use if capable is not present */
+	if (current->euid != 0)
+		return -EPERM;
+#else	
+	if (!capable(CAP_SYS_RESOURCE)) {
+		errcode = -EPERM;
+		goto error_return;
+	}
+#endif
+	if (!create_args) {
+		errcode = -EINVAL;
+		goto error_return;
+	}
+
+	if (copy_from_user(&create, create_args, sizeof(create)))  {
+		errcode = -EFAULT;
+		goto error_return;
+	}
+		
+	/* 
+	 * Allocate some of the memory we might need, before we start
+	 * locking
+	 */
+
+	attached = (struct job_attach *)kmalloc(sizeof(struct job_attach), GFP_KERNEL);
+	if (!attached) {
+		/* error */
+		errcode = -ENOMEM;
+		goto error_return;
+	}
+
+	job = (struct job_entry *)kmalloc(sizeof(struct job_entry), GFP_KERNEL);
+	if (!job) {
+		/* error */
+		errcode = -ENOMEM;
+		goto error_return;
+	}
+
+	/* We keep the old pagg around in case we need it in an error condition.
+	 * If, for example, a job_getjob call fails because the requested JID is
+	 * already in use, we don't want to detach that job.  Having this ability 
+	 * is complicated by the locking.
+	 */
+	write_lock_pagg_list(current);
+	old_pagg = get_pagg(current, pagg_hook.name);
+
+	/* 
+	 * Lock the job_table and add the pointers for the new job.
+	 * Since the job is new, we won't need to lock the job.
+	 */
+	JOB_WLOCK(&job_table_sem);  
+
+	/*
+	 * Determine if create should use specified JID or one that is
+	 * generated.
+	 */
+	if (create.jid != 0) {
+		/* We use the specified JID value */
+
+		if (job_getjob(create.jid)) { 
+			/* JID already in use, bail */
+			/* error_return doesn't do JOB_WUNLOCK */
+			JOB_WUNLOCK(&job_table_sem);
+			/* we haven't allocated a new pagg yet so error_return won't unlock 
+			 * this.  We'll unlock here */
+			write_unlock_pagg_list(current);
+			errcode = -EBUSY;
+			/* error_return doesn't touch old_pagg so we don't detach */
+			goto error_return;
+		} else {
+			/* Using specifiec JID */
+			job->jid = create.jid;
+		}
+
+	} else {	
+
+		/* We generate a new JID value */
+		*(iptr_hid(job->jid)) = jid_hid;
+		*(iptr_sid(job->jid)) = current->pid;
+	}
+
+	pagg = alloc_pagg(current, &pagg_hook);
+	if (!pagg) {
+		/* error */
+		write_unlock_pagg_list(current);
+		errcode = -ENOMEM;
+		goto error_return;
+	}
+
+	/* Initialize job entry values & lists */
+	job->refcnt = 1;
+	job->user = create.user;
+	job->start = jiffies;
+	job->csa.corehimem = 0;
+	job->csa.virthimem = 0;
+	job->csa.acctfile  = NULL;
+	job->state = RUNNING;
+	init_rwsem(&job->sem);
+	INIT_LIST_HEAD(&job->attached);
+	list_add_tail(&attached->entry, &job->attached);
+	init_waitqueue_head(&job->wait);
+	init_waitqueue_head(&job->zombie);
+	job->waitcnt = 0;
+	job->waitinfo.status = 0;
+
+	/* set link from entry in attached list to task and job entry */
+	attached->task = current;
+	attached->job = job;
+	attached->pagg = pagg;
+	set_pagg(pagg, attached);
+
+	/* Insert new job into front of chain list */
+	list_add_tail(&job->entry, &job_table[ jid_hash(job->jid) ]);;
+	++job_table_refcnt;
+
+	JOB_WUNLOCK(&job_table_sem); 
+	/* At this point, the possible error conditions where we would need the
+	 * old pagg are gone.  So we can remove it.  We remove after we unlock
+	 * because detach_pagg does job table lock of its own.
+	 */
+	if (old_pagg) {
+		/* 
+		 * Detaching paggs for jobs never has a failure case,
+		 * so we don't need to worry about error codes.
+		 */
+		detach_pagg(current, old_pagg);
+		free_pagg(old_pagg);
+	} 
+	write_unlock_pagg_list(current);
+
+	/* Issue callbacks into accounting subscribers */
+
+#if defined(CONFIG_CSA_JOB_ACCT) || defined(CONFIG_CSA_JOB_ACCT_MODULE)
+	/* - CSA subscriber */
+	if (acct_list[JOB_ACCT_CSA]) {
+		struct job_acctmod *acct = acct_list[JOB_ACCT_CSA];
+		if (acct->module) {
+			if (try_module_get(acct->module) == 0) {
+				printk(KERN_WARNING
+					"job_sys_create: Tried to get non-living acct module\n");
+			}
+		}
+		if (acct->jobstart) {
+			int res;
+			struct job_csa csa;
+
+			csa.job_id = job->jid;
+			csa.job_uid = job->user;
+			csa.job_start = job->start;
+			csa.job_corehimem = job->csa.corehimem;
+			csa.job_virthimem = job->csa.virthimem;
+			csa.job_acctfile = job->csa.acctfile;
+
+			res = acct->jobstart(JOB_EVENT_START, &csa);
+			if (res < 0) {
+				printk(KERN_WARNING "job_sys_create: CSA -"
+						" jobstart failed.\n");
+			}
+		}
+		if (acct->module) 
+			module_put(acct->module);
+	}
+#endif /* CONFIG_CSA_JOB_ACCT || defined(CONFIG_CSA_JOB_ACCT_MODULE) */
+
+
+	create.r_jid = job->jid;
+	if (copy_to_user(create_args, &create, sizeof(create))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	DBG_PRINTEXIT(0);
+	return 0;
+
+error_return:
+	DBG_PRINTEXIT(errcode);
+	if (attached) kfree(attached);
+	if (job) kfree(job);
+	if (pagg) {
+		detach_pagg(current, pagg);
+		free_pagg(pagg);
+		/* This was locked at alloc_pagg call */
+		write_unlock_pagg_list(current);
+	}
+	create.r_jid = 0;
+	if (copy_to_user(create_args, &create, sizeof(create))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	return errcode;
+}
+
+
+/*
+ * job_sys_getjid
+ *
+ * Function retrieves the job ID (jid) for the specified process (pid).
+ *
+ * returns 0 on success, negative errno value on exit.
+ */
+static int
+job_sys_getjid(struct job_getjid *getjid_args) 
+{
+	struct job_getjid	   getjid;
+	int		   errcode = 0;
+	struct task_struct *task;
+	DBG_PRINTINIT("job_sys_getjid");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&getjid, getjid_args, sizeof(getjid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	/* lock the tasklist until we grab the specific task */
+	read_lock(&tasklist_lock);
+
+	if (getjid.pid == current->pid) {
+		task = current;
+	} else {
+		task = find_task_by_pid(getjid.pid);
+	}
+	if (task) {
+		get_task_struct(task); /* Ensure the task doesn't vanish on us */
+		read_unlock(&tasklist_lock); /* unlock the task list */
+		getjid.r_jid = job_getjid(task);
+		put_task_struct(task); /* We're done accessing the task */
+		if (getjid.r_jid == 0) {
+			errcode = -ENODATA;
+		}
+	} else {
+		read_unlock(&tasklist_lock);
+		getjid.r_jid = 0;
+		errcode = -ESRCH;
+	}
+
+
+	DBG_PRINTEXIT(errcode);
+	if (copy_to_user(getjid_args, &getjid, sizeof(getjid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return errcode;
+}
+
+
+/* 
+ * job_sys_waitjid
+ *
+ * This job allows a process to wait until a job exits & it returns the 
+ * status information for the last process to exit the job.
+ *
+ * On success returns 0, failure it returns the negative errno value.
+ */
+static int
+job_sys_waitjid(struct job_waitjid *waitjid_args)
+{
+	struct job_waitjid 	waitjid;
+	struct job_entry	*job;
+	int		retcode = 0;
+	DBG_PRINTINIT("job_sys_waitjid");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&waitjid, waitjid_args, sizeof(waitjid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+
+	waitjid.r_jid = waitjid.stat = 0;
+
+	if (waitjid.options != 0) {
+		retcode = -EINVAL;
+		goto general_return;
+	}
+
+	/* Lock the job table so that the current jobs don't change */
+	JOB_RLOCK(&job_table_sem);
+
+
+	if ((job = job_getjob(waitjid.jid)) == NULL ) {
+		JOB_RUNLOCK(&job_table_sem);
+		retcode = -ENODATA;
+		goto general_return;
+	} 
+
+	/* 
+	 * We got the job we need, we can release the job_table_sem
+	 */
+	JOB_WLOCK(&job->sem);
+	JOB_RUNLOCK(&job_table_sem);
+
+	++job->waitcnt; 
+
+	JOB_WUNLOCK(&job->sem);
+
+	/* We shouldn't hold any locks at this point! The increment of the
+	 * jobs waitcnt will ensure that the job is not removed without
+	 * first notifying this current task */
+	retcode = wait_event_interruptible(job->wait, 
+			job->refcnt == 0);
+
+	if (!retcode) {
+		/* 
+		 * This data is static at this point, we will 
+		 * not need a lock to read it.
+		 */
+		waitjid.stat = job->waitinfo.status;
+		waitjid.r_jid = job->jid;
+	}
+
+	JOB_WLOCK(&job->sem);
+	--job->waitcnt;
+	
+	if (job->waitcnt == 0)  {
+		JOB_WUNLOCK(&job->sem);
+
+		/* 
+		 * We shouldn't hold any locks at this point!  Else, the
+		 * last process in the job will not be able to remove the
+		 * job entry.
+		 *
+		 * That process is stuck waiting for this wake_up, so the
+		 * job shouldn't disappear until after this function call.
+		 * The job entry is not longer in the job table, so no
+		 * other process can get to the entry to foul things up.
+		 */
+		wake_up(&job->zombie);
+	} else {
+		JOB_WUNLOCK(&job->sem);
+	}
+
+general_return:
+
+	DBG_PRINTEXIT(retcode);
+	if (copy_to_user(waitjid_args, &waitjid, sizeof(waitjid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return retcode;
+}
+
+
+/*
+ * job_sys_killjid
+ *
+ * This functions allows a signal to be sent to all processes in a job.
+ *
+ * returns 0 on success, negative of errno on failure.
+ */
+static int
+job_sys_killjid(struct job_killjid *killjid_args)
+{
+	struct job_killjid	 killjid;
+	struct job_entry	 *job;
+	struct list_head *attached_entry;
+	struct siginfo   info;
+	int retcode = 0;
+	DBG_PRINTINIT("job_sys_killjid");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&killjid, killjid_args, sizeof(killjid))) {
+		retcode = -EFAULT;
+		goto cleanup_0locks_return;
+	}
+
+	killjid.r_val = -1;
+
+	/* A signal of zero is really a status check and is handled as such
+	 * by send_sig_info.  So we have < 0 instead of <= 0 here.
+	 */
+	if (killjid.sig < 0) {
+		retcode = -EINVAL;
+		goto cleanup_0locks_return;
+	} 
+
+	JOB_RLOCK(&job_table_sem);
+	job = job_getjob(killjid.jid);
+	if (!job) {
+		/* Job not found, copy back data & bail with error */
+		retcode = -ENODATA;
+		goto cleanup_1locks_return;
+	}
+
+	JOB_RLOCK(&job->sem);
+
+	/* 
+         * Check capability to signal job.  The signaling user must be
+	 * the owner of the job or have CAP_SYS_RESOURCE capability.
+	 */
+#if 0		/* Use this if not capability is available */
+	if (current->uid != 0) { 
+#else
+	if (!capable(CAP_SYS_RESOURCE)) {
+#endif
+		if (current->uid != job->user) {
+			retcode = -EPERM;
+			goto cleanup_2locks_return;
+		}
+	}
+
+	info.si_signo = killjid.sig;
+	info.si_errno = 0;
+	info.si_code = SI_USER;
+	info.si_pid = current->pid;
+	info.si_uid = current->uid;
+
+	list_for_each(attached_entry, &job->attached) {
+		int err;
+		struct job_attach *attached;
+
+		attached = list_entry(attached_entry, struct job_attach, entry);
+		err = send_sig_info(killjid.sig, &info, 
+				attached->task);
+		if (err != 0) {
+			/* 
+			 * XXX - the "prime" process, or initiating process
+			 * for the job may not be owned by the user.  So,
+			 * we would get an error in this case.  However, we
+			 * ignore the error for that specific process - it
+			 * should exit when all the child processes exit. It 
+			 * should ignore all signals from the user.
+			 *
+			 */
+			if (attached->entry.prev != &job->attached) {
+				retcode = err;
+			}
+		}
+
+	}
+
+cleanup_2locks_return:
+	JOB_RUNLOCK(&job->sem);
+cleanup_1locks_return:
+	JOB_RUNLOCK(&job_table_sem);
+cleanup_0locks_return:
+	killjid.r_val = retcode;
+	
+	DBG_PRINTEXIT(retcode);
+	if (copy_to_user(killjid_args, &killjid, sizeof(killjid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return retcode;
+}
+
+
+/*
+ * job_sys_getjidcnt
+ *
+ * Retun the number of jobs currently on the system.
+ *
+ * returns 0 on success & it always succeeds.
+ */ 
+static int
+job_sys_getjidcnt(struct job_jidcnt *jidcnt_args)
+{
+	struct job_jidcnt 	jidcnt;
+	DBG_PRINTINIT("job_sys_getjidcnt");
+
+	DBG_PRINTENTRY();
+
+	/* read lock might be overdoing it in this case */
+	JOB_RLOCK(&job_table_sem);
+	jidcnt.r_val = job_table_refcnt;
+	JOB_RUNLOCK(&job_table_sem);
+
+	DBG_PRINTEXIT(0);
+
+	if (copy_to_user(jidcnt_args, &jidcnt, sizeof(jidcnt))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+		
+
+/*
+ * job_sys_getjidlst
+ *
+ * Get the list of all jids currently on the system (limited by the number of 
+ * jobs there are and the number you say you can accept.
+ */
+static int
+job_sys_getjidlst(struct job_jidlst *jidlst_args)
+{
+	struct job_jidlst	 jidlst;
+	u64	 *jid;
+	struct job_entry	 *job;
+	struct list_head *job_entry;
+	int		 i;
+	int 		 count;
+	DBG_PRINTINIT("job_sys_getjidlst");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&jidlst, jidlst_args, sizeof(jidlst))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+
+	if (jidlst.r_val == 0)  {
+		DBG_PRINTEXIT(0);
+		return 0;
+	}
+
+	jid = (u64 *)kmalloc(sizeof(u64 *)*jidlst.r_val, GFP_KERNEL);
+	if (!jid) {
+		jidlst.r_val = 0;
+		DBG_PRINTEXIT(-ENOMEM);
+		if (copy_to_user(jidlst_args, &jidlst, sizeof(jidlst))) {
+			DBG_PRINTEXIT(-EFAULT);
+			return -EFAULT;
+		}
+		return -ENOMEM;
+	}
+
+
+	count = 0;
+	JOB_RLOCK(&job_table_sem);
+	for (i = 0; i < HASH_SIZE && count < jidlst.r_val; i++) {
+		list_for_each(job_entry, &job_table[i]) {
+			job = list_entry(job_entry, struct job_entry, entry);
+			jid[count++] = job->jid;
+			if (count == jidlst.r_val) {
+				break;
+			}
+		}
+	}
+	JOB_RUNLOCK(&job_table_sem);
+
+	DBG_PRINTEXIT(0);
+	jidlst.r_val = count;
+
+	for (i = 0; i < count; i++) {
+		if (copy_to_user(jidlst.jid+i, &jid[i], sizeof(u64))) {
+			DBG_PRINTEXIT(-EFAULT);
+			return -EFAULT;
+		}
+	}
+
+	kfree(jid);
+
+	if (copy_to_user(jidlst_args, &jidlst, sizeof(jidlst))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+
+/*
+ * job_sys_getpidcnt
+ *
+ * Get the number of processes currently attached to a specific job.
+ *
+ * returns 0 on success, or negative errno value on failure.
+ */
+static int
+job_sys_getpidcnt(struct job_pidcnt *pidcnt_args)
+{
+	struct job_pidcnt pidcnt;
+	struct job_entry  *job;
+	int	     retcode = 0;
+	DBG_PRINTINIT("job_sys_getpidcnt");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&pidcnt, pidcnt_args, sizeof(pidcnt))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	pidcnt.r_val = 0;
+
+	JOB_RLOCK(&job_table_sem);
+	job = job_getjob(pidcnt.jid);
+	if (!job) {
+		retcode = -ENODATA;
+	} else {
+		/* Read lock might be overdoing it for this case */
+		JOB_RLOCK(&job->sem);
+		pidcnt.r_val = job->refcnt;
+		JOB_RUNLOCK(&job->sem);
+	}
+	JOB_RUNLOCK(&job_table_sem);
+
+	DBG_PRINTEXIT(retcode);
+
+	if (copy_to_user(pidcnt_args, &pidcnt, sizeof(pidcnt))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return retcode;
+}
+
+/*
+ * job_getpidlst
+ *
+ * Get the list of processes (pids) currently attached to the specified
+ * job.  The number of processes provided is limited by the number the user
+ * specivies that they can accept (have memory for) and the number currently
+ * attached.
+ *
+ * returns 0 on success, negative errno value on failure.
+ */
+static int
+job_sys_getpidlst(struct job_pidlst *pidlst_args)
+{
+	struct job_pidlst	 pidlst;
+	struct job_entry	 *job;
+	struct job_attach	 *attached;
+	struct list_head *attached_entry;
+	pid_t		 *pid;
+	int		 max;
+	int		 i;
+	DBG_PRINTINIT("job_sys_getpidlst");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&pidlst, pidlst_args, sizeof(pidlst))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+
+	if (pidlst.r_val == 0) {
+		DBG_PRINTEXIT(0);
+		return 0;
+	}
+
+	max = pidlst.r_val;
+	pidlst.r_val = 0;
+	pid = (pid_t *)kmalloc(sizeof(pid_t)*max, GFP_KERNEL);
+	if (!pid) {
+		DBG_PRINTEXIT(-ENOMEM);
+		if (copy_to_user(pidlst_args, &pidlst, sizeof(pidlst))) {
+			DBG_PRINTEXIT(-EFAULT);
+			return -EFAULT;
+		}
+		return -ENOMEM;
+	}
+
+	JOB_RLOCK(&job_table_sem);
+
+	job = job_getjob(pidlst.jid);
+	if (!job) {
+
+		JOB_RUNLOCK(&job_table_sem);
+
+		DBG_PRINTEXIT(-ENODATA);
+		if (copy_to_user(pidlst_args, &pidlst, sizeof(pidlst))) {
+			DBG_PRINTEXIT(-EFAULT);
+			return -EFAULT;
+		}
+		return -ENODATA;
+	} else {
+
+		JOB_RLOCK(&job->sem);
+		JOB_RUNLOCK(&job_table_sem);
+
+		i = 0;
+		list_for_each(attached_entry, &job->attached) {
+			if (i == max) {
+				break;
+			}
+			attached = list_entry(attached_entry, struct job_attach, 
+					entry);
+			pid[i++] = attached->task->pid;
+		}
+		pidlst.r_val = i;
+
+		JOB_RUNLOCK(&job->sem);
+	}
+
+	for (i = 0; i < pidlst.r_val; i++) {
+		if (copy_to_user(pidlst.pid+i, &pid[i], sizeof(pid_t))) {
+			DBG_PRINTEXIT(-EFAULT);
+			return -EFAULT;
+		}
+	}
+	kfree(pid);
+
+	DBG_PRINTEXIT(0);
+	copy_to_user(pidlst_args, &pidlst, sizeof(pidlst));
+	return 0;
+}
+
+
+/*
+ * job_sys_getuser
+ *
+ * Get the uid of the user that owns the job.
+ *
+ * returns 0 on success, returns negative errno on failure.
+ */
+static int
+job_sys_getuser(struct job_user *user_args)
+{
+	struct job_entry *job;
+	struct job_user user;
+	int        retcode = 0;
+	DBG_PRINTINIT("job_sys_getuser");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&user, user_args, sizeof(user))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return(-EFAULT);
+	}
+
+	user.r_user = 0;
+
+	JOB_RLOCK(&job_table_sem);
+
+	job = job_getjob(user.jid);
+	if (!job) {
+		retcode = -ENODATA;
+	} else {
+		JOB_RLOCK(&job->sem);
+		user.r_user = job->user;
+		JOB_RUNLOCK(&job->sem);
+	}
+
+	JOB_RUNLOCK(&job_table_sem);
+
+	if (copy_to_user(user_args, &user, sizeof(user))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	DBG_PRINTEXIT(retcode);
+	return retcode;
+}
+
+
+/* 
+ * job_sys_getprimepid
+ *
+ * Get the primary process - the oldest process in the job.
+ *
+ * returns 0 on success, negative errno on failure.
+ */
+static int
+job_sys_getprimepid(struct job_primepid *primepid_args)
+{
+	struct job_primepid   primepid;
+	struct job_entry      *job = NULL;
+	struct job_attach     *attached = NULL;
+	int              retcode = 0;
+	DBG_PRINTINIT("getprimepid");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&primepid, primepid_args, sizeof(primepid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	primepid.r_pid = 0;
+
+	JOB_RLOCK(&job_table_sem);
+
+	job = job_getjob(primepid.jid);
+	if (!job) {
+		JOB_RUNLOCK(&job_table_sem);
+		/* Job not found, return INVALID VALUE */
+		DBG_PRINTEXIT(-ENODATA);
+		return -ENODATA;
+	}
+
+	/* 
+	 * Job found, now look at first pid entry in the 
+	 * attached list.
+	 */
+	JOB_RLOCK(&job->sem);
+	JOB_RUNLOCK(&job_table_sem);
+	if (list_empty(&job->attached)) {
+		retcode = -ESRCH;
+		primepid.r_pid = 0;
+	}  else {
+		attached = list_entry(job->attached.next, struct job_attach, entry);
+		if (!attached->task) {
+			retcode = -ESRCH;
+		} else {
+			primepid.r_pid = attached->task->pid;
+		}
+	}
+	JOB_RUNLOCK(&job->sem);
+
+	if (copy_to_user(primepid_args, &primepid, sizeof(primepid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	DBG_PRINTEXIT(retcode);
+	return retcode;
+}
+
+
+/* 
+ * job_sys_sethid
+ *
+ * This function is used to set the host ID segment for the job IDs (jid).
+ * If this does not get set, then the jids upper 32 bits will be set to 
+ * 0 and the jid cannot be used reliably in a cluster environment.
+ *
+ * returns -errno value on fail, 0 on success
+ */
+static int
+job_sys_sethid(struct job_sethid *sethid_args)
+{
+	struct job_sethid	sethid;
+	int			errcode = 0;
+	DBG_PRINTINIT("job_sys_sethid");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&sethid, sethid_args, sizeof(sethid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	if (!capable(CAP_SYS_RESOURCE)) {
+		errcode = -EPERM;
+		sethid.r_hid = 0;
+		goto cleanup_return;
+	}
+
+	/* 
+	 * Set job_table_sem, so no jobs can be deleted while doing
+	 * this operation.
+	 */
+	JOB_WLOCK(&job_table_sem); 
+
+	sethid.r_hid = jid_hid = sethid.hid;
+
+	JOB_WUNLOCK(&job_table_sem);
+
+cleanup_return:
+	DBG_PRINTEXIT(errcode);
+	if (copy_to_user(sethid_args, &sethid, sizeof(sethid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return errcode;
+}
+
+
+/* 
+ * job_sys_detachjid
+ *
+ * This function is detach all the processes from a job, but allows the 
+ * processes to continue running.  You need CAP_SYS_RESOURCE capability
+ * for this to succeed. Since all processes will be detached, the job will
+ * exit.
+ *
+ * returns -errno value on fail, 0 on success
+ */
+static int
+job_sys_detachjid(struct job_detachjid *detachjid_args)
+{
+	struct job_detachjid	   detachjid;
+	struct job_entry	   *job;
+	struct list_head   *entry;
+	int		   count;
+	int		   errcode = 0;
+	struct task_struct *task;
+	struct pagg *pagg;
+
+	DBG_PRINTINIT("job_sys_detachjid");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&detachjid, detachjid_args, sizeof(detachjid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	detachjid.r_val = 0;
+
+	if (!capable(CAP_SYS_RESOURCE)) {
+		errcode = -EPERM;
+		goto cleanup_return;
+	}
+
+	/* 
+	 * Set job_table_sem, so no jobs can be deleted while doing
+	 * this operation.
+	 */
+	JOB_WLOCK(&job_table_sem); 
+
+	job = job_getjob(detachjid.jid);
+
+	if (job) {
+
+		JOB_WLOCK(&job->sem);
+
+		/* Mark job as ZOMBIE so no new processes can attach to it */	
+		job->state = ZOMBIE;
+
+		count = job->refcnt;
+
+		/* Okay, no new processes can attach to the job.  We can 
+		 * release the locks on the job_table and job since the only
+		 * way for the job to change now is for tasks to detach and
+		 * the job to be removed.  And this is what we want to happen
+		 */
+		JOB_WUNLOCK(&job_table_sem);
+		JOB_WUNLOCK(&job->sem);
+
+
+		/* Walk through list of attached tasks and unset the 
+		 * pagg entries. 
+		 * 
+		 * We don't test with list_empty because that actually means NO tasks
+		 * left rather than one task.  If we used !list_empty or list_for_each,
+		 * we could reference memory freed by detach_pagg (job_detach).
+		 * 
+		 * We know there is only one task left when job->attached.next and
+		 * job->attached.prev both point to the same place.
+		 */
+		while (job->attached.next != job->attached.prev) {
+			entry = job->attached.next;
+
+			task = (list_entry(entry, struct job_attach, entry))->task;
+			pagg = (list_entry(entry, struct job_attach, entry))->pagg;
+
+			write_lock_pagg_list(task);
+			detach_pagg(task, pagg);
+			free_pagg(pagg);
+			write_unlock_pagg_list(task);
+
+		}
+		/* At this point, there is only one task left */
+
+		entry = job->attached.next;
+
+		task = (list_entry(entry, struct job_attach, entry))->task;
+		pagg = (list_entry(entry, struct job_attach, entry))->pagg;
+
+		write_lock_pagg_list(task);
+		detach_pagg(task, pagg);
+		free_pagg(pagg);
+		write_unlock_pagg_list(task);
+			
+		detachjid.r_val = count;
+
+	} else {
+		errcode = -ENODATA;
+		JOB_WUNLOCK(&job_table_sem);
+	}
+
+cleanup_return:
+	DBG_PRINTEXIT(errcode);
+	if (copy_to_user(detachjid_args, &detachjid, sizeof(detachjid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return errcode;
+}
+
+
+/* 
+ * job_sys_detachpid
+ *
+ * This function is detach a process from the job it is attached too, 
+ * but allows the processes to continue running.  You need 
+ * CAP_SYS_RESOURCE capability for this to succeed. 
+ *
+ * returns -errno value on fail, 0 on success
+ */
+static int
+job_sys_detachpid(struct job_detachpid *detachpid_args)
+{
+	struct job_detachpid	   detachpid;
+	struct task_struct *task;
+	struct pagg *pagg;
+	int		   errcode = 0;
+	DBG_PRINTINIT("job_sys_detachpid");
+
+	DBG_PRINTENTRY();
+
+	if (copy_from_user(&detachpid, detachpid_args, sizeof(detachpid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+
+	detachpid.r_jid = 0;
+
+	if (!capable(CAP_SYS_RESOURCE)) {
+		errcode = -EPERM;
+		goto cleanup_return;
+	}
+
+	/* Lock the task list while we find a specific task */
+	read_lock(&tasklist_lock);
+	task = find_task_by_pid(detachpid.pid);
+	if (!task) {
+		errcode = -ESRCH;
+		/* We need to unlock the tasklist here too or the lock is held forever */
+		read_unlock(&tasklist_lock);
+		goto cleanup_return;
+	}
+
+	/* We have a valid task now */
+	get_task_struct(task); /* Ensure the task doesn't vanish on us */
+	read_unlock(&tasklist_lock); /* Unlock the tasklist */
+	write_lock_pagg_list(task);
+
+	pagg = get_pagg(task, pagg_hook.name);
+	if (pagg) {
+		detachpid.r_jid = ((struct job_attach *)pagg->data)->job->jid;
+		detach_pagg(task, pagg);
+		free_pagg(pagg);
+	} else {
+		errcode = -ENODATA;
+	}
+	put_task_struct(task);  /* Done accessing the task */
+	write_unlock_pagg_list(task);
+
+cleanup_return:
+	DBG_PRINTEXIT(errcode);
+	if (copy_to_user(detachpid_args, &detachpid, sizeof(detachpid))) {
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;
+	}
+	return errcode;
+}
+
+
+/*
+ * job_register_acct
+ *
+ * This function is used by modules that are registering to provide job 
+ * accounting services.
+ *
+ * returns -errno value on fail, 0 on success.
+ */
+int 
+job_register_acct(struct job_acctmod *am)
+{
+	DBG_PRINTINIT("job_register_acct");
+
+	DBG_PRINTENTRY();
+
+	if (!am) {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;	/* error, invalid value */
+	}
+	if (am->type < 0 || am->type > (JOB_ACCT_COUNT-1)) {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;	/* error, invalid value */
+	}
+
+	JOB_WLOCK(&acct_list_sem);
+	if (acct_list[am->type] != NULL) {
+		JOB_WUNLOCK(&acct_list_sem);
+		DBG_PRINTEXIT(-EBUSY);
+		return -EBUSY;	/* error, duplicate entry */
+	}
+
+	acct_list[am->type] = am;
+	JOB_WUNLOCK(&acct_list_sem);
+	DBG_PRINTEXIT(0);
+	return 0;
+}
+
+
+/*
+ * job_unregister_acct
+ *
+ * This is used by accounting modules to unregister with the job module as
+ * subscribers for job accounting information.
+ *
+ * Returns -errno on failure and 0 on success.
+ */
+int 
+job_unregister_acct(struct job_acctmod *am)
+{
+	DBG_PRINTINIT("job_unregister_acct");
+
+	DBG_PRINTENTRY();
+
+	if (!am) {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;	/* error, invalid value */
+	}
+	if (am->type < 0 || am->type > (JOB_ACCT_COUNT-1))  {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;	/* error, invalid value */
+	}
+
+	JOB_WLOCK(&acct_list_sem);
+	if (acct_list[am->type] != am) {
+		JOB_WUNLOCK(&acct_list_sem);
+		DBG_PRINTEXIT(-EFAULT);
+		return -EFAULT;	/* error, not matching entry */
+	}
+
+	acct_list[am->type] = NULL;
+	JOB_WUNLOCK(&acct_list_sem);
+	DBG_PRINTEXIT(0);
+	return 0;
+}
+
+/*
+ * job_getjid
+ *
+ * This function will return the Job ID for the given task.  If
+ * the task is not attached to a job, then 0 is returned.
+ *
+ */
+u64 job_getjid(struct task_struct *task)
+{
+	struct pagg *pagg = NULL;
+	struct job_entry	   *job = NULL;
+	u64	   jid = 0;
+	DBG_PRINTINIT("job_getjid");
+
+	DBG_PRINTENTRY();
+
+	read_lock_pagg_list(task);
+	pagg = get_pagg(task, pagg_hook.name);
+	if (pagg) {
+		job = ((struct job_attach *)pagg->data)->job;
+		JOB_RLOCK(&job->sem);
+		jid = job->jid;
+		JOB_RUNLOCK(&job->sem);
+	}
+	read_unlock_pagg_list(task);
+
+	DBG_PRINTEXIT((int)jid);
+	return jid;
+}
+
+
+/*
+ * job_getacct
+ *
+ * This function is used by accounting subscribers to get accounting 
+ * information about a job.
+ *
+ * The caller must supply the Job ID (jid) that specifies the job. The
+ * "type" argument indicates the type of accounting data to be returned.
+ * The data will be returned in the memory accessed via the data pointer
+ * argument.  The data pointer is void so that this function interface
+ * can handle different types of accounting data.
+ */
+int job_getacct(u64 jid, int type, void *data)
+{
+	struct job_entry	*job;
+	DBG_PRINTINIT("job_getacct");
+
+	DBG_PRINTENTRY();
+
+	if (!data) {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;
+	}
+
+	if (!jid) {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;
+	}
+
+	JOB_RLOCK(&job_table_sem);
+	job = job_getjob(jid);
+	if (!job) {
+		JOB_RUNLOCK(&job_table_sem);
+		DBG_PRINTEXIT(-ENODATA);
+		return -ENODATA;
+	}
+
+	JOB_RLOCK(&job->sem);
+	JOB_RUNLOCK(&job_table_sem);
+
+	switch (type) {
+#if defined(CONFIG_CSA_JOB_ACCT) || defined(CONFIG_CSA_JOB_ACCT_MODULE)
+		case JOB_ACCT_CSA: 
+		{
+			struct job_csa *csa = (struct job_csa *)data;
+
+			csa->job_id = job->jid;
+			csa->job_uid = job->user;
+			csa->job_start = job->start;
+			csa->job_corehimem = job->csa.corehimem;
+			csa->job_virthimem = job->csa.virthimem;
+			csa->job_acctfile = job->csa.acctfile;
+			break;
+		}
+#endif
+		default:
+			JOB_RUNLOCK(&job->sem);
+			DBG_PRINTEXIT(-EINVAL);
+			return -EINVAL;
+			break;
+	}
+	JOB_RUNLOCK(&job->sem);
+	DBG_PRINTEXIT(0);
+	return 0;
+}
+
+/*
+ * job_setacct
+ *
+ * This function is used by accounting subscribers to set specific
+ * accounting information in the job (so that the job remembers it
+ * in relation to a specific job).
+ *
+ * The job is identified by the jid argument.  The type indicates the
+ * type of accounting the information is associated with.  The subfield
+ * is a bitmask that indicates exactly what subfields are to be changed.
+ * The data that is used to set the values is supplied by the data pointer.
+ * The data pointer is a void type so that the interface can be used for
+ * different types of accounting information.
+ */
+int job_setacct(u64 jid, int type, int subfield, void *data)
+{
+	struct job_entry	*job;
+	DBG_PRINTINIT("job_setacct");
+
+	DBG_PRINTENTRY();
+
+	if (!data) {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;
+	}
+
+	if (!jid) {
+		DBG_PRINTEXIT(-EINVAL);
+		return -EINVAL;
+	}
+
+	JOB_RLOCK(&job_table_sem);
+	job = job_getjob(jid);
+	if (!job) {
+		JOB_RUNLOCK(&job_table_sem);
+		DBG_PRINTEXIT(-ENODATA);
+		return -ENODATA;
+	}
+
+	JOB_RLOCK(&job->sem);
+	JOB_RUNLOCK(&job_table_sem);
+
+	switch (type) {
+#if defined(CONFIG_CSA_JOB_ACCT) || defined(CONFIG_CSA_JOB_ACCT_MODULE)
+		case JOB_ACCT_CSA:
+		{
+			struct job_csa *csa = (struct job_csa *)data;
+			
+			if (subfield & JOB_CSA_ACCTFILE) {
+				job->csa.acctfile = csa->job_acctfile;
+			}
+			break;
+		}
+#endif
+		default:
+			JOB_RUNLOCK(&job->sem);
+			DBG_PRINTEXIT(-EINVAL);
+			return -EINVAL;
+			break;
+	}
+	JOB_RUNLOCK(&job->sem);
+	DBG_PRINTEXIT(0);
+	return 0;
+}
+
+
+
+/*
+ * job_syscall
+ *
+ * Function to handle job syscall requests.
+ *
+ * Returns 0 on success and -(ERRNO VALUE) upon failure.
+ */
+int
+job_syscall(unsigned int request, unsigned long data)
+{                 
+	int rc=0;
+
+	DBG_PRINTINIT("job_syscall");
+
+	DBG_PRINTENTRY();
+
+	switch (request) {
+		case JOB_CREATE:
+			rc = job_sys_create((struct job_create *)data);
+			break;
+		case JOB_ATTACH:
+		case JOB_DETACH:
+			/* RESERVED */
+			rc = -EBADRQC;
+			break;
+		case JOB_GETJID:
+			rc = job_sys_getjid((struct job_getjid *)data);
+			break;
+		case JOB_WAITJID:
+			rc = job_sys_waitjid((struct job_waitjid *)data);
+			break;
+		case JOB_KILLJID:
+			rc = job_sys_killjid((struct job_killjid *)data);
+			break;
+		case JOB_GETJIDCNT:
+			rc = job_sys_getjidcnt((struct job_jidcnt *)data);
+			break;
+		case JOB_GETJIDLST:
+			rc = job_sys_getjidlst((struct job_jidlst *)data);
+			break;
+		case JOB_GETPIDCNT:
+			rc = job_sys_getpidcnt((struct job_pidcnt *)data);
+			break;
+		case JOB_GETPIDLST:
+			rc = job_sys_getpidlst((struct job_pidlst *)data);
+			break;
+		case JOB_GETUSER:
+			rc = job_sys_getuser((struct job_user *)data);
+			break;
+		case JOB_GETPRIMEPID:
+			rc = job_sys_getprimepid((struct job_primepid *)data);
+			break;
+		case JOB_SETHID:
+			rc = job_sys_sethid((struct job_sethid *)data);
+			break;
+		case JOB_DETACHJID:
+			rc = job_sys_detachjid((struct job_detachjid *)data);
+			break;
+		case JOB_DETACHPID:
+			rc = job_sys_detachpid((struct job_detachpid *)data);
+			break;
+		case JOB_SETJLIMIT:
+		case JOB_GETJLIMIT:
+		case JOB_GETJUSAGE:
+		case JOB_FREE:
+		default:
+			rc = -EBADRQC;
+			break;
+	}
+
+	DBG_PRINTEXIT(rc);
+	return rc;
+}
+
+
+/*
+ * job_ioctl
+ *
+ * Function to handle job ioctl call requests.
+ *
+ * Returns 0 on success and -(ERRNO VALUE) upon failure.
+ */
+int
+job_ioctl(struct inode *inode, struct file *file, unsigned int request,
+	  unsigned long data)        
+{                 
+	return job_syscall(request, data);
+}
+
+
+/* 
+ * init_module
+ *
+ * This function is called when a module is inserted into a kernel. This
+ * function allocates any necessary structures and sets initial values for
+ * module data.
+ *
+ * If the function succeeds, then 0 is returned.  On failure, -1 is returned.
+ */
+static int __init
+init_job(void) 
+{
+	int i,rc;
+
+
+	/* Initialize the job table chains */
+	for (i = 0; i < HASH_SIZE; i++) {
+		INIT_LIST_HEAD(&job_table[i]);
+	}
+
+	/* Initialize the list for accounting subscribers */
+	for (i = 0; i < JOB_ACCT_COUNT; i++) {
+		acct_list[i] = NULL;
+	}
+
+	/* Get hostID string and fill in jid_template hostID segment */
+	if (hid) {
+		jid_hid = (int)simple_strtoul(hid, &hid, 16);
+	} else {
+		jid_hid = 0;
+	}
+
+	rc = register_pagg_hook(&pagg_hook);
+	if (rc < 0) {
+		return -1;
+	}
+
+	/* Setup our /proc entry file */
+	job_proc_entry = create_proc_entry(JOB_PROC_ENTRY,
+		S_IFREG | S_IRUGO, &proc_root);
+
+	if (!job_proc_entry) {
+		unregister_pagg_hook(&pagg_hook);
+		return -1;
+	}
+
+	job_proc_entry->proc_fops = &job_file_ops;
+	job_proc_entry->proc_iops = NULL;
+
+
+	return 0;
+}
+
+/*
+ * cleanup_module
+ *
+ * This function is called to cleanup after a module when it is removed.
+ * All memory allocated for this module will be freed.
+ *
+ * This function does not take any inputs or produce and output.
+ */
+static void __exit
+cleanup_job(void)
+{
+	remove_proc_entry(JOB_PROC_ENTRY, &proc_root);
+	unregister_pagg_hook(&pagg_hook);
+	return;
+}
+
+module_init(init_job);
+module_exit(cleanup_job);
+
+EXPORT_SYMBOL(job_register_acct);
+EXPORT_SYMBOL(job_unregister_acct);
+EXPORT_SYMBOL(job_getjid);
+EXPORT_SYMBOL(job_getacct);
+EXPORT_SYMBOL(job_setacct);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Silicon Graphics, Inc.");
+MODULE_DESCRIPTION("PAGG-based inescapable jobs");
+
+#endif /* CONFIG_PAGG_JOB */
diff -Naurp -X /home/jbarnes/dontdiff 090-fakeprom-update.patch/kernel/pagg.c 150-pagg-job.patch/kernel/pagg.c
--- 090-fakeprom-update.patch/kernel/pagg.c	Wed Dec 31 16:00:00 1969
+++ 150-pagg-job.patch/kernel/pagg.c	Wed Jan 21 09:21:48 2004
@@ -0,0 +1,401 @@
+/* 
+ * PAGG (Process Aggregates) interface
+ *
+ * 
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of version 2 of the GNU General Public License 
+ * as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+ * 
+ * Further, this software is distributed without any warranty that it is 
+ * free of the rightful claim of any third person regarding infringement 
+ * or the like.  Any license provided herein, whether implied or 
+ * otherwise, applies only to this software file.  Patent licenses, if 
+ * any, provided herein do not apply to combinations of this program with 
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program; if not, write the Free Software 
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ * 
+ * Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
+ */
+
+/*
+ * Description:  This file, kernel/pagg.c, contains the routines used
+ *               to implement process aggregates (paggs).  The pagg
+ *               extends the task_struct to allow for various process
+ *               aggregation continers.  Examples of such containers
+ *               include "jobs" and cluster applications IDs.  Process
+ *               sessions and groups could have been implemented using
+ *               paggs (although there would be little purpose in
+ *               making that change at this juncture).  The pagg
+ *               structure maintains pointers to callback functions and
+ *               data strucures maintained in modules that have
+ *               registered with the kernel as pagg container
+ *               providers.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_PAGG
+
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <asm/semaphore.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/pagg.h>
+
+/* list of pagg hook entries that reference the "module" implementations */
+static LIST_HEAD(pagg_hook_list);
+static DECLARE_RWSEM(pagg_hook_list_sem);
+
+
+/* 
+ * get_pagg
+ *
+ * Given a pagg_list list structure, this function will return
+ * a pointer to the pagg struct that matches the search
+ * key.  If the key is not found, the function will return NULL.
+ *
+ * The caller should hold at least a read lock on the pagg_list
+ * for task using read_lock_pagg_list(task).
+ */
+struct pagg *
+get_pagg(struct task_struct *task, char *key)
+{
+	struct list_head *entry;
+
+	list_for_each(entry, &task->pagg_list.head) {
+		struct pagg *pagg = list_entry(entry, struct pagg, entry);
+		if (!strcmp(pagg->hook->name,key)) {
+			return pagg;
+		}
+	}
+	return NULL;
+}
+
+
+/*
+ * alloc_pagg
+ *
+ * Given a task and a pagg hook, this function will allocate
+ * a new pagg structure, initialize the settings, and insert the pagg into
+ * the pagg_list for the task.
+ *
+ * The caller for this function should hold at least a read lock on the
+ * pagg_hook_list_sem - or ensure that the pagg hook entry cannot be 
+ * removed. If this function was called from the pagg module (usually the
+ * case), then the caller need not hold this lock. The caller should hold 
+ * a write lock on for the tasks pagg_list.sem.  This can be locked using the
+ * write_lock_pagg_list(task) macro.
+ */
+struct pagg *
+alloc_pagg(struct task_struct *task, struct pagg_hook *pagg_hook)
+{
+	struct pagg *pagg;
+
+	pagg = (struct pagg *)kmalloc(sizeof(struct pagg), GFP_KERNEL);
+	if (!pagg)
+		return NULL;
+
+	pagg->hook = pagg_hook;
+	pagg->data = NULL;
+	list_add_tail(&pagg->entry, &task->pagg_list.head);
+	return pagg;
+}
+
+
+/*
+ * free_pagg
+ *
+ * This function will ensure the pagg is deleted form 
+ * the list of pagg entries for the task. Finally, the memory for the 
+ * pagg is discarded.
+ *
+ * The caller of this function should hold a write lock on the pagg_list.sem
+ * for the task. This can be locke dusing the write_lock_pagg_list(task) 
+ * macro.
+ *
+ * Prior to calling free_pagg, the pagg should have been detached from the
+ * pagg container represented by this pagg.  That is usually done using the
+ * macro detach_pagg(pagg).
+ */
+void
+free_pagg(struct pagg *pagg) 
+{
+
+	list_del(&pagg->entry);
+	kfree(pagg);
+}
+
+
+/*
+ * get_pagg_hook
+ *
+ * Given a pagg hook name key, this function will return a pointer
+ * to the pagg_hook struct that contains that matches the name.
+ * 
+ * You should hold either the write or read lock for pagg_hook_list_sem
+ * before using this function.  This will ensure that the pagg_hook_list
+ * does not change while iterating through the list entries.
+ */
+static struct pagg_hook *
+get_pagg_hook(char *key)
+{
+	struct list_head *entry;
+	struct pagg_hook *pagg_hook;
+
+	list_for_each(entry, &pagg_hook_list) {
+		pagg_hook = list_entry(entry, struct pagg_hook, entry);
+		if (!strcmp(pagg_hook->name, key)) {
+			return pagg_hook;
+		}
+	}
+	return NULL;
+}
+
+
+/*
+ * register_pagg_hook
+ *
+ * Used to register a new pagg hook and enter it into the pagg_hook_list.
+ * The service name for a pagg hook is restricted to 32 characters.
+ *
+ * In the future an initialization function may also be defined so that all
+ * existing tasks can be assigned to a default pagg entry for the hook.
+ * However, this would require iterating through the tasklist.  To do that
+ * requires that the tasklist_lock be read locked.  Since the initialization
+ * function might be in a module, and therefore it might sleep (implementors
+ * decision), holding the tasklist_lock seems like a bad idea. It may be a
+ * requirement that the initialization function will be strictly forbidden
+ * from locking - by gentlemans agreement... 
+ *
+ * If a memory error is encountered, the pagg hook is unregistered and any
+ * tasks that have been attached to the initial pagg container are detached
+ * from that container.
+ */
+int
+register_pagg_hook(struct pagg_hook *pagg_hook_new)
+{
+	struct pagg_hook *pagg_hook = NULL;
+
+	/* ADD NEW PAGG MODULE TO ACCESS LIST */
+	if (!pagg_hook_new)
+		return -EINVAL;			/* error */
+	if (!list_empty(&pagg_hook_new->entry))
+		return -EINVAL;			/* error */
+	if (pagg_hook_new->name == NULL || strlen(pagg_hook_new->name) > PAGG_NAMELN) 
+		return -EINVAL;			/* error */
+
+	/* Try to insert new hook entry into the pagg hook list */
+	down_write(&pagg_hook_list_sem);
+
+	pagg_hook = get_pagg_hook(pagg_hook_new->name);
+
+	if (pagg_hook) {
+		up_write(&pagg_hook_list_sem);
+		printk(KERN_WARNING "Attempt to register duplicate"
+				" PAGG support (name=%s)\n", pagg_hook_new->name);
+		return -EBUSY;
+	}
+
+	/* Okay, we can insert into the pagg hook list */
+	list_add_tail(&pagg_hook_new->entry, &pagg_hook_list);
+	up_write(&pagg_hook_list_sem);
+
+	printk(KERN_INFO "Registering PAGG support for (name=%s)\n",
+			pagg_hook_new->name);
+
+	return 0;					/* success */
+
+}
+
+
+/*
+ * unregister_pagg_hook
+ *
+ * Used to unregister pagg hooks and remove them from the pagg_hook_list.
+ * Once the pagg hook entry in the pagg_hook_list is found, all of the
+ * tasks are scanned and detached from any pagg containers defined by the
+ * pagg implementation module.
+ */
+int
+unregister_pagg_hook(struct pagg_hook *pagg_hook_old)
+{
+	struct pagg_hook *pagg_hook;
+	struct task_struct *task;
+
+
+	/* Check the validity of the arguments */
+	if (!pagg_hook_old)
+		return -EINVAL;			/* error */
+	if (list_empty(&pagg_hook_old->entry))
+		return -EINVAL;			/* error */
+	if (pagg_hook_old->name == NULL)
+		return -EINVAL;			/* error */
+
+	down_write(&pagg_hook_list_sem);
+
+	pagg_hook = get_pagg_hook(pagg_hook_old->name);
+	if (pagg_hook && pagg_hook == pagg_hook_old) {
+		/* 
+		 * Scan through processes on system and check for  
+		 * references to pagg containers for this pagg hook.
+		 * 
+		 * The module cannot be unloaded if there are references.
+		 */
+		read_lock(&tasklist_lock);
+		for_each_process(task) {
+			struct pagg *pagg = NULL;
+
+			read_lock_pagg_list(task);
+			pagg = get_pagg(task, pagg_hook_old->name);
+			/* 
+			 * We won't be accessing pagg's memory, just need
+			 * to see if one existed - so we can release the task
+			 * lock now.
+			 */
+			read_unlock_pagg_list(task);
+			if (pagg) {
+				read_unlock(&tasklist_lock);
+				up_read(&pagg_hook_list_sem);
+				return -EBUSY;
+			}
+		}
+		list_del_init(&pagg_hook->entry);
+		read_unlock(&tasklist_lock);
+
+		up_write(&pagg_hook_list_sem);
+
+		printk(KERN_INFO "Unregistering PAGG support for"
+				" (name=%s)\n", pagg_hook_old->name);
+
+		return 0;			/* success */
+	}
+
+	up_write(&pagg_hook_list_sem);
+
+	printk(KERN_WARNING "Attempt to unregister PAGG support (name=%s)"
+			" failed - not found\n", pagg_hook_old->name);
+	
+	return -EINVAL;				/* error */
+}
+
+
+/*
+ * attach_pagg_list
+ *
+ * Used to attach a new task to the same pagg containers to which it's parent
+ * is attached.
+ *
+ * The "from" argument is the parent task.  The "to" argument is the child
+ * task. 
+ *
+ */
+int
+attach_pagg_list(struct task_struct *to_task, struct task_struct *from_task)
+{
+	struct list_head   *entry;
+	int  		   retcode = 0;
+
+
+
+	/* lock the parents pagg_list we are copying from */
+	read_lock_pagg_list(from_task);
+
+	list_for_each(entry, &from_task->pagg_list.head) {
+		struct pagg *to_pagg = NULL;
+		struct pagg *from_pagg = list_entry(entry, struct pagg, 
+							entry);
+		to_pagg = alloc_pagg(to_task, from_pagg->hook);
+		if (!to_pagg) {
+			retcode = -ENOMEM;
+			goto error_return;
+		}
+		retcode = attach_pagg(to_task, to_pagg, from_pagg->data);
+		if (retcode != 0) {
+			/* attach should issue error message */
+			goto error_return;
+		}
+	}
+
+	read_unlock_pagg_list(from_task);
+
+	return 0;					/* success */
+
+  error_return:
+	/* 
+	 * Clean up all the pagg attachments made on behalf of the new
+	 * task.  Set new task pagg ptr to NULL for return.
+	 */
+	read_unlock_pagg_list(from_task);
+	detach_pagg_list(to_task);
+	return retcode;				/* failure */
+}
+
+
+/*
+ * detach_pagg_list
+ *
+ * Used to detach a task from all pagg containers to which it is attached.
+ */
+int
+detach_pagg_list(struct task_struct *task)
+{
+	struct list_head   *entry;
+	int retcode = 0;
+	int rettmp = 0;
+
+	/* Remove ref. to paggs from task immediately */
+	write_lock_pagg_list(task);
+
+	if (list_empty(&task->pagg_list.head)) {
+		write_unlock_pagg_list(task);
+		return retcode;
+	} 
+
+	list_for_each(entry, &task->pagg_list.head) {
+		int rettemp = 0;
+		struct pagg *pagg = list_entry(entry, struct pagg, entry);
+
+		entry = &task->pagg_list.head;
+
+		rettemp = detach_pagg(task, pagg);
+		if (rettmp) {
+			/* an error message should be logged in free_pagg */
+			retcode = rettmp;
+		}
+		free_pagg(pagg);
+	}
+
+	write_unlock_pagg_list(task);
+
+	return retcode;	/* 0 = success, else return last code for failure */
+}
+
+
+EXPORT_SYMBOL(get_pagg);
+EXPORT_SYMBOL(alloc_pagg);
+EXPORT_SYMBOL(free_pagg);
+EXPORT_SYMBOL(attach_pagg_list);
+EXPORT_SYMBOL(detach_pagg_list);
+EXPORT_SYMBOL(register_pagg_hook);
+EXPORT_SYMBOL(unregister_pagg_hook);
+
+#endif /* CONFIG_PAGG */
