Hi,
Following is a rather simple module implementation that adds an interface for protecting against the oom_killer by setting the oomkilladj in the task struct.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/capability.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/swap.h>
#include <linux/sysrq.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#define OOM_KILL_CTL_VERSION "1.0"
#define OOM_KILL_CTL_DESC "OOM kill protection interface"
static struct proc_dir_entry *proc_oom_kill = NULL;
static struct proc_dir_entry *proc_oom_kill_ctl = NULL;
static struct proc_dir_entry *proc_oom_kill_usage = NULL;
static struct proc_dir_entry *proc_oom_kill_status = NULL;
static int get_task_oom_protection(struct task_struct *p)
{
return (p->oomkilladj == OOM_DISABLE);
}
/*
* Protecting a process does not protect all existing children - only future children
*/
static void update_process_oom_protection(struct task_struct *tsk, int protect)
{
struct mm_struct *mm = NULL;
mm = tsk->mm;
if (!mm) {
return;
}
if (protect)
tsk->oomkilladj = OOM_DISABLE;
else
tsk->oomkilladj = 0;
}
static int set_oom_protection_state(int pid, int protect)
{
struct task_struct *p;
int rc = 1;
read_lock(&tasklist_lock);
p = find_task_by_pid(pid);
if (p) {
update_process_oom_protection(p, protect);
rc = 0;
}
read_unlock(&tasklist_lock);
return rc;
}
static int set_task_oom_protection(struct task_struct *p, int protect)
{
read_lock(&tasklist_lock);
update_process_oom_protection(p, protect);
read_unlock(&tasklist_lock);
return 0;
}
void unprotect_all(void)
{
struct task_struct *p, *g;
read_lock(&tasklist_lock);
do_each_thread(g, p) {
update_process_oom_protection(p, 0);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
}
int print_protected(char *buffer, int len)
{
struct task_struct *g, *p;
int pos = 0, protected;
pos = snprintf(buffer, len,
"%-10s %-16s\n"
"%-10s %-16s\n",
"PID", "NAME",
"---", "----");
read_lock(&tasklist_lock);
do_each_thread(g, p)
if (p->pid) {
protected = get_task_oom_protection(p);
if (protected) {
pos += snprintf(buffer + pos, len - pos, "%-10d %-16s\n",
p->pid,
p->comm);
}
}
while_each_thread(g, p);
read_unlock(&tasklist_lock);
return pos;
}
static int oom_kill_ctl(struct file * file, const char * buf,
unsigned long length, void *data)
{
int pid, err, protect;
char *buffer, *p;
if (!buf || length > PAGE_SIZE)
return -EINVAL;
buffer = (char *)__get_free_page(GFP_KERNEL);
if (!buffer)
return -ENOMEM;
err = -EFAULT;
if (copy_from_user(buffer, buf, length))
goto out;
err = -EINVAL;
if (length < PAGE_SIZE)
buffer[length] = '\0';
else if (buffer[PAGE_SIZE-1])
goto out;
if (!strncmp("setprotection", buffer, 13)) {
p = buffer + 14;
pid = simple_strtoul(p, &p, 0);
if (!pid) {
printk (KERN_WARNING "OOMKill: no pid was supplied\n");
} else {
protect = simple_strtoul(p + 1, &p, 0);
set_oom_protection_state(pid, protect);
}
err = length;
}
out:
free_page((unsigned long)buffer);
return err;
}
static int oom_kill_usage(char *buffer, char **start, off_t offset, int length)
{
char *temp;
temp = buffer;
temp += snprintf(temp, length, "Usage:\n"
"cat /proc/oom_kill_protection/stat\n"
"echo \"setprotection <pid> [0/1]\" > /proc/oom_kill_protection/ctl\n");
return temp-buffer;
}
static int oom_kill_proc_info(char *buffer, char **start, off_t offset, int length)
{
int size, len = 0;
off_t begin = 0;
off_t pos = 0;
size = snprintf(buffer, length, "OOM protected processes:\n");
len += size;
pos = begin + len;
size = print_protected(buffer + len, length - len);
len += size;
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
*start = buffer + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Start slop */
if (len > length)
len = length;
return len;
}
static void remove_proc_entries(void)
{
if (proc_oom_kill_ctl) {
remove_proc_entry("ctl", proc_oom_kill);
proc_oom_kill_ctl = NULL;
}
if (proc_oom_kill_status) {
remove_proc_entry("status", proc_oom_kill);
proc_oom_kill_status = NULL;
}
if (proc_oom_kill_usage) {
remove_proc_entry("usage", proc_oom_kill);
proc_oom_kill_usage = NULL;
}
if (proc_oom_kill) {
remove_proc_entry("oom_kill_protection", 0);
proc_oom_kill = NULL;
}
}
static int oom_protect_major = 0;
#define OOM_SET_PROTECT_MAGIC 'O'
#define OOM_GET_PROTECT_MAGIC 'o'
#define NAME "oom_protect"
#define OOMSETPROTECTCMD _IOWR(OOM_SET_PROTECT_MAGIC, 1, int)
#define OOMGETPROTECTCMD _IOR(OOM_GET_PROTECT_MAGIC, 2, int)
static int oom_kill_ioctl(struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case OOMSETPROTECTCMD: {
int mode;
printk(KERN_DEBUG "Received OOMSETPROTECTCMD from task %d\n", current->pid);
if (copy_from_user(&mode, (void *) arg, sizeof(int)))
return -EFAULT;
printk(KERN_DEBUG "[%d] Protection mode was set to %d\n", current->pid, mode);
if (set_task_oom_protection(current, mode))
return -EINVAL;
break;
}
case OOMGETPROTECTCMD: {
int mode;
printk(KERN_DEBUG "Received OOMGETPROTECTCMD from task %d\n", current->pid);
mode = get_task_oom_protection(current);
copy_to_user((void *) arg, &mode, sizeof(int));
break;
}
default:
return -EINVAL;
}
return 0;
}
static struct file_operations oom_kill_fops =
{
.owner = THIS_MODULE,
.open = nonseekable_open,
.ioctl = oom_kill_ioctl,
};
static void register_device(void)
{
int r;
r = register_chrdev(oom_protect_major, NAME, &oom_kill_fops);
if (r < 0) {
printk(KERN_ERR NAME ": unable to register character device\n");
return;
}
if (!oom_protect_major) {
oom_protect_major = r;
printk(KERN_DEBUG NAME ": got dynamic major %d\n", oom_protect_major);
}
return;
}
static void unregister_device(void)
{
unregister_chrdev(oom_protect_major, NAME);
}
static int __init oom_kill_ctl_init(void)
{
proc_oom_kill = proc_mkdir("oom_kill_protection", 0);
if (!proc_oom_kill) {
printk (KERN_ERR "cannot init /proc/oom_kill_protection\n");
remove_proc_entries();
return -ENOMEM;
}
proc_oom_kill_ctl = create_proc_entry ("ctl", S_IFREG|S_IRUGO|S_IWUSR, proc_oom_kill);
if (!proc_oom_kill_ctl) {
printk (KERN_ERR "cannot init /proc/oom_kill_protection/ctl\n");
remove_proc_entries();
return -ENOMEM;
}
proc_oom_kill_status = create_proc_info_entry("status", 0, proc_oom_kill, oom_kill_proc_info);
if (!proc_oom_kill_status) {
printk (KERN_ERR "cannot init /proc/oom_kill_protection/status\n");
remove_proc_entries();
return -ENOMEM;
}
proc_oom_kill_usage = create_proc_info_entry("usage", 0, proc_oom_kill, oom_kill_usage);
if (!proc_oom_kill_usage) {
printk (KERN_ERR "cannot init /proc/oom_kill_protection/usage\n");
remove_proc_entries();
return -ENOMEM;
}
proc_oom_kill_ctl->write_proc = oom_kill_ctl;
register_device();
printk(KERN_INFO OOM_KILL_CTL_DESC " was initialized\n");
return 0;
}
static void __exit oom_kill_ctl_cleanup(void)
{
unprotect_all();
remove_proc_entries();
unregister_device();
printk(KERN_INFO OOM_KILL_CTL_DESC " was cleaned-up\n");
}
module_init(oom_kill_ctl_init);
module_exit(oom_kill_ctl_cleanup);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(OOM_KILL_CTL_DESC ", v" OOM_KILL_CTL_VERSION);
MODULE_AUTHOR("Menny Hamburger., <[email protected]>");
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
There are two interfaces:
1) /proc interface (/proc/oom_kill_protection) that can be used inside scripts
2) A device that registers an ioctl, which can be issued via a calling c program.
To create the device after the module is loaded do the following from a script:
major_name=$(dmesg | grep oom_protect | grep dynamic | tail -n 1 | awk '{print $5}' | xargs)
if [ -n "$major_name" ]; then
mknod /dev/oom_protect c $major_name 1
fi
if [ ! -r /dev/oom_protect ]; then
logger -p error "Unable to create /dev/oom_protect"
else
logger -p notice "Created device /dev/oom_protect"
fi
Usage via a script:
echo "setprotection $$ 1 > /proc/oom_kill_protection/ctl"
Usage via a c prog:
#include <sys/ioctl.h>
........
#define OOMSETPROTECTCMD _IOWR('O', 1, int)
int oom_protection = 1, oom_protected = 1, fd;
if ((fd = open("/dev/oom_protect", 0)) >= 0) {
if (ioctl(fd, OOMSETPROTECTCMD, &oom_protection) == 0)
oom_protected = 1;
close(fd);
}
....
Menny
On Thu, 2006-12-07 at 17:50 +0200, Menny Hamburger wrote:
> Hi,
>
> Following is a rather simple module implementation that adds an interface for protecting against the oom_killer by setting the oomkilladj in the task struct.
> There are two interfaces:
> 1) /proc interface (/proc/oom_kill_protection) that can be used inside scripts
> 2) A device that registers an ioctl, which can be issued via a calling c program.
>
> To create the device after the module is loaded do the following from a script:
> major_name=$(dmesg | grep oom_protect | grep dynamic | tail -n 1 | awk '{print $5}' | xargs)
> if [ -n "$major_name" ]; then
> mknod /dev/oom_protect c $major_name 1
> fi
> if [ ! -r /dev/oom_protect ]; then
> logger -p error "Unable to create /dev/oom_protect"
> else
> logger -p notice "Created device /dev/oom_protect"
> fi
>
> Usage via a script:
> echo "setprotection $$ 1 > /proc/oom_kill_protection/ctl"
>
> Usage via a c prog:
> #include <sys/ioctl.h>
>
> .........
>
> #define OOMSETPROTECTCMD _IOWR('O', 1, int)
>
> int oom_protection = 1, oom_protected = 1, fd;
>
> if ((fd = open("/dev/oom_protect", 0)) >= 0) {
> if (ioctl(fd, OOMSETPROTECTCMD, &oom_protection) == 0)
> oom_protected = 1;
>
> close(fd);
> }
> .....
Whatever for?
Why is writing to /proc/{<pid>,self}/oom_adj not good enough?
On Thu, 7 Dec 2006 17:50:55 +0200
"Menny Hamburger" <[email protected]> wrote:
> Hi,
>
> Following is a rather simple module implementation that adds an interface for protecting against the oom_killer by setting the oomkilladj in the task struct.
NAK
you can adjust it via the /proc entry already.