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"); */
