cgroup notifications API and memory thresholds
Subsystems (3)
cover cgroup notifications API and memory thresholds
It's my first attempt to implement cgroup notifications API and memory thresholds on top of it. The idea of API was proposed by Paul Menage.
It lacks some important features and need more testing, but I want publish it as soon as possible to get feedback from community.
TODO:
- memory thresholds on root cgroup;
- memsw support;
- documentation.
Kirill A. Shutemov (3):
cgroup: implement eventfd-based generic API for notifications
res_counter: implement thresholds
memcg: implement memory thresholds
include/linux/cgroup.h | 8 ++ include/linux/res_counter.h | 44 +++++++++++ kernel/cgroup.c | 181 ++++++++++++++++++++++++++++++++++++++++++- kernel/res_counter.c | 4 + mm/memcontrol.c | 149 +++++++++++++++++++++++++++++++++++ 5 files changed, 385 insertions(+), 1 deletions(-)
A message that is not in the archive (sent off-list or not yet received): cover.1259255307.git.kirill-oKw7cIdHH8eLwutG50LtGA@public.gmane.org
RFC PATCH 1/3 cgroup: implement eventfd-based generic API for notifications
This patch introduces write-only file "cgroup.event_control" in every cgroup.
To register new notification handler you need:
- create an eventfd;
- open a control file to be monitored. Callbacks register_event() and unregister_event() must be defined for the control file;
- write "<event_fd> <control_fd> <args>" to cgroup.event_control. Interpretation of args is defined by control file implementation;
eventfd will be woken up by control file implementation or when the cgroup is removed.
To unregister notification handler just close eventfd.
--- include/linux/cgroup.h | 8 ++ kernel/cgroup.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 1 deletions(-)
include/linux/cgroup.h +8 -0
@@ -220,6 +220,9 @@ struct cgroup {/* For RCU-protected deletion */ struct rcu_head rcu_head; + +struct list_head event_list; +struct mutex event_list_mutex; };/*
@@ -362,6 +365,11 @@ struct cftype {int (*trigger)(struct cgroup *cgrp,unsigned int event);int (*release)(struct inode *inode,struct file *file); + +int (*register_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args); +int (*unregister_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd); };struct cgroup_scanner {
kernel/cgroup.c +180 -1
236 lines not on this page · raw message
Signature · 1 line
1.6.5.3
A message that is not in the archive (sent off-list or not yet received): bc4dc055a7307c8667da85a4d4d9d5d189af27d5.1259255307.git.kirill-oKw7cIdHH8eLwutG50LtGA@public.gmane.org
RFC PATCH 2/3 res_counter: implement thresholds
It allows to setup two thresholds: one above current usage and one below. Callback threshold_notifier() will be called if a threshold is crossed.
--- include/linux/res_counter.h | 44 +++++++++++++++++++++++++++++++++++++++++++ kernel/res_counter.c | 4 +++ 2 files changed, 48 insertions(+), 0 deletions(-)
include/linux/res_counter.h +44 -0
@@ -9,6 +9,10 @@* * Author: Pavel Emelianov <[email protected]> * +* Thresholds support +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * See Documentation/cgroups/resource_counter.txt for more * info about what this counter is. */
@@ -42,6 +46,13 @@ struct res_counter {* the number of unsuccessful attempts to consume the resource */ unsigned long long failcnt; + +unsigned long long threshold_above; +unsigned long long threshold_below; +void (*threshold_notifier)(struct res_counter *counter, +unsigned long long usage, +unsigned long long threshold); +/* * the lock to protect all of the above. * the routines below consider this to be IRQ-safe
@@ -145,6 +156,20 @@ static inline bool res_counter_soft_limit_check_locked(struct res_counter *cnt)return false; } +static inline void res_counter_threshold_notify_locked (struct res_counter *cnt) +{ +if (cnt->usage >= cnt->threshold_above) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_above); +return ; + } + +if (cnt->usage < cnt->threshold_below) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_below); +return ; + } +} + +/** * Get the difference between the usage and the soft limit * @cnt: The counter
@@ -238,4 +263,23 @@ res_counter_set_soft_limit(struct res_counter *cnt, return 0; } +static inline int +res_counter_set_thresholds (struct res_counter *cnt, +unsigned long long threshold_above, +unsigned long long threshold_below) +{ +unsigned long flags; +int ret = -EINVAL ; + +spin_lock_irqsave (&cnt->lock, flags); +if ((cnt->usage < threshold_above) && + (cnt->usage >= threshold_below)) { + cnt->threshold_above = threshold_above; + cnt->threshold_below = threshold_below; + ret =0 ; + } +spin_unlock_irqrestore (&cnt->lock, flags); +return ret; +} +#endif
kernel/res_counter.c +4 -0
@@ -20,6 +20,8 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent)spin_lock_init (&counter->lock); counter->limit =RESOURCE_MAX ; counter->soft_limit =RESOURCE_MAX ; + counter->threshold_above =RESOURCE_MAX ; + counter->threshold_below =0ULL ; counter->parent = parent; }
@@ -33,6 +35,7 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val) counter->usage += val;if (counter->usage > counter->max_usage) counter->max_usage = counter->usage; +res_counter_threshold_notify_locked (counter);return 0 ; }
@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }void res_counter_uncharge (struct res_counter *counter,unsigned long val)
Signature · 1 line
1.6.5.3
A message that is not in the archive (sent off-list or not yet received): 8524ba285f6dd59cda939c28da523f344cdab3da.1259255307.git.kirill-oKw7cIdHH8eLwutG50LtGA@public.gmane.org
RFC PATCH 3/3 memcg: implement memory thresholds
It allows to register multiple memory thresholds and gets notifications when it crosses.
To register a threshold application need:
- create an eventfd;
- open file memory.usage_in_bytes of a cgroup
- write string "<event_fd> <memory.usage_in_bytes> <threshold>" to cgroup.event_control.
Application will be notified through eventfd when memory usage crosses threshold in any direction.
--- mm/memcontrol.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 149 insertions(+), 0 deletions(-)
mm/memcontrol.c +149 -0
@@ -6,6 +6,10 @@* Copyright 2007 OpenVZ SWsoft Inc * Author: Pavel Emelianov <[email protected]> * +* Memory thresholds +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or
@@ -38,6 +42,7 @@#include <linux/vmalloc.h> #include <linux/mm_inline.h> #include <linux/page_cgroup.h> +#include <linux/eventfd.h> #include "internal.h" #include <asm/uaccess.h>
@@ -174,6 +179,12 @@ struct mem_cgroup_tree {static struct mem_cgroup_tree soft_limit_tree__read_mostly ; +struct mem_cgroup_threshold { +struct list_head list; +struct eventfd_ctx *eventfd; +u64 threshold; +}; +/* * The memory controller data structure. The memory controller controls both * page cache and RSS per cgroup. We would eventually like to provide
@@ -225,6 +236,9 @@ struct mem_cgroup {/* set when res.limit == memsw.limit */ bool memsw_is_minimum; +struct list_head thresholds; +struct mem_cgroup_threshold *current_threshold; +/* * statistics. This must be placed at the end of memcg. */
@@ -2839,12 +2853,119 @@ static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft,return 0 ; } +static inline void mem_cgroup_set_thresholds (struct res_counter *counter, +u64 above,u64 below) +{ +BUG_ON (res_counter_set_thresholds (counter, above, below)); +} + +static void mem_cgroup_threshold (struct res_counter *counter,u64 usage, +u64 threshold) +{ +struct mem_cgroup *memcg =container_of (counter, + struct mem_cgroup,res); +struct mem_cgroup_threshold *above, *below; + + above = below = memcg->current_threshold; + +if (threshold <= usage) { +list_for_each_entry_continue (above, &memcg->thresholds, + list) { +if (above->threshold > usage) +break ; + below = above; +eventfd_signal (below->eventfd,1 ); + } + }else { +list_for_each_entry_continue_reverse (below, + &memcg->thresholds, list) { +eventfd_signal (above->eventfd,1 ); +if (below->threshold <= usage) +break ; + above = below; + } + } + +mem_cgroup_set_thresholds (&memcg->res, above->threshold, + below->threshold); + memcg->current_threshold = below; +} + +static void mem_cgroup_invalidate_thresholds (struct cgroup *cgrp) +{ +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +struct mem_cgroup_threshold *tmp, *prev =NULL ; +u64 usage = memcg->res.usage; + +list_for_each_entry (tmp, &memcg->thresholds, list) { +if (tmp->threshold > usage) { +BUG_ON (!prev); + memcg->current_threshold = prev; +break ; + } + prev = tmp; + } + +mem_cgroup_set_thresholds (&memcg->res, tmp->threshold, + prev->threshold); +} + +static int mem_cgroup_register_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args) +{ +u64 threshold; +struct mem_cgroup_threshold *new, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +int ret; + +/* TODO: Root cgroup is a special case */ +if (mem_cgroup_is_root (memcg)) +return -ENOSYS ; + + ret =res_counter_memparse_write_strategy (args, &threshold); +if (ret) +return ret; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->eventfd = eventfd; + new->threshold = threshold; + +list_for_each_entry (tmp, &memcg->thresholds, list) +if (new->threshold < tmp->threshold) { +list_add_tail (&new->list, &tmp->list); +break ; + } +mem_cgroup_invalidate_thresholds (cgrp); + +return 0 ; +} + +static int mem_cgroup_unregister_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd) +{ +struct mem_cgroup_threshold *threshold, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); + +list_for_each_entry_safe (threshold, tmp, &memcg->thresholds, list) +if (threshold->eventfd == eventfd) { +list_del (&threshold->list); +kfree (threshold); + } +mem_cgroup_invalidate_thresholds (cgrp); + +return 0 ; +}static struct cftype mem_cgroup_files[] = { { .name ="usage_in_bytes" , .private =MEMFILE_PRIVATE (_MEM,RES_USAGE ), .read_u64 = mem_cgroup_read, + .register_event = mem_cgroup_register_event, + .unregister_event = mem_cgroup_unregister_event, }, { .name ="max_usage_in_bytes" ,
@@ -3080,6 +3201,32 @@ static int mem_cgroup_soft_limit_tree_init(void)return 0 ; } +static int mem_cgroup_thresholds_init (struct mem_cgroup *mem) +{ +struct mem_cgroup_threshold *new; + + mem->res.threshold_notifier = mem_cgroup_threshold; +INIT_LIST_HEAD (&mem->thresholds); + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =0ULL ; +list_add (&new->list, &mem->thresholds); + + mem->current_threshold = new; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =RESOURCE_MAX ; +list_add_tail (&new->list, &mem->thresholds); + +return 0 ; +} +static struct cgroup_subsys_state *__ref mem_cgroup_create (struct cgroup_subsys *ss,struct cgroup *cont) {
@@ -3125,6 +3272,8 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont) mem->last_scanned_child =0 ;spin_lock_init (&mem->reclaim_param_lock); +mem_cgroup_thresholds_init (mem); +if (parent) mem->swappiness =get_swappiness (parent);atomic_set (&mem->refcnt,1 );
Signature · 1 line
1.6.5.3
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
In reply to a message not in the archiveHi.
Quoted 106 lines · Kirill A. Shutemov
It allows to setup two thresholds: one above current usage and one below. Callback
threshold_notifier()will be called if a threshold is crossed.Signed-off-by Kirill A. Shutemov <[email protected]>--- include/linux/res_counter.h | 44 +++++++++++++++++++++++++++++++++++++++++++ kernel/res_counter.c | 4 +++ 2 files changed, 48 insertions(+), 0 deletions(-)include/linux/res_counter.h +44
-0@@ -9,6 +9,10 @@* * Author: Pavel Emelianov <[email protected]> * +* Thresholds support +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * See Documentation/cgroups/resource_counter.txt for more * info about what this counter is. */ @@ -42,6 +46,13 @@ struct res_counter {* the number of unsuccessful attempts to consume the resource */ unsigned long long failcnt; + +unsigned long long threshold_above; +unsigned long long threshold_below; +void (*threshold_notifier)(struct res_counter *counter, +unsigned long long usage, +unsigned long long threshold); +/* * the lock to protect all of the above. * the routines below consider this to be IRQ-safe @@ -145,6 +156,20 @@ static inline bool res_counter_soft_limit_check_locked(struct res_counter *cnt)return false; } +static inline void res_counter_threshold_notify_locked (struct res_counter *cnt) +{ +if (cnt->usage >= cnt->threshold_above) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_above); +return ; + } + +if (cnt->usage < cnt->threshold_below) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_below); +return ; + } +} + +/** * Get the difference between the usage and the soft limit * @cnt: The counter @@ -238,4 +263,23 @@ res_counter_set_soft_limit(struct res_counter *cnt, return 0; } +static inline int +res_counter_set_thresholds (struct res_counter *cnt, +unsigned long long threshold_above, +unsigned long long threshold_below) +{ +unsigned long flags; +int ret = -EINVAL ; + +spin_lock_irqsave (&cnt->lock, flags); +if ((cnt->usage < threshold_above) && + (cnt->usage >= threshold_below)) { + cnt->threshold_above = threshold_above; + cnt->threshold_below = threshold_below; + ret =0 ; + } +spin_unlock_irqrestore (&cnt->lock, flags); +return ret; +} +#endif kernel/res_counter.c +3
-0@@ -20,6 +20,8 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent)spin_lock_init (&counter->lock); counter->limit =RESOURCE_MAX ; counter->soft_limit =RESOURCE_MAX ; + counter->threshold_above =RESOURCE_MAX ; + counter->threshold_below =0ULL ; counter->parent = parent; }@@ -33,6 +35,7 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val) counter->usage += val;if (counter->usage > counter->max_usage) counter->max_usage = counter->usage; +res_counter_threshold_notify_locked (counter);return 0 ; }
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 6 lines
kernel/res_counter.c +1
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }
hmm.. this adds new checks to hot-path of process life cycle.
Do you have any number on performance impact of these patches(w/o setting any threshold)? IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for res_counter itself, but I want to know just to make sure.
Regards,
Daisuke Nishimura.
RFC PATCH 1/3 cgroup: implement eventfd-based generic API for notifications
In reply to a message not in the archiveThis patch introduces write-only file "cgroup.event_control" in every cgroup.
To register new notification handler you need:
- create an eventfd;
- open a control file to be monitored. Callbacks register_event() and unregister_event() must be defined for the control file;
- write "<event_fd> <control_fd> <args>" to cgroup.event_control. Interpretation of args is defined by control file implementation;
eventfd will be woken up by control file implementation or when the cgroup is removed.
To unregister notification handler just close eventfd.
--- include/linux/cgroup.h | 8 ++ kernel/cgroup.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 1 deletions(-)
include/linux/cgroup.h +8 -0
@@ -220,6 +220,9 @@ struct cgroup {/* For RCU-protected deletion */ struct rcu_head rcu_head; + +struct list_head event_list; +struct mutex event_list_mutex; };/*
@@ -362,6 +365,11 @@ struct cftype {int (*trigger)(struct cgroup *cgrp,unsigned int event);int (*release)(struct inode *inode,struct file *file); + +int (*register_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args); +int (*unregister_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd); };struct cgroup_scanner {
kernel/cgroup.c +180 -1
236 lines not on this page · raw message
Signature · 1 line
1.6.5.3
RFC PATCH 1/3 cgroup: implement eventfd-based generic API for notifications
In reply to Kirill A. ShutemovThis patch introduces write-only file "cgroup.event_control" in every cgroup.
To register new notification handler you need:
- create an eventfd;
- open a control file to be monitored. Callbacks register_event() and unregister_event() must be defined for the control file;
- write "<event_fd> <control_fd> <args>" to cgroup.event_control. Interpretation of args is defined by control file implementation;
eventfd will be woken up by control file implementation or when the cgroup is removed.
To unregister notification handler just close eventfd.
--- include/linux/cgroup.h | 8 ++ kernel/cgroup.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 1 deletions(-)
include/linux/cgroup.h +8 -0
@@ -220,6 +220,9 @@ struct cgroup {/* For RCU-protected deletion */ struct rcu_head rcu_head; + +struct list_head event_list; +struct mutex event_list_mutex; };/*
@@ -362,6 +365,11 @@ struct cftype {int (*trigger)(struct cgroup *cgrp,unsigned int event);int (*release)(struct inode *inode,struct file *file); + +int (*register_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args); +int (*unregister_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd); };struct cgroup_scanner {
kernel/cgroup.c +180 -1
236 lines not on this page · raw message
Signature · 1 line
1.6.5.3
RFC PATCH 2/3 res_counter: implement thresholds
It allows to setup two thresholds: one above current usage and one below. Callback threshold_notifier() will be called if a threshold is crossed.
--- include/linux/res_counter.h | 44 +++++++++++++++++++++++++++++++++++++++++++ kernel/res_counter.c | 4 +++ 2 files changed, 48 insertions(+), 0 deletions(-)
include/linux/res_counter.h +44 -0
@@ -9,6 +9,10 @@* * Author: Pavel Emelianov <[email protected]> * +* Thresholds support +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * See Documentation/cgroups/resource_counter.txt for more * info about what this counter is. */
@@ -42,6 +46,13 @@ struct res_counter {* the number of unsuccessful attempts to consume the resource */ unsigned long long failcnt; + +unsigned long long threshold_above; +unsigned long long threshold_below; +void (*threshold_notifier)(struct res_counter *counter, +unsigned long long usage, +unsigned long long threshold); +/* * the lock to protect all of the above. * the routines below consider this to be IRQ-safe
@@ -145,6 +156,20 @@ static inline bool res_counter_soft_limit_check_locked(struct res_counter *cnt)return false; } +static inline void res_counter_threshold_notify_locked (struct res_counter *cnt) +{ +if (cnt->usage >= cnt->threshold_above) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_above); +return ; + } + +if (cnt->usage < cnt->threshold_below) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_below); +return ; + } +} + +/** * Get the difference between the usage and the soft limit * @cnt: The counter
@@ -238,4 +263,23 @@ res_counter_set_soft_limit(struct res_counter *cnt, return 0; } +static inline int +res_counter_set_thresholds (struct res_counter *cnt, +unsigned long long threshold_above, +unsigned long long threshold_below) +{ +unsigned long flags; +int ret = -EINVAL ; + +spin_lock_irqsave (&cnt->lock, flags); +if ((cnt->usage < threshold_above) && + (cnt->usage >= threshold_below)) { + cnt->threshold_above = threshold_above; + cnt->threshold_below = threshold_below; + ret =0 ; + } +spin_unlock_irqrestore (&cnt->lock, flags); +return ret; +} +#endif
kernel/res_counter.c +4 -0
@@ -20,6 +20,8 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent)spin_lock_init (&counter->lock); counter->limit =RESOURCE_MAX ; counter->soft_limit =RESOURCE_MAX ; + counter->threshold_above =RESOURCE_MAX ; + counter->threshold_below =0ULL ; counter->parent = parent; }
@@ -33,6 +35,7 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val) counter->usage += val;if (counter->usage > counter->max_usage) counter->max_usage = counter->usage; +res_counter_threshold_notify_locked (counter);return 0 ; }
@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }void res_counter_uncharge (struct res_counter *counter,unsigned long val)
Signature · 1 line
1.6.5.3
RFC PATCH 3/3 memcg: implement memory thresholds
It allows to register multiple memory thresholds and gets notifications when it crosses.
To register a threshold application need:
- create an eventfd;
- open file memory.usage_in_bytes of a cgroup
- write string "<event_fd> <memory.usage_in_bytes> <threshold>" to cgroup.event_control.
Application will be notified through eventfd when memory usage crosses threshold in any direction.
--- mm/memcontrol.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 149 insertions(+), 0 deletions(-)
mm/memcontrol.c +149 -0
@@ -6,6 +6,10 @@* Copyright 2007 OpenVZ SWsoft Inc * Author: Pavel Emelianov <[email protected]> * +* Memory thresholds +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or
@@ -38,6 +42,7 @@#include <linux/vmalloc.h> #include <linux/mm_inline.h> #include <linux/page_cgroup.h> +#include <linux/eventfd.h> #include "internal.h" #include <asm/uaccess.h>
@@ -174,6 +179,12 @@ struct mem_cgroup_tree {static struct mem_cgroup_tree soft_limit_tree__read_mostly ; +struct mem_cgroup_threshold { +struct list_head list; +struct eventfd_ctx *eventfd; +u64 threshold; +}; +/* * The memory controller data structure. The memory controller controls both * page cache and RSS per cgroup. We would eventually like to provide
@@ -225,6 +236,9 @@ struct mem_cgroup {/* set when res.limit == memsw.limit */ bool memsw_is_minimum; +struct list_head thresholds; +struct mem_cgroup_threshold *current_threshold; +/* * statistics. This must be placed at the end of memcg. */
@@ -2839,12 +2853,119 @@ static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft,return 0 ; } +static inline void mem_cgroup_set_thresholds (struct res_counter *counter, +u64 above,u64 below) +{ +BUG_ON (res_counter_set_thresholds (counter, above, below)); +} + +static void mem_cgroup_threshold (struct res_counter *counter,u64 usage, +u64 threshold) +{ +struct mem_cgroup *memcg =container_of (counter, + struct mem_cgroup,res); +struct mem_cgroup_threshold *above, *below; + + above = below = memcg->current_threshold; + +if (threshold <= usage) { +list_for_each_entry_continue (above, &memcg->thresholds, + list) { +if (above->threshold > usage) +break ; + below = above; +eventfd_signal (below->eventfd,1 ); + } + }else { +list_for_each_entry_continue_reverse (below, + &memcg->thresholds, list) { +eventfd_signal (above->eventfd,1 ); +if (below->threshold <= usage) +break ; + above = below; + } + } + +mem_cgroup_set_thresholds (&memcg->res, above->threshold, + below->threshold); + memcg->current_threshold = below; +} + +static void mem_cgroup_invalidate_thresholds (struct cgroup *cgrp) +{ +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +struct mem_cgroup_threshold *tmp, *prev =NULL ; +u64 usage = memcg->res.usage; + +list_for_each_entry (tmp, &memcg->thresholds, list) { +if (tmp->threshold > usage) { +BUG_ON (!prev); + memcg->current_threshold = prev; +break ; + } + prev = tmp; + } + +mem_cgroup_set_thresholds (&memcg->res, tmp->threshold, + prev->threshold); +} + +static int mem_cgroup_register_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args) +{ +u64 threshold; +struct mem_cgroup_threshold *new, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +int ret; + +/* TODO: Root cgroup is a special case */ +if (mem_cgroup_is_root (memcg)) +return -ENOSYS ; + + ret =res_counter_memparse_write_strategy (args, &threshold); +if (ret) +return ret; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->eventfd = eventfd; + new->threshold = threshold; + +list_for_each_entry (tmp, &memcg->thresholds, list) +if (new->threshold < tmp->threshold) { +list_add_tail (&new->list, &tmp->list); +break ; + } +mem_cgroup_invalidate_thresholds (cgrp); + +return 0 ; +} + +static int mem_cgroup_unregister_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd) +{ +struct mem_cgroup_threshold *threshold, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); + +list_for_each_entry_safe (threshold, tmp, &memcg->thresholds, list) +if (threshold->eventfd == eventfd) { +list_del (&threshold->list); +kfree (threshold); + } +mem_cgroup_invalidate_thresholds (cgrp); + +return 0 ; +}static struct cftype mem_cgroup_files[] = { { .name ="usage_in_bytes" , .private =MEMFILE_PRIVATE (_MEM,RES_USAGE ), .read_u64 = mem_cgroup_read, + .register_event = mem_cgroup_register_event, + .unregister_event = mem_cgroup_unregister_event, }, { .name ="max_usage_in_bytes" ,
@@ -3080,6 +3201,32 @@ static int mem_cgroup_soft_limit_tree_init(void)return 0 ; } +static int mem_cgroup_thresholds_init (struct mem_cgroup *mem) +{ +struct mem_cgroup_threshold *new; + + mem->res.threshold_notifier = mem_cgroup_threshold; +INIT_LIST_HEAD (&mem->thresholds); + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =0ULL ; +list_add (&new->list, &mem->thresholds); + + mem->current_threshold = new; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =RESOURCE_MAX ; +list_add_tail (&new->list, &mem->thresholds); + +return 0 ; +} +static struct cgroup_subsys_state *__ref mem_cgroup_create (struct cgroup_subsys *ss,struct cgroup *cont) {
@@ -3125,6 +3272,8 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont) mem->last_scanned_child =0 ;spin_lock_init (&mem->reclaim_param_lock); +mem_cgroup_thresholds_init (mem); +if (parent) mem->swappiness =get_swappiness (parent);atomic_set (&mem->refcnt,1 );
Signature · 1 line
1.6.5.3
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
In reply to Kirill A. ShutemovHi.
Quoted 106 lines · Kirill A. Shutemov
It allows to setup two thresholds: one above current usage and one below. Callback
threshold_notifier()will be called if a threshold is crossed.Signed-off-by Kirill A. Shutemov <[email protected]>--- include/linux/res_counter.h | 44 +++++++++++++++++++++++++++++++++++++++++++ kernel/res_counter.c | 4 +++ 2 files changed, 48 insertions(+), 0 deletions(-)include/linux/res_counter.h +44
-0@@ -9,6 +9,10 @@* * Author: Pavel Emelianov <[email protected]> * +* Thresholds support +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * See Documentation/cgroups/resource_counter.txt for more * info about what this counter is. */ @@ -42,6 +46,13 @@ struct res_counter {* the number of unsuccessful attempts to consume the resource */ unsigned long long failcnt; + +unsigned long long threshold_above; +unsigned long long threshold_below; +void (*threshold_notifier)(struct res_counter *counter, +unsigned long long usage, +unsigned long long threshold); +/* * the lock to protect all of the above. * the routines below consider this to be IRQ-safe @@ -145,6 +156,20 @@ static inline bool res_counter_soft_limit_check_locked(struct res_counter *cnt)return false; } +static inline void res_counter_threshold_notify_locked (struct res_counter *cnt) +{ +if (cnt->usage >= cnt->threshold_above) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_above); +return ; + } + +if (cnt->usage < cnt->threshold_below) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_below); +return ; + } +} + +/** * Get the difference between the usage and the soft limit * @cnt: The counter @@ -238,4 +263,23 @@ res_counter_set_soft_limit(struct res_counter *cnt, return 0; } +static inline int +res_counter_set_thresholds (struct res_counter *cnt, +unsigned long long threshold_above, +unsigned long long threshold_below) +{ +unsigned long flags; +int ret = -EINVAL ; + +spin_lock_irqsave (&cnt->lock, flags); +if ((cnt->usage < threshold_above) && + (cnt->usage >= threshold_below)) { + cnt->threshold_above = threshold_above; + cnt->threshold_below = threshold_below; + ret =0 ; + } +spin_unlock_irqrestore (&cnt->lock, flags); +return ret; +} +#endif kernel/res_counter.c +3
-0@@ -20,6 +20,8 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent)spin_lock_init (&counter->lock); counter->limit =RESOURCE_MAX ; counter->soft_limit =RESOURCE_MAX ; + counter->threshold_above =RESOURCE_MAX ; + counter->threshold_below =0ULL ; counter->parent = parent; }@@ -33,6 +35,7 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val) counter->usage += val;if (counter->usage > counter->max_usage) counter->max_usage = counter->usage; +res_counter_threshold_notify_locked (counter);return 0 ; }
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 6 lines
kernel/res_counter.c +1
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }
hmm.. this adds new checks to hot-path of process life cycle.
Do you have any number on performance impact of these patches(w/o setting any threshold)? IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for res_counter itself, but I want to know just to make sure.
Regards,
Daisuke Nishimura.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
A message that is not in the archive (sent off-list or not yet received): 20091127092035.bbf2efdc.nishimura-YQH0OdQVrdy45+QrQBaojngSJqDPrsil@public.gmane.org
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
Quoted 11 lines · On Fri, 27 Nov 2009 09:20:35 +0900 Daisuke Nishimura…
Hi.
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 8 lines
kernel/res_counter.c +1
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }hmm.. this adds new checks to hot-path of process life cycle.
Quoted 4 lines
Do you have any number on performance impact of these patches(w/o setting any threshold)? IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for
res_counteritself, but I want to know just to make sure.
Another concern is to support root cgroup, you need another notifier hook in memcg because root cgroup doesn't use res_counter now.
Can't this be implemented in a way like softlimit check ?
Filter by the number of event will be good for notifier behavior, for avoiding too much wake up, too.
Thanks,
-Kame
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
In reply to Daisuke NishimuraQuoted 11 lines · On Fri, 27 Nov 2009 09:20:35 +0900 Daisuke Nishimura…
Hi.
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 8 lines
kernel/res_counter.c +1
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }hmm.. this adds new checks to hot-path of process life cycle.
Quoted 4 lines
Do you have any number on performance impact of these patches(w/o setting any threshold)? IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for
res_counteritself, but I want to know just to make sure.
Another concern is to support root cgroup, you need another notifier hook in memcg because root cgroup doesn't use res_counter now.
Can't this be implemented in a way like softlimit check ?
Filter by the number of event will be good for notifier behavior, for avoiding too much wake up, too.
Thanks,
-Kame
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
Quoted 18 lines · KAMEZAWA Hiroyuki
Quoted 11 lines · On Fri, 27 Nov 2009 09:20:35 +0900 Daisuke Nishimura…
Hi.
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 8 lines
kernel/res_counter.c +1
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; + res_counter_threshold_notify_locked(counter); }hmm.. this adds new checks to hot-path of process life cycle.
Quoted 4 lines
Do you have any number on performance impact of these patches(w/o setting any threshold)? IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for
res_counteritself, but I want to know just to make sure.
Quoted 6 lines
Another concern is to support root cgroup, you need another notifier hook in memcg because root cgroup doesn't use
res_counternow.Can't this be implemented in a way like softlimit check ?
Filter by the number of event will be good for notifier behavior, for avoiding too much wake up, too.
I guess the semantics would vary then, they would become activity semantics. I think we should avoid threshold notification for root, since we have no limits in root anymore.
BTW, Kirill, I've been meaning to write this layer on top of cgroupstats, is there anything that prevents us from using that today? CC'ing Dan Malek and Vladslav Buzov who worked on similar patches earlier.
Balbir Singh.
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
Quoted 10 lines · Balbir Singh
Quoted 8 lines · KAMEZAWA Hiroyuki
Quoted 5 lines · On Fri, 27 Nov 2009 09:20:35 +0900 Daisuke Nishimura…
Hi.
Quoted 4 lines
kernel/res_counter.c +0
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage;
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 4 lines
kernel/res_counter.c +1
-0counter->usage -= val; + res_counter_threshold_notify_locked(counter); }hmm.. this adds new checks to hot-path of process life cycle.
Do you have any number on performance impact of these patches(w/o setting any threshold)?
No, I don't. I did only functional testing on this stage.
Quoted 7 lines
Quoted 7 lines
Quoted 3 lines
IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for
res_counteritself, but I want to know just to make sure.Another concern is to support root cgroup, you need another notifier hook in memcg because root cgroup doesn't use
res_counternow.Can't this be implemented in a way like softlimit check ?
I'll investigate it.
Quoted 2 lines
Quoted 2 lines
Filter by the number of event will be good for notifier behavior, for avoiding too much wake up, too.
Good idea, thanks.
Quoted 3 lines
I guess the semantics would vary then, they would become activity semantics. I think we should avoid threshold notification for root, since we have no limits in root anymore.
Threshold notifications for root cgroup is really needed on embedded systems to avid OOM-killer.
Quoted 3 lines
BTW, Kirill, I've been meaning to write this layer on top of cgroupstats, is there anything that prevents us from using that today?
I'll investigate it.
Quoted 5 lines
CC'ing Dan Malek and Vladslav Buzov who worked on similar patches earlier.
Balbir Singh.
A message that is not in the archive (sent off-list or not yet received): 661de9470911261908i4bb51e91v649025e6c75bd91b-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
Quoted 10 lines · Balbir Singh
Quoted 8 lines · KAMEZAWA Hiroyuki
Quoted 5 lines · On Fri, 27 Nov 2009 09:20:35 +0900 Daisuke Nishimura…
Hi.
Quoted 4 lines
kernel/res_counter.c +0
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage;
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 4 lines
kernel/res_counter.c +1
-0counter->usage -= val; + res_counter_threshold_notify_locked(counter); }hmm.. this adds new checks to hot-path of process life cycle.
Do you have any number on performance impact of these patches(w/o setting any threshold)?
No, I don't. I did only functional testing on this stage.
Quoted 7 lines
Quoted 7 lines
Quoted 3 lines
IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for
res_counteritself, but I want to know just to make sure.Another concern is to support root cgroup, you need another notifier hook in memcg because root cgroup doesn't use
res_counternow.Can't this be implemented in a way like softlimit check ?
I'll investigate it.
Quoted 2 lines
Quoted 2 lines
Filter by the number of event will be good for notifier behavior, for avoiding too much wake up, too.
Good idea, thanks.
Quoted 3 lines
I guess the semantics would vary then, they would become activity semantics. I think we should avoid threshold notification for root, since we have no limits in root anymore.
Threshold notifications for root cgroup is really needed on embedded systems to avid OOM-killer.
Quoted 3 lines
BTW, Kirill, I've been meaning to write this layer on top of cgroupstats, is there anything that prevents us from using that today?
I'll investigate it.
Quoted 5 lines
CC'ing Dan Malek and Vladslav Buzov who worked on similar patches earlier.
Balbir Singh.
_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers
A message that is not in the archive (sent off-list or not yet received): 20091127114511.bbb43d5a.kamezawa.hiroyu-+CUm20s59erQFUHtdCDX3A@public.gmane.org
Re: [PATCH RFC v0 2/3] res_counter: implement thresholds
Quoted 18 lines · KAMEZAWA Hiroyuki
Quoted 11 lines · On Fri, 27 Nov 2009 09:20:35 +0900 Daisuke Nishimura…
Hi.
kernel/res_counter.c void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) · line 81
Quoted 8 lines
kernel/res_counter.c +1
-0@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; + res_counter_threshold_notify_locked(counter); }hmm.. this adds new checks to hot-path of process life cycle.
Quoted 4 lines
Do you have any number on performance impact of these patches(w/o setting any threshold)? IMHO, it might be small enough to be ignored because KAMEZAWA-san's coalesce charge/uncharge patches have decreased charge/uncharge for
res_counteritself, but I want to know just to make sure.
Quoted 6 lines
Another concern is to support root cgroup, you need another notifier hook in memcg because root cgroup doesn't use
res_counternow.Can't this be implemented in a way like softlimit check ?
Filter by the number of event will be good for notifier behavior, for avoiding too much wake up, too.
I guess the semantics would vary then, they would become activity semantics. I think we should avoid threshold notification for root, since we have no limits in root anymore.
BTW, Kirill, I've been meaning to write this layer on top of cgroupstats, is there anything that prevents us from using that today? CC'ing Dan Malek and Vladslav Buzov who worked on similar patches earlier.
Balbir Singh.
A message that is not in the archive (sent off-list or not yet received): cover.1259321503.git.kirill-oKw7cIdHH8eLwutG50LtGA@public.gmane.org
A message that is not in the archive (sent off-list or not yet received): bc4dc055a7307c8667da85a4d4d9d5d189af27d5.1259321503.git.kirill-oKw7cIdHH8eLwutG50LtGA@public.gmane.org
RFC PATCH 2/3 res_counter: implement thresholds
It allows to setup two thresholds: one above current usage and one below. Callback threshold_notifier() will be called if a threshold is crossed.
--- include/linux/res_counter.h | 44 +++++++++++++++++++++++++++++++++++++++++++ kernel/res_counter.c | 4 +++ 2 files changed, 48 insertions(+), 0 deletions(-)
include/linux/res_counter.h +44 -0
@@ -9,6 +9,10 @@* * Author: Pavel Emelianov <[email protected]> * +* Thresholds support +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * See Documentation/cgroups/resource_counter.txt for more * info about what this counter is. */
@@ -42,6 +46,13 @@ struct res_counter {* the number of unsuccessful attempts to consume the resource */ unsigned long long failcnt; + +unsigned long long threshold_above; +unsigned long long threshold_below; +void (*threshold_notifier)(struct res_counter *counter, +unsigned long long usage, +unsigned long long threshold); +/* * the lock to protect all of the above. * the routines below consider this to be IRQ-safe
@@ -145,6 +156,20 @@ static inline bool res_counter_soft_limit_check_locked(struct res_counter *cnt)return false; } +static inline void res_counter_threshold_notify_locked (struct res_counter *cnt) +{ +if (cnt->usage >= cnt->threshold_above) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_above); +return ; + } + +if (cnt->usage < cnt->threshold_below) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_below); +return ; + } +} + +/** * Get the difference between the usage and the soft limit * @cnt: The counter
@@ -238,4 +263,23 @@ res_counter_set_soft_limit(struct res_counter *cnt, return 0; } +static inline int +res_counter_set_thresholds (struct res_counter *cnt, +unsigned long long threshold_above, +unsigned long long threshold_below) +{ +unsigned long flags; +int ret = -EINVAL ; + +spin_lock_irqsave (&cnt->lock, flags); +if ((cnt->usage < threshold_above) && + (cnt->usage >= threshold_below)) { + cnt->threshold_above = threshold_above; + cnt->threshold_below = threshold_below; + ret =0 ; + } +spin_unlock_irqrestore (&cnt->lock, flags); +return ret; +} +#endif
kernel/res_counter.c +4 -0
@@ -20,6 +20,8 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent)spin_lock_init (&counter->lock); counter->limit =RESOURCE_MAX ; counter->soft_limit =RESOURCE_MAX ; + counter->threshold_above =RESOURCE_MAX ; + counter->threshold_below =0ULL ; counter->parent = parent; }
@@ -33,6 +35,7 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val) counter->usage += val;if (counter->usage > counter->max_usage) counter->max_usage = counter->usage; +res_counter_threshold_notify_locked (counter);return 0 ; }
@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }void res_counter_uncharge (struct res_counter *counter,unsigned long val)
Signature · 1 line
1.6.5.3
A message that is not in the archive (sent off-list or not yet received): 8524ba285f6dd59cda939c28da523f344cdab3da.1259321503.git.kirill-oKw7cIdHH8eLwutG50LtGA@public.gmane.org
RFC PATCH 3/3 memcg: implement memory thresholds
It allows to register multiple memory and memsw thresholds and gets notifications when it crosses.
To register a threshold application need:
- create an eventfd;
- open memory.usage_in_bytes or memory.memsw.usage_in_bytes;
- write string like "<event_fd> <memory.usage_in_bytes> <threshold>" to cgroup.event_control.
Application will be notified through eventfd when memory usage crosses threshold in any direction.
--- mm/memcontrol.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 224 insertions(+), 0 deletions(-)
mm/memcontrol.c +224 -0
@@ -6,6 +6,10 @@* Copyright 2007 OpenVZ SWsoft Inc * Author: Pavel Emelianov <[email protected]> * +* Memory thresholds +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or
@@ -38,6 +42,7 @@#include <linux/vmalloc.h> #include <linux/mm_inline.h> #include <linux/page_cgroup.h> +#include <linux/eventfd.h> #include "internal.h" #include <asm/uaccess.h>
@@ -174,6 +179,12 @@ struct mem_cgroup_tree {static struct mem_cgroup_tree soft_limit_tree__read_mostly ; +struct mem_cgroup_threshold { +struct list_head list; +struct eventfd_ctx *eventfd; +u64 threshold; +}; +/* * The memory controller data structure. The memory controller controls both * page cache and RSS per cgroup. We would eventually like to provide
@@ -225,6 +236,12 @@ struct mem_cgroup {/* set when res.limit == memsw.limit */ bool memsw_is_minimum; +struct list_head thresholds; +struct mem_cgroup_threshold *current_threshold; + +struct list_head memsw_thresholds; +struct mem_cgroup_threshold *memsw_current_threshold; +/* * statistics. This must be placed at the end of memcg. */
@@ -2839,12 +2856,184 @@ static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft,return 0 ; } +static inline void mem_cgroup_set_thresholds (struct res_counter *counter, +u64 above,u64 below) +{ +BUG_ON (res_counter_set_thresholds (counter, above, below)); +} + +static void mem_cgroup_threshold (struct mem_cgroup *memcg, +struct res_counter *counter,u64 usage,u64 threshold) +{ +struct mem_cgroup_threshold *above, *below; +struct list_head *thresholds; +struct mem_cgroup_threshold **current_threshold; + +if (&memcg->res == counter) { + thresholds = &memcg->thresholds; + current_threshold = &memcg->current_threshold; + }else if (&memcg->memsw == counter) { + thresholds = &memcg->memsw_thresholds; + current_threshold = &memcg->memsw_current_threshold; + }else +BUG (); + + above = below = *current_threshold; + +if (threshold <= usage) { +list_for_each_entry_continue (above, thresholds, list) { +if (above->threshold > usage) +break ; + below = above; +eventfd_signal (below->eventfd,1 ); + } + }else { +list_for_each_entry_continue_reverse (below, thresholds, list) { +eventfd_signal (above->eventfd,1 ); +if (below->threshold <= usage) +break ; + above = below; + } + } + +mem_cgroup_set_thresholds (counter, above->threshold, below->threshold); + *current_threshold = below; +} + +static void mem_cgroup_mem_threshold (struct res_counter *counter,u64 usage, +u64 threshold) +{ + struct mem_cgroup *memcg = container_of(counter, struct mem_cgroup, + res); + +mem_cgroup_threshold (memcg, counter, usage, threshold); +} + +static void mem_cgroup_memsw_threshold (struct res_counter *counter,u64 usage, +u64 threshold) +{ + struct mem_cgroup *memcg = container_of(counter, struct mem_cgroup, + memsw); + +mem_cgroup_threshold (memcg, counter, usage, threshold); +} + +static void mem_cgroup_invalidate_thresholds (struct res_counter *counter, +struct list_head *thresholds, +struct mem_cgroup_threshold **current_threshold) +{ +struct mem_cgroup_threshold *tmp, *prev =NULL ; + +list_for_each_entry (tmp, thresholds, list) { +if (tmp->threshold > counter->usage) { +BUG_ON (!prev); + *current_threshold = prev; +break ; + } + prev = tmp; + } + +mem_cgroup_set_thresholds (counter, tmp->threshold, prev->threshold); +} + +static int mem_cgroup_register_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args) +{ +u64 threshold; +struct mem_cgroup_threshold *new, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +struct list_head *thresholds; +struct mem_cgroup_threshold **current_threshold; +struct res_counter *counter; +int type =MEMFILE_TYPE (cft->private); +int ret; + +/* XXX: Should we implement thresholds for root cgroup */ +if (mem_cgroup_is_root (memcg)) +return -EINVAL ; + + ret =res_counter_memparse_write_strategy (args, &threshold); +if (ret) +return ret; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->eventfd = eventfd; + new->threshold = threshold; + +switch (type) { +case _MEM: + thresholds = &memcg->thresholds; + current_threshold = &memcg->current_threshold; + counter = &memcg->res; +break ; +case _MEMSWAP: + thresholds = &memcg->memsw_thresholds; + current_threshold = &memcg->memsw_current_threshold; + counter = &memcg->memsw; +break ; +default : +BUG (); +break ; + } + +list_for_each_entry (tmp, thresholds, list) +if (new->threshold < tmp->threshold) { +list_add_tail (&new->list, &tmp->list); +break ; + } +mem_cgroup_invalidate_thresholds (counter, thresholds, + current_threshold); + +return 0 ; +} + +static int mem_cgroup_unregister_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd) +{ +struct mem_cgroup_threshold *threshold, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +struct list_head *thresholds; +struct mem_cgroup_threshold **current_threshold; +struct res_counter *counter; +int type =MEMFILE_TYPE (cft->private); + +switch (type) { +case _MEM: + thresholds = &memcg->thresholds; + current_threshold = &memcg->current_threshold; + counter = &memcg->res; +break ; +case _MEMSWAP: + thresholds = &memcg->memsw_thresholds; + current_threshold = &memcg->memsw_current_threshold; + counter = &memcg->memsw; +break ; +default : +BUG (); +break ; + } + +list_for_each_entry_safe (threshold, tmp, thresholds, list) +if (threshold->eventfd == eventfd) { +list_del (&threshold->list); +kfree (threshold); + } +mem_cgroup_invalidate_thresholds (counter, thresholds, + current_threshold); + +return 0 ; +}static struct cftype mem_cgroup_files[] = { { .name ="usage_in_bytes" , .private =MEMFILE_PRIVATE (_MEM,RES_USAGE ), .read_u64 = mem_cgroup_read, + .register_event = mem_cgroup_register_event, + .unregister_event = mem_cgroup_unregister_event, }, { .name ="max_usage_in_bytes" ,
@@ -2896,6 +3085,8 @@ static struct cftype memsw_cgroup_files[] = { .name = "memsw.usage_in_bytes", .private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE), .read_u64 = mem_cgroup_read, + .register_event = mem_cgroup_register_event, + .unregister_event = mem_cgroup_unregister_event, }, { .name = "memsw.max_usage_in_bytes",
@@ -3080,6 +3271,33 @@ static int mem_cgroup_soft_limit_tree_init(void)return 0 ; } + +static int mem_cgroup_thresholds_init (struct list_head *thresholds, +struct mem_cgroup_threshold **current_threshold) +{ +struct mem_cgroup_threshold *new; + +INIT_LIST_HEAD (thresholds); + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =0ULL ; +list_add (&new->list, thresholds); + + *current_threshold = new; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =RESOURCE_MAX ; +list_add_tail (&new->list, thresholds); + +return 0 ; +} +static struct cgroup_subsys_state *__ref mem_cgroup_create (struct cgroup_subsys *ss,struct cgroup *cont) {
@@ -3125,6 +3343,12 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont) mem->last_scanned_child =0 ;spin_lock_init (&mem->reclaim_param_lock); + mem->res.threshold_notifier = mem_cgroup_mem_threshold; + mem->memsw.threshold_notifier = mem_cgroup_memsw_threshold; +mem_cgroup_thresholds_init (&mem->thresholds, &mem->current_threshold); +mem_cgroup_thresholds_init (&mem->memsw_thresholds, + &mem->memsw_current_threshold); +if (parent) mem->swappiness =get_swappiness (parent);atomic_set (&mem->refcnt,1 );
Signature · 1 line
1.6.5.3
RFC PATCH 1/3 cgroup: implement eventfd-based generic API for notifications
In reply to Kirill A. ShutemovThis patch introduces write-only file "cgroup.event_control" in every cgroup.
To register new notification handler you need:
- create an eventfd;
- open a control file to be monitored. Callbacks register_event() and unregister_event() must be defined for the control file;
- write "<event_fd> <control_fd> <args>" to cgroup.event_control. Interpretation of args is defined by control file implementation;
eventfd will be woken up by control file implementation or when the cgroup is removed.
To unregister notification handler just close eventfd.
--- include/linux/cgroup.h | 8 ++ kernel/cgroup.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 1 deletions(-)
include/linux/cgroup.h +8 -0
@@ -220,6 +220,9 @@ struct cgroup {/* For RCU-protected deletion */ struct rcu_head rcu_head; + +struct list_head event_list; +struct mutex event_list_mutex; };/*
@@ -362,6 +365,11 @@ struct cftype {int (*trigger)(struct cgroup *cgrp,unsigned int event);int (*release)(struct inode *inode,struct file *file); + +int (*register_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args); +int (*unregister_event)(struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd); };struct cgroup_scanner {
kernel/cgroup.c +180 -1
236 lines not on this page · raw message
Signature · 7 lines
1.6.5.3 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
RFC PATCH 2/3 res_counter: implement thresholds
It allows to setup two thresholds: one above current usage and one below. Callback threshold_notifier() will be called if a threshold is crossed.
--- include/linux/res_counter.h | 44 +++++++++++++++++++++++++++++++++++++++++++ kernel/res_counter.c | 4 +++ 2 files changed, 48 insertions(+), 0 deletions(-)
include/linux/res_counter.h +44 -0
@@ -9,6 +9,10 @@* * Author: Pavel Emelianov <[email protected]> * +* Thresholds support +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * See Documentation/cgroups/resource_counter.txt for more * info about what this counter is. */
@@ -42,6 +46,13 @@ struct res_counter {* the number of unsuccessful attempts to consume the resource */ unsigned long long failcnt; + +unsigned long long threshold_above; +unsigned long long threshold_below; +void (*threshold_notifier)(struct res_counter *counter, +unsigned long long usage, +unsigned long long threshold); +/* * the lock to protect all of the above. * the routines below consider this to be IRQ-safe
@@ -145,6 +156,20 @@ static inline bool res_counter_soft_limit_check_locked(struct res_counter *cnt)return false; } +static inline void res_counter_threshold_notify_locked (struct res_counter *cnt) +{ +if (cnt->usage >= cnt->threshold_above) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_above); +return ; + } + +if (cnt->usage < cnt->threshold_below) { + cnt->threshold_notifier (cnt, cnt->usage, cnt->threshold_below); +return ; + } +} + +/** * Get the difference between the usage and the soft limit * @cnt: The counter
@@ -238,4 +263,23 @@ res_counter_set_soft_limit(struct res_counter *cnt, return 0; } +static inline int +res_counter_set_thresholds (struct res_counter *cnt, +unsigned long long threshold_above, +unsigned long long threshold_below) +{ +unsigned long flags; +int ret = -EINVAL ; + +spin_lock_irqsave (&cnt->lock, flags); +if ((cnt->usage < threshold_above) && + (cnt->usage >= threshold_below)) { + cnt->threshold_above = threshold_above; + cnt->threshold_below = threshold_below; + ret =0 ; + } +spin_unlock_irqrestore (&cnt->lock, flags); +return ret; +} +#endif
kernel/res_counter.c +4 -0
@@ -20,6 +20,8 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent)spin_lock_init (&counter->lock); counter->limit =RESOURCE_MAX ; counter->soft_limit =RESOURCE_MAX ; + counter->threshold_above =RESOURCE_MAX ; + counter->threshold_below =0ULL ; counter->parent = parent; }
@@ -33,6 +35,7 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val) counter->usage += val;if (counter->usage > counter->max_usage) counter->max_usage = counter->usage; +res_counter_threshold_notify_locked (counter);return 0 ; }
@@ -73,6 +76,7 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) val = counter->usage; counter->usage -= val; +res_counter_threshold_notify_locked (counter); }void res_counter_uncharge (struct res_counter *counter,unsigned long val)
Signature · 7 lines
1.6.5.3 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
RFC PATCH 3/3 memcg: implement memory thresholds
It allows to register multiple memory and memsw thresholds and gets notifications when it crosses.
To register a threshold application need:
- create an eventfd;
- open memory.usage_in_bytes or memory.memsw.usage_in_bytes;
- write string like "<event_fd> <memory.usage_in_bytes> <threshold>" to cgroup.event_control.
Application will be notified through eventfd when memory usage crosses threshold in any direction.
--- mm/memcontrol.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 224 insertions(+), 0 deletions(-)
mm/memcontrol.c +224 -0
@@ -6,6 +6,10 @@* Copyright 2007 OpenVZ SWsoft Inc * Author: Pavel Emelianov <[email protected]> * +* Memory thresholds +* Copyright (C) 2009 Nokia Corporation +* Author: Kirill A. Shutemov +* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or
@@ -38,6 +42,7 @@#include <linux/vmalloc.h> #include <linux/mm_inline.h> #include <linux/page_cgroup.h> +#include <linux/eventfd.h> #include "internal.h" #include <asm/uaccess.h>
@@ -174,6 +179,12 @@ struct mem_cgroup_tree {static struct mem_cgroup_tree soft_limit_tree__read_mostly ; +struct mem_cgroup_threshold { +struct list_head list; +struct eventfd_ctx *eventfd; +u64 threshold; +}; +/* * The memory controller data structure. The memory controller controls both * page cache and RSS per cgroup. We would eventually like to provide
@@ -225,6 +236,12 @@ struct mem_cgroup {/* set when res.limit == memsw.limit */ bool memsw_is_minimum; +struct list_head thresholds; +struct mem_cgroup_threshold *current_threshold; + +struct list_head memsw_thresholds; +struct mem_cgroup_threshold *memsw_current_threshold; +/* * statistics. This must be placed at the end of memcg. */
@@ -2839,12 +2856,184 @@ static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft,return 0 ; } +static inline void mem_cgroup_set_thresholds (struct res_counter *counter, +u64 above,u64 below) +{ +BUG_ON (res_counter_set_thresholds (counter, above, below)); +} + +static void mem_cgroup_threshold (struct mem_cgroup *memcg, +struct res_counter *counter,u64 usage,u64 threshold) +{ +struct mem_cgroup_threshold *above, *below; +struct list_head *thresholds; +struct mem_cgroup_threshold **current_threshold; + +if (&memcg->res == counter) { + thresholds = &memcg->thresholds; + current_threshold = &memcg->current_threshold; + }else if (&memcg->memsw == counter) { + thresholds = &memcg->memsw_thresholds; + current_threshold = &memcg->memsw_current_threshold; + }else +BUG (); + + above = below = *current_threshold; + +if (threshold <= usage) { +list_for_each_entry_continue (above, thresholds, list) { +if (above->threshold > usage) +break ; + below = above; +eventfd_signal (below->eventfd,1 ); + } + }else { +list_for_each_entry_continue_reverse (below, thresholds, list) { +eventfd_signal (above->eventfd,1 ); +if (below->threshold <= usage) +break ; + above = below; + } + } + +mem_cgroup_set_thresholds (counter, above->threshold, below->threshold); + *current_threshold = below; +} + +static void mem_cgroup_mem_threshold (struct res_counter *counter,u64 usage, +u64 threshold) +{ + struct mem_cgroup *memcg = container_of(counter, struct mem_cgroup, + res); + +mem_cgroup_threshold (memcg, counter, usage, threshold); +} + +static void mem_cgroup_memsw_threshold (struct res_counter *counter,u64 usage, +u64 threshold) +{ + struct mem_cgroup *memcg = container_of(counter, struct mem_cgroup, + memsw); + +mem_cgroup_threshold (memcg, counter, usage, threshold); +} + +static void mem_cgroup_invalidate_thresholds (struct res_counter *counter, +struct list_head *thresholds, +struct mem_cgroup_threshold **current_threshold) +{ +struct mem_cgroup_threshold *tmp, *prev =NULL ; + +list_for_each_entry (tmp, thresholds, list) { +if (tmp->threshold > counter->usage) { +BUG_ON (!prev); + *current_threshold = prev; +break ; + } + prev = tmp; + } + +mem_cgroup_set_thresholds (counter, tmp->threshold, prev->threshold); +} + +static int mem_cgroup_register_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd,const char *args) +{ +u64 threshold; +struct mem_cgroup_threshold *new, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +struct list_head *thresholds; +struct mem_cgroup_threshold **current_threshold; +struct res_counter *counter; +int type =MEMFILE_TYPE (cft->private); +int ret; + +/* XXX: Should we implement thresholds for root cgroup */ +if (mem_cgroup_is_root (memcg)) +return -EINVAL ; + + ret =res_counter_memparse_write_strategy (args, &threshold); +if (ret) +return ret; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->eventfd = eventfd; + new->threshold = threshold; + +switch (type) { +case _MEM: + thresholds = &memcg->thresholds; + current_threshold = &memcg->current_threshold; + counter = &memcg->res; +break ; +case _MEMSWAP: + thresholds = &memcg->memsw_thresholds; + current_threshold = &memcg->memsw_current_threshold; + counter = &memcg->memsw; +break ; +default : +BUG (); +break ; + } + +list_for_each_entry (tmp, thresholds, list) +if (new->threshold < tmp->threshold) { +list_add_tail (&new->list, &tmp->list); +break ; + } +mem_cgroup_invalidate_thresholds (counter, thresholds, + current_threshold); + +return 0 ; +} + +static int mem_cgroup_unregister_event (struct cgroup *cgrp,struct cftype *cft, +struct eventfd_ctx *eventfd) +{ +struct mem_cgroup_threshold *threshold, *tmp; +struct mem_cgroup *memcg =mem_cgroup_from_cont (cgrp); +struct list_head *thresholds; +struct mem_cgroup_threshold **current_threshold; +struct res_counter *counter; +int type =MEMFILE_TYPE (cft->private); + +switch (type) { +case _MEM: + thresholds = &memcg->thresholds; + current_threshold = &memcg->current_threshold; + counter = &memcg->res; +break ; +case _MEMSWAP: + thresholds = &memcg->memsw_thresholds; + current_threshold = &memcg->memsw_current_threshold; + counter = &memcg->memsw; +break ; +default : +BUG (); +break ; + } + +list_for_each_entry_safe (threshold, tmp, thresholds, list) +if (threshold->eventfd == eventfd) { +list_del (&threshold->list); +kfree (threshold); + } +mem_cgroup_invalidate_thresholds (counter, thresholds, + current_threshold); + +return 0 ; +}static struct cftype mem_cgroup_files[] = { { .name ="usage_in_bytes" , .private =MEMFILE_PRIVATE (_MEM,RES_USAGE ), .read_u64 = mem_cgroup_read, + .register_event = mem_cgroup_register_event, + .unregister_event = mem_cgroup_unregister_event, }, { .name ="max_usage_in_bytes" ,
@@ -2896,6 +3085,8 @@ static struct cftype memsw_cgroup_files[] = { .name = "memsw.usage_in_bytes", .private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE), .read_u64 = mem_cgroup_read, + .register_event = mem_cgroup_register_event, + .unregister_event = mem_cgroup_unregister_event, }, { .name = "memsw.max_usage_in_bytes",
@@ -3080,6 +3271,33 @@ static int mem_cgroup_soft_limit_tree_init(void)return 0 ; } + +static int mem_cgroup_thresholds_init (struct list_head *thresholds, +struct mem_cgroup_threshold **current_threshold) +{ +struct mem_cgroup_threshold *new; + +INIT_LIST_HEAD (thresholds); + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =0ULL ; +list_add (&new->list, thresholds); + + *current_threshold = new; + + new =kmalloc (sizeof (*new),GFP_KERNEL ); +if (!new) +return -ENOMEM ; +INIT_LIST_HEAD (&new->list); + new->threshold =RESOURCE_MAX ; +list_add_tail (&new->list, thresholds); + +return 0 ; +} +static struct cgroup_subsys_state *__ref mem_cgroup_create (struct cgroup_subsys *ss,struct cgroup *cont) {
@@ -3125,6 +3343,12 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont) mem->last_scanned_child =0 ;spin_lock_init (&mem->reclaim_param_lock); + mem->res.threshold_notifier = mem_cgroup_mem_threshold; + mem->memsw.threshold_notifier = mem_cgroup_memsw_threshold; +mem_cgroup_thresholds_init (&mem->thresholds, &mem->current_threshold); +mem_cgroup_thresholds_init (&mem->memsw_thresholds, + &mem->memsw_current_threshold); +if (parent) mem->swappiness =get_swappiness (parent);atomic_set (&mem->refcnt,1 );
Signature · 7 lines
1.6.5.3 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>