LWN.net Logo

Process Aggregates (PAGG) update for 2.6.6

From:  Erik Jacobson <erikj@subway.americas.sgi.com>
To:  linux-kernel@vger.kernel.org
Subject:  [PATCH] Process Aggregates (PAGG) update for 2.6.6
Date:  Thu, 27 May 2004 09:15:36 -0500

Hi there.

Attached is a fresh PAGG patch for the 2.6.6 kernel.

This patch implements the LKML feedback received so far (with very few
exceptions).

I would be happy to post the inescapable jobs patch (a user of PAGG) as well.
It can be found here (along with pagg):

http://oss.sgi.com/projects/pagg/

Thanks!

--
Erik Jacobson - Linux System Software - Silicon Graphics - Eagan, Minnesota
diff -Naru 2.6-patch/Documentation/pagg.txt 2.6pagg-patch/Documentation/pagg.txt
--- 2.6-patch/Documentation/pagg.txt	1969-12-31 18:00:00.000000000 -0600
+++ 2.6pagg-patch/Documentation/pagg.txt	2004-05-24 08:08:34.000000000 -0500
@@ -0,0 +1,32 @@
+Linux Process Aggregates (PAGG)
+-------------------------------
+
+The process aggregates infrastructure, or PAGG, provides a generalized
+mechanism for providing arbitrary process groups in Linux.  PAGG consists
+of a series of functions for registering and unregistering support
+for new types of process aggregation containers with the kernel.
+This is similar to the support currently provided within Linux that
+allows for dynamic support of filesystems, block and character devices,
+symbol tables, network devices, serial devices, and execution domains.
+This implementation of PAGG provides developers the basic hooks necessary
+to implement kernel modules for specific process containers, such as
+the job container.
+
+The do_fork function in the kernel was altered to support PAGG.  If a
+process is attached to any PAGG containers and subsequently forks a
+child process, the child process will also be attached to the same PAGG
+containers.  The PAGG containers involved during the fork are notified
+that a new process has been attached.  The notification is accomplished
+via a callback function provided by the PAGG module.
+
+The do_exit function in the kernel has also been altered.  If a process
+is attached to any PAGG containers and that process is exiting, the PAGG
+containers are notified that a process has detached from the container.
+The notification is accomplished via a callback function provided by
+the PAGG module.
+
+The sys_execve function has been modified to support an optional callout
+that can be run when a process in a pagg list does an exec.  It can be 
+used, for example, by other kernel modules that wish to do advanced CPU
+placement on multi-processor systems (just one example).
+
diff -Naru 2.6-patch/fs/exec.c 2.6pagg-patch/fs/exec.c
--- 2.6-patch/fs/exec.c	2004-05-10 12:06:11.000000000 -0500
+++ 2.6pagg-patch/fs/exec.c	2004-05-20 14:46:49.000000000 -0500
@@ -46,7 +46,7 @@
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/rmap.h>
-
+#include <linux/pagg.h>
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/mmu_context.h>
@@ -1142,6 +1142,7 @@
 	retval = search_binary_handler(&bprm,regs);
 	if (retval >= 0) {
 		free_arg_pages(&bprm);
+		pagg_exec(current);
 
 		/* execve success */
 		security_bprm_free(&bprm);
diff -Naru 2.6-patch/include/linux/init_task.h 2.6pagg-patch/include/linux/init_task.h
--- 2.6-patch/include/linux/init_task.h	2004-05-10 12:06:11.000000000 -0500
+++ 2.6pagg-patch/include/linux/init_task.h	2004-05-20 14:46:49.000000000 -0500
@@ -2,6 +2,7 @@
 #define _LINUX__INIT_TASK_H
 
 #include <linux/file.h>
+#include <linux/pagg.h>
 
 #define INIT_FILES \
 { 							\
@@ -112,6 +113,7 @@
 	.proc_lock	= SPIN_LOCK_UNLOCKED,				\
 	.switch_lock	= SPIN_LOCK_UNLOCKED,				\
 	.journal_info	= NULL,						\
+	INIT_TASK_PAGG(tsk)						\
 }
 
 
diff -Naru 2.6-patch/include/linux/pagg.h 2.6pagg-patch/include/linux/pagg.h
--- 2.6-patch/include/linux/pagg.h	1969-12-31 18:00:00.000000000 -0600
+++ 2.6pagg-patch/include/linux/pagg.h	2004-05-26 08:31:00.000000000 -0500
@@ -0,0 +1,191 @@
+/* 
+ * PAGG (Process Aggregates) interface
+ *
+ * 
+ * Copyright (c) 2000-2002, 2004 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Contact information:  Silicon Graphics, Inc., 1500 Crittenden Lane,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan

+ */
+
+/*
+ * Data structure definitions and function prototypes used to implement
+ * process aggregates (paggs).
+ *
+ * Paggs provides a generalized way 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 _LINUX_PAGG_H
+#define _LINUX_PAGG_H
+
+#include <linux/sched.h>
+
+#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:	Task struct to init the pagg_list and semaphore in
+ */
+#define INIT_PAGG_LIST(_l)						\
+do {									\
+	INIT_LIST_HEAD(&(_l)->pagg_list);					\
+	init_rwsem(&(_l)->pagg_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
+ *     exec:           Function pointer to function used when a process
+ *                     in the pagg container exec's a new process. This
+ *                     is optional and may be set to NULL if it is not 
+ *                     needed by the pagg module.
+ *     refcnt:         Keep track of user count of the pagg hook
+ */
+struct pagg_hook {
+       struct module	*module;
+       char		*name;	/* Name Key - restricted to 32 characters */
+       void		*data;	/* Opaque module specific data */
+       struct list_head	entry;	/* List pointers */
+		 atomic_t refcnt; /* usage counter */
+       int		(*init)(struct task_struct *, struct pagg *);
+       int		(*attach)(struct task_struct *, struct pagg *, void*);
+       int		(*detach)(struct task_struct *, struct pagg *);
+       void		(*exec)(struct task_struct *, struct pagg *);
+};
+
+
+/* Kernel service functions for providing PAGG support */
+extern struct pagg *pagg_get(struct task_struct *task, char *key);
+extern struct pagg *pagg_alloc(struct task_struct *task, 
+			       struct pagg_hook *pt);
+extern void pagg_free(struct pagg *pagg);
+extern int pagg_hook_register(struct pagg_hook *pt_new);
+extern int pagg_hook_unregister(struct pagg_hook *pt_old);
+extern int __pagg_attach(struct task_struct *to_task, 
+			 struct task_struct *from_task);
+extern int __pagg_detach(struct task_struct *task);
+extern int __pagg_exec(struct task_struct *task);
+
+/* function used when a child process must inherit attachment to pagg
+ * containers from the parent.
+ */
+static inline int pagg_attach(struct task_struct *child, 
+			      struct task_struct *parent)
+{
+	INIT_PAGG_LIST(child);
+	if (!list_empty(&parent->pagg_list))
+		return __pagg_attach(child, parent);
+	return 0;
+}
+
+
+/* 
+ * Function used when a process must detach from pagg containers to which it
+ * is currenlty a member.
+ *
+ */
+static inline void pagg_detach(struct task_struct *task)
+{
+	if (!list_empty(&task->pagg_list))
+		__pagg_detach(task);
+}
+
+/* 
+ * function used when a process exec's.
+ *
+ */
+static inline void pagg_exec(struct task_struct *task)
+{
+	if (!list_empty(&task->pagg_list))
+		__pagg_exec(task);
+}
+
+/*
+ * Marco Used in INIT_TASK to set the head and sem of pagg_list.
+ * If CONFIG_PAGG is off, it is defined as an empty macro below.
+ */
+#define INIT_TASK_PAGG(tsk) \
+	.pagg_list = LIST_HEAD_INIT(tsk.pagg_list),     \
+	.pagg_sem  = __RWSEM_INITIALIZER(tsk.pagg_sem)  
+
+#else  /* CONFIG_PAGG */
+
+/* 
+ * Replacement macros used when PAGG (Process Aggregates) support is not
+ * compiled into the kernel.
+ */
+#define INIT_TASK_PAGG(tsk)
+#define INIT_PAGG_LIST(l) do { } while(0)
+#define pagg_attach(ct, pt)  do { } while(0)
+#define pagg_detach(t)  do {  } while(0)     
+#define pagg_exec(t)  do {  } while(0)     
+
+#endif /* CONFIG_PAGG */
+
+#endif /* _LINUX_PAGG_H */
diff -Naru 2.6-patch/include/linux/sched.h 2.6pagg-patch/include/linux/sched.h
--- 2.6-patch/include/linux/sched.h	2004-05-10 12:06:11.000000000 -0500
+++ 2.6pagg-patch/include/linux/sched.h	2004-05-26 08:31:24.000000000 -0500
@@ -499,11 +499,16 @@
 
 	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.  */
+
+#ifdef CONFIG_PAGG
+/* List of pagg (process aggregate) attachments */
+	struct list_head pagg_list;
+	struct rw_semaphore pagg_sem;
+#endif
 };
 
 static inline pid_t process_group(struct task_struct *tsk)
diff -Naru 2.6-patch/init/Kconfig 2.6pagg-patch/init/Kconfig
--- 2.6-patch/init/Kconfig	2004-05-10 12:06:11.000000000 -0500
+++ 2.6pagg-patch/init/Kconfig	2004-05-20 14:46:49.000000000 -0500
@@ -121,6 +121,14 @@
 	  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.  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 SYSCTL
 	bool "Sysctl support"
 	---help---
diff -Naru 2.6-patch/kernel/exit.c 2.6pagg-patch/kernel/exit.c
--- 2.6-patch/kernel/exit.c	2004-05-10 12:06:11.000000000 -0500
+++ 2.6pagg-patch/kernel/exit.c	2004-05-20 14:46:49.000000000 -0500
@@ -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>
@@ -799,6 +799,9 @@
 		module_put(tsk->binfmt->module);
 
 	tsk->exit_code = code;
+
+	pagg_detach(tsk);
+
 	exit_notify(tsk);
 	schedule();
 	BUG();
diff -Naru 2.6-patch/kernel/fork.c 2.6pagg-patch/kernel/fork.c
--- 2.6-patch/kernel/fork.c	2004-05-10 12:06:11.000000000 -0500
+++ 2.6pagg-patch/kernel/fork.c	2004-05-21 10:13:53.000000000 -0500
@@ -33,7 +33,7 @@
 #include <linux/ptrace.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
-
+#include <linux/pagg.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
@@ -236,6 +236,9 @@
 
 	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);
 }
 
 static struct task_struct *dup_task_struct(struct task_struct *orig)
@@ -995,6 +998,12 @@
 	   
 	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.
+	 */
+	pagg_attach(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 -Naru 2.6-patch/kernel/Makefile 2.6pagg-patch/kernel/Makefile
--- 2.6-patch/kernel/Makefile	2004-05-10 12:06:11.000000000 -0500
+++ 2.6pagg-patch/kernel/Makefile	2004-05-20 14:46:49.000000000 -0500
@@ -18,6 +18,7 @@
 obj-$(CONFIG_PM) += power/
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_COMPAT) += compat.o
+obj-$(CONFIG_PAGG) += pagg.o
 obj-$(CONFIG_IKCONFIG) += configs.o
 obj-$(CONFIG_IKCONFIG_PROC) += configs.o
 obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
diff -Naru 2.6-patch/kernel/pagg.c 2.6pagg-patch/kernel/pagg.c
--- 2.6-patch/kernel/pagg.c	1969-12-31 18:00:00.000000000 -0600
+++ 2.6pagg-patch/kernel/pagg.c	2004-05-25 15:35:29.000000000 -0500
@@ -0,0 +1,385 @@
+/* 
+ * PAGG (Process Aggregates) interface
+ *
+ * 
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Contact information:  Silicon Graphics, Inc., 1500 Crittenden Lane,
+ * 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>
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/pagg.h>
+#include <asm/semaphore.h>
+
+/* list of pagg hook entries that reference the "module" implementations */
+static LIST_HEAD(pagg_hook_list);
+static DECLARE_RWSEM(pagg_hook_list_sem);
+
+
+/* 
+ * pagg_get
+ *
+ * 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 down_read(&task->pagg_list.sem).
+ */
+struct pagg *
+pagg_get(struct task_struct *task, char *key)
+{
+	struct pagg *pagg;
+
+	list_for_each_entry(pagg, &task->pagg_list, entry) {
+		if (!strcmp(pagg->hook->name,key))
+			return pagg;
+	}
+	return NULL;
+}
+
+
+/*
+ * pagg_alloc
+ *
+ * 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_sem.  This can be locked using 
+ * down_write(&task->pagg_sem)
+ */
+struct pagg *
+pagg_alloc(struct task_struct *task, struct pagg_hook *pagg_hook)
+{
+	struct pagg *pagg;
+
+	pagg = kmalloc(sizeof(struct pagg), GFP_KERNEL);
+	if (!pagg)
+		return NULL;
+
+	pagg->hook = pagg_hook;
+	pagg->data = NULL;
+	atomic_inc(&pagg_hook->refcnt);  /* Increase hook's reference count */
+	list_add_tail(&pagg->entry, &task->pagg_list);
+	return pagg;
+}
+
+
+/*
+ * pagg_free
+ *
+ * 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_sem
+ * for the task. This can be locked using down_write(&task->pagg_sem).
+ *
+ * Prior to calling pagg_free, the pagg should have been detached from the
+ * pagg container represented by this pagg.  That is usually done using
+ * p->hook->detach(task, pagg);
+ */
+void
+pagg_free(struct pagg *pagg) 
+{
+	atomic_dec(&pagg->hook->refcnt); /* decr the reference count on the hook */
+	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 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 pagg_hook *pagg_hook;
+
+	list_for_each_entry(pagg_hook, &pagg_hook_list, entry) {
+		if (!strcmp(pagg_hook->name, key)) {
+			return pagg_hook;
+		}
+	}
+	return NULL;
+}
+
+
+/*
+ * pagg_hook_register
+ *
+ * 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
+pagg_hook_register(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);
+	/* set the ref count to zero */
+	atomic_set(&pagg_hook_new->refcnt, 0);
+	/* printk("DEBUG - pagg hook register - refcnt now: %d\n", 
+		     atomic_read(&pagg_hook_new->refcnt)); */
+	up_write(&pagg_hook_list_sem);
+
+	printk(KERN_INFO "Registering PAGG support for (name=%s)\n",
+			pagg_hook_new->name);
+
+	return 0;					/* success */
+
+}
+
+
+/*
+ * pagg_hook_unregister
+ *
+ * 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, we check if 
+ * the pagg hook is still in use.  
+ */
+int
+pagg_hook_unregister(struct pagg_hook *pagg_hook_old)
+{
+	struct pagg_hook *pagg_hook;
+
+	/* 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);
+
+	/* printk("DEBUG - pagg hook unregister - refcnt now: %d\n", 
+	 *	     atomic_read(&pagg_hook->refcnt));
+	 */
+
+	if (pagg_hook && pagg_hook == pagg_hook_old) {
+		/* Is the pagg hook busy?  Check if the refcnt is zero */
+		if (atomic_read(&pagg_hook->refcnt) != 0) {
+			up_write(&pagg_hook_list_sem);
+			printk(KERN_INFO "Failed attempt to unregister a PAGG hook from: %s\n", pagg_hook_old->name);
+			return -EBUSY;
+		}
+		list_del_init(&pagg_hook->entry);
+		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 */
+}
+
+
+/*
+ * __pagg_attach
+ *
+ * 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 __pagg_attach(struct task_struct *to_task, struct task_struct *from_task)
+{
+	int  		   retcode = 0;
+	struct pagg *from_pagg;
+
+	/* lock the parents pagg_list we are copying from */
+	down_read(&from_task->pagg_sem); /* read lock the pagg list */
+
+	list_for_each_entry(from_pagg, &from_task->pagg_list, entry) {
+		struct pagg *to_pagg = NULL;
+
+		to_pagg = pagg_alloc(to_task, from_pagg->hook);
+		if (!to_pagg) {
+			retcode = -ENOMEM;
+			goto error_return;
+		}
+		retcode = to_pagg->hook->attach(to_task, to_pagg, from_pagg->data);
+		if (retcode != 0) {
+			/* attach should issue error message */
+			goto error_return;
+		}
+	}
+
+	up_read(&from_task->pagg_sem); /* unlock the pagg list */
+
+	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.
+	 */
+	up_read(&from_task->pagg_sem); /* unlock the pagg list */
+	__pagg_detach(to_task);
+	return retcode;				/* failure */
+}
+
+/*
+ * __pagg_detach
+ *
+ * Used to detach a task from all pagg containers to which it is attached.
+ * 
+ * list_for_each used here because we need to reset the list after a 
+ * pagg is detached.
+ */
+int
+__pagg_detach(struct task_struct *task)
+{
+	struct pagg *pagg;
+	struct pagg *paggtmp;
+	int retcode = 0;
+	int rettmp = 0;
+
+	/* Remove ref. to paggs from task immediately */
+	down_write(&task->pagg_sem); /* write lock pagg list */
+
+	list_for_each_entry_safe(pagg, paggtmp, &task->pagg_list, entry) {
+		rettmp = pagg->hook->detach(task, pagg);
+		if (rettmp) {
+			/* an error message should be logged in free_pagg */
+			retcode = rettmp;
+		}
+		pagg_free(pagg);
+	}
+                                                                                
+	up_write(&task->pagg_sem); /* write unlock the pagg list */
+                                                                                
+	return retcode;   /* 0 = success, else return last code for failure */
+}
+
+
+/*
+ * __pagg_exec
+ *
+ * Used to when a process that is in a pagg container does an exec.
+ *
+ * The "from" argument is the task.  The "name" argument is the name
+ * of the process being exec'ed.
+ *
+ */
+int __pagg_exec(struct task_struct *task) 
+{
+	struct pagg	*pagg;
+
+	/* lock the parents pagg_list we are copying from */
+	down_read(&task->pagg_sem); /* lock the pagg list */
+
+	list_for_each_entry(pagg, &task->pagg_list, entry) {
+		if (pagg->hook->exec) /* conditional because it's optional */
+			pagg->hook->exec(task, pagg);
+	}
+
+	up_read(&task->pagg_sem); /* unlock the pagg list */
+	return 0;
+}
+
+
+EXPORT_SYMBOL(pagg_get);
+EXPORT_SYMBOL(pagg_alloc);
+EXPORT_SYMBOL(pagg_free);
+EXPORT_SYMBOL(pagg_hook_register);
+EXPORT_SYMBOL(pagg_hook_unregister);

Copyright © 2004, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds