[PATCH 4/5] forkbomb : periodic flushing mm history information

From: KAMEZAWA Hiroyuki
Date: Thu Mar 24 2011 - 05:36:32 EST


At 1st, this patch adds a control knob for enable/disable mm_history
tracking.

2nd, at tracking mm's history for forkbomb detection, information of
processes which doesn't seem to be important for fork-bomb detection
is just a noise.

This patch adds a knob for forgetting information with a periodic
check routine.

At every 30secs (can be configured),
1. check nr_procesess doesn't increase
2. check kswapd doesn't run
3. check allocstall doesn't occur.

If all don't happens, clear mm_history which is older than 30secs.

Note: reorder of objects in makefile was required because
mm_kobj's initcall should be called before oom's...

Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@xxxxxxxxxxxxxx>
---
mm/Makefile | 4 -
mm/oom_kill.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 139 insertions(+), 9 deletions(-)

Index: mm-work2/mm/oom_kill.c
===================================================================
--- mm-work2.orig/mm/oom_kill.c
+++ mm-work2/mm/oom_kill.c
@@ -768,6 +768,7 @@ void pagefault_out_of_memory(void)
static DEFINE_PER_CPU(unsigned long, pcpu_history_lock);
static DECLARE_RWSEM(hist_rwsem);
static int need_global_history_lock;
+static int mm_tracking_enabled = 1;

static void update_history_lock(void)
{
@@ -841,6 +842,9 @@ void track_mm_history(struct mm_struct *
{
struct mm_history *hist, *phist;

+ if (!mm_tracking_enabled)
+ return;
+
hist = kmalloc(sizeof(*hist), GFP_KERNEL);
if (!hist)
return;
@@ -864,19 +868,19 @@ void track_mm_history(struct mm_struct *
return;
}

-void delete_mm_history(struct mm_struct *mm)
+static void __delete_mm_history(struct mm_history *hist, bool check_ancestors)
{
- struct mm_history *hist, *phist;
+ struct mm_history *phist;
bool nochild;

- if (!mm->history)
+ if (!hist)
return;
- update_history_lock();
- hist = mm->history;
spin_lock(&hist->lock);
nochild = list_empty(&hist->children);
- mm->history = NULL;
- hist->mm = NULL;
+ if (hist->mm) {
+ hist->mm->history = NULL;
+ hist->mm = NULL;
+ }
spin_unlock(&hist->lock);
/* delete if we have no child */
while (nochild && hist != &init_hist) {
@@ -887,8 +891,16 @@ void delete_mm_history(struct mm_struct
nochild = (phist->mm == NULL && list_empty(&phist->children));
spin_unlock(&phist->lock);
kfree(hist);
+ if (!check_ancestors)
+ break;
hist = phist;
}
+}
+
+void delete_mm_history(struct mm_struct *mm)
+{
+ update_history_lock();
+ __delete_mm_history(mm->history, true);
update_history_unlock();
}

@@ -951,4 +963,122 @@ static struct mm_history *mm_history_sca
#define for_each_mm_history_safe(pos, tmp)\
for_each_mm_history_safe_under((pos), &init_hist, (tmp))

+static unsigned long reset_interval_jiffies = 30*HZ;
+unsigned long last_nr_procs;
+unsigned long last_pageout_run;
+unsigned long last_allocstall;
+static void reset_mm_tracking(struct work_struct *w);
+DECLARE_DELAYED_WORK(reset_mm_tracking_work, reset_mm_tracking);
+
+static void reset_mm_tracking(struct work_struct *w)
+{
+ struct mm_history *pos, *tmp;
+ unsigned long nr_procs;
+ unsigned long events[NR_VM_EVENT_ITEMS];
+ bool forget = true;
+
+ nr_procs = nr_processes();
+ if (nr_procs > last_nr_procs)
+ forget = false;
+ last_nr_procs = nr_procs;
+
+ all_vm_events(events);
+ if (last_pageout_run != events[PAGEOUTRUN])
+ forget = false;
+ last_pageout_run = events[PAGEOUTRUN];
+ if (last_allocstall != events[ALLOCSTALL])
+ forget = false;
+ last_allocstall = events[ALLOCSTALL];
+
+ if (forget) {
+ unsigned long thresh = jiffies - reset_interval_jiffies;
+ scan_history_lock();
+ for_each_mm_history_safe(pos, tmp) {
+ if (time_before(pos->start_time, thresh))
+ __delete_mm_history(pos, false);
+ }
+ scan_history_unlock();
+ }
+ if (mm_tracking_enabled)
+ schedule_delayed_work(&reset_mm_tracking_work,
+ reset_interval_jiffies);
+ return;
+}
+
+#define OOM_ATTR(_name)\
+ static struct kobj_attribute _name##_attr =\
+ __ATTR(_name, 0644, _name##_show, _name##_store)
+
+static ssize_t mm_tracker_reset_interval_msecs_show(struct kobject *obj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u", jiffies_to_msecs(reset_interval_jiffies));
+}
+
+static ssize_t mm_tracker_reset_interval_msecs_store(struct kobject *obj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long msecs;
+ int err;
+
+ err = strict_strtoul(buf, 10, &msecs);
+ if (err || msecs > UINT_MAX)
+ return -EINVAL;
+
+ reset_interval_jiffies = msecs_to_jiffies(msecs);
+ return count;
+}
+OOM_ATTR(mm_tracker_reset_interval_msecs);
+
+static ssize_t mm_tracker_enable_show(struct kobject *obj,
+ struct kobj_attribute *attr, char *buf)
+{
+ if (mm_tracking_enabled)
+ return sprintf(buf, "enabled");
+ return sprintf(buf, "disabled");
+}
+
+static ssize_t mm_tracker_enable_store(struct kobject *obj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ if (!memcmp("disable", buf, min(sizeof("disable")-1, count)))
+ mm_tracking_enabled = 0;
+ else if (!memcmp("enable", buf, min(sizeof("enable")-1, count)))
+ mm_tracking_enabled = 1;
+ else
+ return -EINVAL;
+ if (mm_tracking_enabled
+ && delayed_work_pending(&reset_mm_tracking_work))
+ schedule_delayed_work(&reset_mm_tracking_work,
+ reset_interval_jiffies);
+
+ return count;
+}
+OOM_ATTR(mm_tracker_enable);
+
+static struct attribute *oom_attrs[] = {
+ &mm_tracker_reset_interval_msecs_attr.attr,
+ &mm_tracker_enable_attr.attr,
+ NULL,
+};
+
+static struct attribute_group oom_attr_group = {
+ .attrs = oom_attrs,
+ .name = "oom",
+};
+
+static int __init init_mm_history(void)
+{
+ int err = 0;
+
+#ifdef CONFIG_SYSFS
+ err = sysfs_create_group(mm_kobj, &oom_attr_group);
+ if (err)
+ printk(KERN_ERR
+ "failed to register mm history tracking for oom\n");
+#endif
+ schedule_delayed_work(&reset_mm_tracking_work, reset_interval_jiffies);
+ return 0;
+}
+module_init(init_mm_history);
#endif
Index: mm-work2/mm/Makefile
===================================================================
--- mm-work2.orig/mm/Makefile
+++ mm-work2/mm/Makefile
@@ -7,11 +7,11 @@ mmu-$(CONFIG_MMU) := fremap.o highmem.o
mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \
vmalloc.o pagewalk.o pgtable-generic.o

-obj-y := filemap.o mempool.o oom_kill.o fadvise.o \
+obj-y := mm_init.o filemap.o mempool.o oom_kill.o fadvise.o \
maccess.o page_alloc.o page-writeback.o \
readahead.o swap.o truncate.o vmscan.o shmem.o \
prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \
- page_isolation.o mm_init.o mmu_context.o percpu.o \
+ page_isolation.o mmu_context.o percpu.o \
$(mmu-y)
obj-y += init-mm.o


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/