12 Dec 2002 lukeg   » (Master)


I've done a small kernel hack, and it doesn't work. I suspect the problem is some deep kernel magic ("blame the compiler" :-)), so I'm hoping that posting it here will lead to some kindly kernel hacker spotting the trouble.

The idea is to make an "emergency rescue" feature for a thrasing system. Recently my desktop machine was thrashing so badly that I couldn't rsh into it, couldn't kill the X server with C-M-Backspace, and basically just couldn't get it working again. It did however have perfectly good pings times during the thrasing. I assume that the kernel was healthy but all userspace processes were swapped into oblivion, and I ended up cycling the power to recover.

The attempted solution is a kernel-space program that simply kills the process using the most physical memory. It is implemented as an iptables target, so that it can be triggered remotely by sending a magic packet that matches some special iptables rule. Fun, huh?

Here's the code for the iptables target, which freezes the whole machine when I try to use it. See the problem?

/*-*- linux-c -*-
 * This is a module for recovering a system that is thrashing. When
 * trigged by a matched packet, it will kill the task that is using
 * the most physical memory (RSS). Not too subtle, but hopefully it
 * beats hitting the power button when userspace is totally thrashed
 * out of operation.
 * -- luke@bluetail.com
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/spinlock.h>
#include <net/icmp.h>
#include <net/udp.h>
#include <net/tcp.h>
#include <linux/netfilter_ipv4/ip_tables.h>

struct in_device; #include <net/route.h>

static unsigned int ipt_killfatso_target(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, const void *targinfo, void *userinfo) { struct task_struct *p, *fatso = NULL; int fatso_rss; /* Find the "fatso" process -- the one with the most RSS */ read_lock(&tasklist_lock); for_each_task(p) { spin_lock(&p->mm->page_table_lock); if (fatso == NULL || (p->mm->rss > fatso_rss)) { fatso = p; fatso_rss = p->mm->rss; } spin_unlock(&p->mm->page_table_lock); } /* And kill him.. */ if (fatso != NULL) { /* presumably there is some process.. */ printk(KERN_NOTICE "killing fatso: %d", fatso->pid); force_sig(SIGKILL, fatso); } /* Unlock the task list, *after* sending the signal - this seems to be important. */ read_unlock(&tasklist_lock); return IPT_CONTINUE; }

static int ipt_killfatso_checkentry(const char *tablename, const struct ipt_entry *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask) { return 1; }

static struct ipt_target ipt_killfatso_reg = { { NULL, NULL }, "KILLFATSO", ipt_killfatso_target, ipt_killfatso_checkentry, NULL, THIS_MODULE };

static int __init init(void) { if (ipt_register_target(&ipt_killfatso_reg)) { return -EINVAL; }

return 0; }

static void __exit fini(void) { ipt_unregister_target(&ipt_killfatso_reg); }

module_init(init); module_exit(fini); /* MODULE_LICENSE("GPL"); */

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!