Enhanced Linux System Accounting
Subsystems (4)
PATCH 1/1 Enhanced Linux System Accounting
Hello,
This is a new patch "patch-2.6.6-elsa" for 2.6.6 kernel. It can be downloaded from:
http://sourceforge.net/project/showfiles.php?group_id=105806
I also attach it at the end of this file. In this patch we rewrite
functions and we made a clean separation between different parts of ELSA (thank you very much to Chris Wright for its usefull comments). This patch allows you to test BSD-like accounting for a group of process. Informations are dumped in /var/log/bank (need to change that). "BANK" can be manipulated via ioctl() and /dev/elsacct. A C program, called "elsa_cmd.c", that allows to add/remove process to/from a bank can be downloaded from the CVS:
http://cvs.sourceforge.net/viewcvs.py/elsa/tests/
We need to improve lock between the main structure called "elsa_broot"
and add new accounting informations. Our goal is to provide an environment that can support System V accounting and a unify interface from existing accounting tools (like sar) to be used by existing Linux administrative tools (like WebMin).
Any feedbacks, any comments are welcome
Best,
Guillaume
drivers/elsacct/bank.c +607 -0
@@ -0,0 +1,607 @@ +/* +* driver/elsacct/bank.c +* +* ELSA - Enhanced Linux System Accounting +* Guillaume Thouvenin - 26/04/2004 +* +* This file implements Enhanced Linux System Accounting. It +* provides structure and functions to manipulate "BANK". +* +* +* This code is licenced under GPL. +*/ + +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/seq_file.h> +#include <linux/module.h> + +#include <linux/bank.h> + +#include <asm/semaphore.h> +#include <asm/bug.h> + +/********************************************* +* structures and macros used to manage banks * +*********************************************/ + +#ifdef CONFIG_BANK_DEBUG +#define dprintk(format...) printk(format) +#else +#define dprintk (format) +#endif + +/* It's the head on the list of banks */ +static struct bank_root elsa_broot =BANK_ROOT_INIT (elsa_broot); + +/* semaphore that protect access to bank id */ +static DECLARE_MUTEX (elsa_bid_sem); +//SPIN_LOCK_UNLOCKED(elsalist_lock); + +/******************************************************************** +* Following functions can be use by a module (=> they are exported) * +********************************************************************/ + +/** +* elsa_bank_alloc - Allocates a new bank +* @fn : callback used when a data or a bank are destroyed +* @info : generic information attached to a bank (used for accounting) +* +* Allocates a new bank and returns the new created bank. +* If an error is encountered NULL is returned. +* +* Here are different steps of the operation +* +* 1) Allocate space for the new bank +* 2) Give it an identifier +* 3) Initialize the head of the list of items (items will point to process) +* 4) Add it to the list of available bank +*/ +struct elsa_bank +*elsa_bank_alloc (int (*fn) (int ,struct elsa_bank *,struct elsa_data *), +void *info) +{ +struct elsa_bank *b; +int bid; + +/* allocate space for the new bank */ + b = (struct elsa_bank *)kmalloc (sizeof (struct elsa_bank ),GFP_KERNEL ); +if (!b) { +printk ("elsa_bank_alloc: cannot allocate space\n" ); +return NULL ; + } + +/* +* give it an id +* Currently we only used next_bid field but this mechnism +* can give problems if we use many banks and the bid go through +* its maximum +*/ +down (&elsa_bid_sem); + bid = elsa_broot.next_bid++; +up (&elsa_bid_sem); + +if (bid <=0 ) { +/* There is no available id == ERROR */ +kfree (b); +printk ("elsa_bank_alloc: can not find bank identifier\n" ); +return NULL ; + }else { + b->bid = bid; + } + +/* Initialize fields */ + b->info = info; + b->callback = fn; +INIT_LIST_HEAD (&(b->data_head)); + +/* add the new bank to the list of banks --- NEED LOCK */ +list_add (&(b->bank_list), &(elsa_broot.bank_head)); + +dprintk ("elsa_bank_alloc: bank #%d created\n" , b->bid); + +return b; +} + +/** +* elsa_data_alloc - Allocates a new data +* +* Allocates a new data and returns the new created item. +* If an error is encountered NULL is returned. +* +* Here are different steps of the operation +* +* 1) Allocate space for the new data +* 2) Initialize different fields +*/ +struct elsa_data *elsa_data_alloc (void ) +{ +struct elsa_data *d; + +/* allocate space for the new data */ + d = (struct elsa_data *)kmalloc (sizeof (struct elsa_data ),GFP_KERNEL ); +if (!d) { +printk ("elsa_data_alloc: cannot allocate space\n" ); +return NULL ; + } + +/* Initialize fields */ + d->bid =0 ; + d->process =NULL ; +INIT_LIST_HEAD (&(d->data_list)); +INIT_LIST_HEAD (&(d->bank_list)); + +return d; +} + +/** +* elsa_bank_free - Frees space occupied by a bank. +* @bank: a pointer to the bank to delete +* +* Removes a bank and returns the identifiers of the removed bank. +* When this function is called bank *MUST* be empty. +* +* Here are different steps of the operation +* +* 1) Write accounting information +* 2) Remove it from the list of banks +* 3) Free space used by bank +*/ +void elsa_bank_free (struct elsa_bank *bank) +{ +BUG_ON (bank ==NULL ); +BUG_ON (!list_empty (&bank->data_head)); + +/* Before deleting the bank, we can do some action */ +/* callback */ +if (bank->callback) { + (bank->callback) (ELSA_BANK_CALLBACK , bank,NULL ); + } + +/* remove bank from the bank's list */ +list_del (&(bank->bank_list)); +dprintk ("elsa_bank_free: bank #%d removed from the list\n" , bank->bid); +kfree (bank); +} + +/** +* elsa_data_free - Free data +* @data: data to be removed +* +* Frees memory space used by data. +*/ +void elsa_data_free (struct elsa_data *data) +{ +struct elsa_bank *b =NULL ; + +BUG_ON (data ==NULL ); + +/* +* if data->bid is equal to 0, it means that an error occured between +* data's allocation and data's addition in a bank. So, it isn't inserted +* in any list and there is no need to call the "callback" +*/ +if (data->bid !=0 ) { +/* callback, we are in the child so callback code */ + b =elsa_get_bank (data->bid); +if (b && b->callback) { + (b->callback) (ELSA_DATA_CALLBACK , b, data); + } + +list_del (&data->data_list); + +/* Need access to head_list in task_struct */ +write_lock_irq (&tasklist_lock); +list_del (&data->bank_list); +write_unlock_irq (&tasklist_lock); + +dprintk + ("elsa_data_free: process #%d removed from the bank#%d\n" , + data->process->pid, b->bid); + } + +kfree (data); +} + +/** +* elsa_bank_add - Add a process to a given bank +* @b : pointer to a bank +* @p : pointer to a process +* +* add a data to a bank by setting lists. Other fields are unchanged +* +* If an error is encountered, a negative value is returned. +* It can not return 0. +* +* Here are steps to perform +* +* 1) Check arguments +* 2) Check if process is already in the bank +* 3) Initialize its fields +* 4) Return BID +*/ +int elsa_bank_add (struct elsa_bank *b,struct elsa_data *d) +{ +if (!b || !d || !(d->process)) { +dprintk ("elsa_bank_add: Wrong parameters\n" ); +return -EINVAL ; + } + +/* Bank ID cannot be equal to 0 */ +BUG_ON (b->bid ==0 ); + +/* check if process is already present */ +if (elsa_process_present (d->process, b)) +/* Process already in the bank, so we can not add it */ +return -EPERM ; + +/* Set BID */ + d->bid = b->bid; + +/* Add data in the list of bank's data */ +list_add (&(d->data_list), &(b->data_head)); + +/* Add data in the list of process's bank */ +write_lock_irq (&tasklist_lock); +list_add (&(d->bank_list), &(d->process->bank_head)); +write_unlock_irq (&tasklist_lock); + +dprintk ("elsa_bank_add: Add process #%d to bank #%d\n" , d->process->pid, + b->bid); + +return b->bid; +} + +/** +* elsa_bank_remove - Remove a process to a given bank +* @b: pointer to the bank +* @d: pointer to the process +* +* Removes a given process from a given bank. +* Return 0 if succedeed, negative value otherwise +*/ +int elsa_bank_remove (struct elsa_data *d) +{ +int b_empty =0 ; +struct elsa_bank *b =NULL ;/* Used if d is the last +item of the bank */ + +if (!d) +return -EINVAL ; + +/* bank will be empty if d is the last item in the bank */ +if (d->data_list.next == d->data_list.prev) { + b_empty =1 ; + b =elsa_get_bank (d->bid); + } + +/* kfree memory */ +elsa_data_free (d); + +/* +* We must free bank after data otherwise we will miss +* accounting informations +*/ +if (b_empty) +elsa_bank_free (b); + +return 0 ; +} + +/** +* elsa_bank_clean - Removes all datas in a given bank +* @b: pointer to the bank to be removed +* +* Removes data found in a bank given as paramater. It go through the +* list, removes link and free space occupied by the data. +* Return 0 if succedeed, negative value otherwise +*/ +int elsa_bank_clean (struct elsa_bank *b) +{ +if (!b) { +/* bank doesn't exist */ +dprintk ("elsa_bank_clean: bank doesn't exist\n" ); +return -EAGAIN ; + } + +/* release all datas and remove the bank */ +while (!list_empty (&(b->data_head))) { +/* get pointer to the first element in the list */ +struct elsa_data *d =list_entry (b->data_head.next, + struct elsa_data, data_list); +elsa_data_free (d); + } + +elsa_bank_free (b); + +return 0 ; +} + +/** +* elsa_process_remove - Remove a process from all banks that it belongs +* @p: pointer to the process to be removed +* +* Removes process from all banks +* Return 0 if succedeed, negative value otherwise +*/ +int elsa_process_remove (struct task_struct *p) +{ + +if (!p) { +/* bank doesn't exist */ +dprintk ("elsa_process_remove: process doesn't exist\n" ); +return -EAGAIN ; + } + +/* release all datas and remove the bank */ +while (!list_empty (&(p->bank_head))) { +/* get pointer to the first element in the list */ +struct elsa_data *d =list_entry (p->bank_head.next, + struct elsa_data, bank_list); +elsa_bank_remove (d); + } + +return 0 ; +} + +/** +* elsa_get_bank - Returns a pointer to a bank +* @bid: The identifier of a bank +* +* Finds the bank with given ID in the list of banks +*/ +struct elsa_bank *elsa_get_bank (unsigned int bid) +{ +struct list_head *entry; + +list_for_each (entry, &(elsa_broot.bank_head)) { +struct elsa_bank *b = + list_entry(entry, struct elsa_bank, bank_list); +if (b->bid == bid) +return b; + } + +return NULL ; +} + +/** +* elsa_get_data - Returns a pointer to the container of a +* given pid in a given bank. +* @pid: The identifier of the process +* +* Find the data that holds process #PID in a given bank. If +* not found, NULL is returned. +*/ +struct elsa_data *elsa_get_data (unsigned int pid,unsigned int bid) +{ +struct list_head *entry; +struct task_struct *p; + +read_lock (&tasklist_lock); + p =find_task_by_pid (pid); +read_unlock (&tasklist_lock); + +list_for_each (entry, &(p->bank_head)) { +struct elsa_data *d = + list_entry(entry, struct elsa_data, bank_list); +if (d->bid == bid) +/* Got it */ +return d; + } + +return NULL ; +} + +/** +* elsa_process_present - Returns 1 if process is in the bank +* @p: the process identifier to look for +* @b: the bank in which we must look. +* +* Return 1 if process is present in a bank, 0 otherwise +*/ +int elsa_process_present (struct task_struct *p,struct elsa_bank *b) +{ +struct list_head *entry; + +list_for_each (entry, &(b->data_head)) { +struct elsa_data *d = + list_entry(entry, struct elsa_data, data_list); +if (d->process == p) { +/* Found it */ +return 1 ; + } + } + +/* Process not found in b */ +return 0 ; +} + +/* memory management */ +EXPORT_SYMBOL (elsa_bank_alloc); +EXPORT_SYMBOL (elsa_data_alloc); +EXPORT_SYMBOL (elsa_bank_free); +EXPORT_SYMBOL (elsa_data_free); + +/* data manipulation */ +EXPORT_SYMBOL (elsa_bank_add); +EXPORT_SYMBOL (elsa_bank_remove); +EXPORT_SYMBOL (elsa_bank_clean); +EXPORT_SYMBOL (elsa_process_remove); + +/* get informations */ +EXPORT_SYMBOL (elsa_get_bank); +EXPORT_SYMBOL (elsa_get_data); +EXPORT_SYMBOL (elsa_process_present); + +/***************************************************************************** +* Following functions are called from kernel function. * +* elsa_copy_parent_bank() is used when child is created (kernel/fork.c) * +*****************************************************************************/ + +/** +* elsa_copy_parent_bank - Add a given process to the same banks +* as another one +* @from: Process from where we will copy information +* @to: Process to where we will copy information +* +* Goes through the banks to which "from" process belong and add +* the process in those banks. +* +* It is used when doing a fork (kernel/fork.c - copy_process()). +* This function is used by the kernel to update child's banks. +*/ +void elsa_copy_parent_bank (struct task_struct *from,struct task_struct *to) +{ +struct list_head *entry; + +/* +* First, initialize to->bank_head otherwise, if +* from->bank_head is NULL it won't be initialize +*/ +write_lock_irq (&tasklist_lock); +INIT_LIST_HEAD (&(to->bank_head)); +write_unlock_irq (&tasklist_lock); + +list_for_each (entry, &(from->bank_head)) { +struct elsa_bank *b; +struct elsa_data *d_from; +struct elsa_data *d_to; + +dprintk ("elsa_copy_parent_bank: from pid#%d to pid#%d\n" , + from->pid, to->pid); + + d_from = list_entry(entry, struct elsa_data, bank_list); +if (!d_from) +return ; + +/* allocat space to new data */ + d_to =elsa_data_alloc (); +if (!d_to) { +printk ("elsa_copy_parent_bank: Cannot create data\n" ); +return ; + } + + d_to->bid = d_from->bid; + d_to->process = to; + +/* d_to->process is set so we can call elsa_bank_add */ + b =elsa_get_bank (d_from->bid); +if (!b) { +elsa_data_free (d_to); +printk + ("elsa_copy_parent_bank: Destination not found\n" ); +return ; + } + +if (b->bid !=elsa_bank_add (b, d_to)) { +elsa_data_free (d_to); +printk ("elsa_copy_parent_bank: Copy error\n" ); +return ; + } + } +} + +/********************************* +* functions used to manage /proc * +* * +* The entry is /proc/bankinfo * +*********************************/ + +/*** +* Add an entry in /proc to get informations concerning +* banks. This entry is called /proc/bankinfo +****/ +#ifdef CONFIG_PROC_FS +static void *b_start (struct seq_file *m,loff_t * pos) +{ +loff_t n = *pos; +struct list_head *p; + +/* Header is displaying just during the first called */ +if (!n) { +seq_puts (m,"# - bankinfo -\n" ); +seq_puts (m,"# bankid:\t<process> <process> ...\n" ); + } + +if (list_empty (&elsa_broot.bank_head)) +return NULL ; + + p = elsa_broot.bank_head.next; +while (n--) { + p = p->next; +if (p == &elsa_broot.bank_head) +return NULL ; + } + +/* we return a pointer to the data in the bank */ + return list_entry(p, struct elsa_bank, bank_list); +} + +static void b_stop (struct seq_file *m,void *v) +{ +} + +static void *b_next (struct seq_file *m,void *v,loff_t * pos) +{ +struct elsa_bank *bank = v; + + ++*pos; +return bank->bank_list.next == &elsa_broot.bank_head ?NULL : + list_entry(bank->bank_list.next, struct elsa_bank, bank_list); +} + +static int show_bankinfo (struct seq_file *m,void *v) +{ +struct elsa_bank *bank = v; +struct elsa_data *data; + +if (!bank) { +seq_printf (m,"There is no banks\n" ); + }else { +/* display bank identifier */ +seq_printf (m,"%d:" , bank->bid); +/* add a tabulation */ +seq_printf (m,"\t" ); +/* display list of processus */ +if (list_empty (&(bank->data_head))) { +seq_printf (m,"Empty" ); + }else { +list_for_each_entry (data, &(bank->data_head), data_list) +seq_printf (m,"%d " , data->process->pid); + } +/* add EOL */ +seq_printf (m,"\n" ); + } + +return 0 ; +} + +/* bankinfo_op - iterator that generates /proc/bankinfo +* +* Output layout: +* bankID pid ... +*/ +struct seq_operations bankinfo_op = { + .start = b_start, + .stop = b_stop, + .next = b_next, + .show = show_bankinfo, +}; + +int bankinfo_open (struct inode *inode,struct file *file) +{ +return seq_open (file, &bankinfo_op); +} + +struct file_operations proc_bankinfo_ops = { + .open = bankinfo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +EXPORT_SYMBOL (proc_bankinfo_ops); + +#endif
drivers/elsacct/elsacct.c +438 -0
438 lines not on this page · raw message
drivers/elsacct/Kconfig +61 -0
61 lines not on this page · raw message
drivers/elsacct/Makefile +5 -0
@@ -0,0 +1,5 @@ +# +# Makefile for the Enhanced Linux System Accounting +# +obj-$(CONFIG_BANK) += bank.o +obj-$(CONFIG_ELSACCT) += elsacct.o
drivers/Kconfig +2 -0
@@ -4,6 +4,8 @@ menu "Device Drivers" source "drivers/base/Kconfig" +source "drivers/elsacct/Kconfig" + source "drivers/mtd/Kconfig" source "drivers/parport/Kconfig"
drivers/Makefile +1 -0
@@ -50,3 +50,4 @@ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-y += firmware/ +obj-y += elsacct/
include/linux/bank.h +164 -0
164 lines not on this page · raw message
include/linux/elsacct.h +55 -0
55 lines not on this page · raw message
include/linux/init_task.h +1 -0
@@ -112,6 +112,7 @@ extern struct group_info init_groups; .proc_lock = SPIN_LOCK_UNLOCKED, \ .switch_lock = SPIN_LOCK_UNLOCKED, \ .journal_info = NULL, \ + .bank_head = LIST_HEAD_INIT(tsk.bank_head), \ }
include/linux/sched.h +3 -0
@@ -504,6 +504,9 @@ struct task_struct {unsigned long ptrace_message;siginfo_t *last_siginfo;/* For ptrace use. */ + +/* List of BANK to which the process belong -- Used by ELSA */ +struct list_head bank_head; };static inline pid_t process_group (struct task_struct *tsk)
kernel/exit.c +1 -0
@@ -59,6 +59,7 @@ repeat:atomic_dec (&p->user->processes);spin_lock (&p->proc_lock); proc_dentry =proc_pid_unhash (p); +elsa_process_remove (p);write_lock_irq (&tasklist_lock);if (unlikely (p->ptrace))__ptrace_unlink (p);
kernel/fork.c +13 -0
@@ -1011,6 +1011,19 @@ struct task_struct *copy_process(unsigneINIT_LIST_HEAD (&p->ptrace_children);INIT_LIST_HEAD (&p->ptrace_list); +/* +* Child is in the same BANK as parent. So we copy the list of banks +* from current to p. We can not just counting reference on the structure +* because the may evoluate in differents way. I think it's easier to +* do like this but maybe I'm wrong... -- guillaume +* +* We put that call outside lock_irq because elsa_copy_parent_bank +* uses kmalloc(GFP_KERNEL). Therefore, as we modify task_struct, +* we manage lock_irq ourselves. -- guillaume +* +*/ +elsa_copy_parent_bank (current, p); +/* Need tasklist lock for parent etc handling! */ write_lock_irq (&tasklist_lock);/*
Makefile +1 -1
@@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 6 -EXTRAVERSION = +EXTRAVERSION = -elsa NAME=Zonked Quokka # *DOCUMENTATION*