cgroup: Limit event generation frequency
authorTejun Heo <tj@kernel.org>
Thu, 26 Apr 2018 21:29:04 +0000 (14:29 -0700)
committerTejun Heo <tj@kernel.org>
Thu, 26 Apr 2018 21:29:04 +0000 (14:29 -0700)
".events" files generate file modified event to notify userland of
possible new events.  Some of the events can be quite bursty
(e.g. memory high event) and generating notification each time is
costly and pointless.

This patch implements a event rate limit mechanism.  If a new
notification is requested before 10ms has passed since the previous
notification, the new notification is delayed till then.

As this only delays from the second notification on in a given close
cluster of notifications, userland reactions to notifications
shouldn't be delayed at all in most cases while avoiding notification
storms.

Signed-off-by: Tejun Heo <tj@kernel.org>
include/linux/cgroup-defs.h
kernel/cgroup/cgroup.c

index dc5b70449dc60ec5163ba891005283525acda40c..133531fcfb33148ea0c00e59fa3f818985ebdfdb 100644 (file)
@@ -105,6 +105,8 @@ enum {
 struct cgroup_file {
        /* do not access any fields from outside cgroup core */
        struct kernfs_node *kn;
+       unsigned long notified_at;
+       struct timer_list notify_timer;
 };
 
 /*
index 25309c8687286f485017ebaca26bfa1fbf3527db..fdb7a582f8fcfc5efa8072c040157e84343fa859 100644 (file)
@@ -61,6 +61,8 @@
 
 #define CGROUP_FILE_NAME_MAX           (MAX_CGROUP_TYPE_NAMELEN +      \
                                         MAX_CFTYPE_NAME + 2)
+/* let's not notify more than 100 times per second */
+#define CGROUP_FILE_NOTIFY_MIN_INTV    DIV_ROUND_UP(HZ, 100)
 
 /*
  * cgroup_mutex is the master lock.  Any modification to cgroup or its
@@ -1554,6 +1556,8 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
                spin_lock_irq(&cgroup_file_kn_lock);
                cfile->kn = NULL;
                spin_unlock_irq(&cgroup_file_kn_lock);
+
+               del_timer_sync(&cfile->notify_timer);
        }
 
        kernfs_remove_by_name(cgrp->kn, cgroup_file_name(cgrp, cft, name));
@@ -3532,6 +3536,12 @@ static int cgroup_kn_set_ugid(struct kernfs_node *kn)
        return kernfs_setattr(kn, &iattr);
 }
 
+static void cgroup_file_notify_timer(struct timer_list *timer)
+{
+       cgroup_file_notify(container_of(timer, struct cgroup_file,
+                                       notify_timer));
+}
+
 static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
                           struct cftype *cft)
 {
@@ -3558,6 +3568,8 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
        if (cft->file_offset) {
                struct cgroup_file *cfile = (void *)css + cft->file_offset;
 
+               timer_setup(&cfile->notify_timer, cgroup_file_notify_timer, 0);
+
                spin_lock_irq(&cgroup_file_kn_lock);
                cfile->kn = kn;
                spin_unlock_irq(&cgroup_file_kn_lock);
@@ -3807,8 +3819,17 @@ void cgroup_file_notify(struct cgroup_file *cfile)
        unsigned long flags;
 
        spin_lock_irqsave(&cgroup_file_kn_lock, flags);
-       if (cfile->kn)
-               kernfs_notify(cfile->kn);
+       if (cfile->kn) {
+               unsigned long last = cfile->notified_at;
+               unsigned long next = last + CGROUP_FILE_NOTIFY_MIN_INTV;
+
+               if (time_in_range(jiffies, last, next)) {
+                       timer_reduce(&cfile->notify_timer, next);
+               } else {
+                       kernfs_notify(cfile->kn);
+                       cfile->notified_at = jiffies;
+               }
+       }
        spin_unlock_irqrestore(&cgroup_file_kn_lock, flags);
 }