Re: [PATCH V3] memcg: add reclaim pgfault latency histograms

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 17 Jun 2011 16:53:48 -0700
Ying Han <yinghan@xxxxxxxxxx> wrote:

> This adds histogram to capture pagefault latencies on per-memcg basis. I used
> this patch on the memcg background reclaim test, and figured there could be more
> usecases to monitor/debug application performance.
> 
> The histogram is composed 8 bucket in us unit. The last one is "rest" which is
> everything beyond the last one. To be more flexible, the buckets can be reset
> and also each bucket is configurable at runtime.
> 
> memory.pgfault_histogram: exports the histogram on per-memcg basis and also can
> be reset by echoing "-1". Meantime, all the buckets are writable by echoing
> the range into the API. see the example below.
> 
> change v3..v2:
> no change except rebasing the patch to 3.0-rc3 and retested.
> 
> change v2..v1:
> 1. record the page fault involving reclaim only and changing the unit to us.
> 2. rename the "inf" to "rest".
> 3. removed the global tunable to turn on/off the recording. this is ok since
> there is no overhead measured by collecting the data.
> 4. changed reseting the history by echoing "-1".
> 
> Functional Test:
> $ cat /dev/cgroup/memory/D/memory.pgfault_histogram
> page reclaim latency histogram (us):
> < 150            22
> < 200            17434
> < 250            69135
> < 300            17182
> < 350            4180
> < 400            3179
> < 450            2644
> < rest           29840
> 
> $ echo -1 >/dev/cgroup/memory/D/memory.pgfault_histogram
> $ cat /dev/cgroup/memory/B/memory.pgfault_histogram
> page reclaim latency histogram (us):
> < 150            0
> < 200            0
> < 250            0
> < 300            0
> < 350            0
> < 400            0
> < 450            0
> < rest           0
> 
> $ echo 500 520 540 580 600 1000 5000 >/dev/cgroup/memory/D/memory.pgfault_histogram
> $ cat /dev/cgroup/memory/B/memory.pgfault_histogram
> page reclaim latency histogram (us):
> < 500            0
> < 520            0
> < 540            0
> < 580            0
> < 600            0
> < 1000           0
> < 5000           0
> < rest           0
> 
> Performance Test:
> I ran through the PageFaultTest (pft) benchmark to measure the overhead of
> recording the histogram. There is no overhead observed on both "flt/cpu/s"
> and "fault/wsec".
> 
> $ mkdir /dev/cgroup/memory/A
> $ echo 16g >/dev/cgroup/memory/A/memory.limit_in_bytes
> $ echo $$ >/dev/cgroup/memory/A/tasks
> $ ./pft -m 15g -t 8 -T a
> 
> Result:
> $ ./ministat no_histogram histogram
> 
> "fault/wsec"
> x fault_wsec/no_histogram
> + fault_wsec/histogram
> +-------------------------------------------------------------------------+
>     N           Min           Max        Median           Avg        Stddev
> x   5     864432.44     880840.81     879707.95     874606.51     7687.9841
> +   5     861986.57     877867.25      870823.9     870901.38     6413.8821
> No difference proven at 95.0% confidence
> 
> "flt/cpu/s"
> x flt_cpu_s/no_histogram
> + flt_cpu_s/histogram
> +-------------------------------------------------------------------------+
>     N           Min           Max        Median           Avg        Stddev
> x   5     110866.26     112277.92     112143.45     111926.43     595.48419
> +   5     110297.25     111843.66     111583.45     111170.42     739.60994
> No difference proven at 95.0% confidence
> 
> Signed-off-by: Ying Han <yinghan@xxxxxxxxxx>


I'll never ack this.

Thanks,
-Kame

> ---
>  include/linux/memcontrol.h |    3 +
>  mm/memcontrol.c            |  130 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 133 insertions(+), 0 deletions(-)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 9724a38..96f93e0 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -85,6 +85,8 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);
>  extern struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page);
>  extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p);
>  
> +extern void memcg_histogram_record(struct task_struct *tsk, u64 delta);
> +
>  static inline
>  int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup)
>  {
> @@ -362,6 +364,7 @@ static inline void mem_cgroup_split_huge_fixup(struct page *head,
>  
>  static inline
>  void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx)
> +void memcg_histogram_record(struct task_struct *tsk, u64 delta)
>  {
>  }
>  #endif /* CONFIG_CGROUP_MEM_CONT */
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index bd9052a..7735ca1 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -48,6 +48,7 @@
>  #include <linux/page_cgroup.h>
>  #include <linux/cpu.h>
>  #include <linux/oom.h>
> +#include <linux/ctype.h>
>  #include "internal.h"
>  
>  #include <asm/uaccess.h>
> @@ -202,6 +203,12 @@ struct mem_cgroup_eventfd_list {
>  static void mem_cgroup_threshold(struct mem_cgroup *mem);
>  static void mem_cgroup_oom_notify(struct mem_cgroup *mem);
>  
> +#define MEMCG_NUM_HISTO_BUCKETS		8
> +
> +struct memcg_histo {
> +	u64 count[MEMCG_NUM_HISTO_BUCKETS];
> +};
> +
>  /*
>   * The memory controller data structure. The memory controller controls both
>   * page cache and RSS per cgroup. We would eventually like to provide
> @@ -279,6 +286,9 @@ struct mem_cgroup {
>  	 */
>  	struct mem_cgroup_stat_cpu nocpu_base;
>  	spinlock_t pcp_counter_lock;
> +
> +	struct memcg_histo *memcg_histo;
> +	u64 memcg_histo_range[MEMCG_NUM_HISTO_BUCKETS];
>  };
>  
>  /* Stuffs for move charges at task migration. */
> @@ -2117,6 +2127,7 @@ static int mem_cgroup_do_charge(struct mem_cgroup *mem, gfp_t gfp_mask,
>  	struct mem_cgroup *mem_over_limit;
>  	struct res_counter *fail_res;
>  	unsigned long flags = 0;
> +	unsigned long long start, delta;
>  	int ret;
>  
>  	ret = res_counter_charge(&mem->res, csize, &fail_res);
> @@ -2146,8 +2157,14 @@ static int mem_cgroup_do_charge(struct mem_cgroup *mem, gfp_t gfp_mask,
>  	if (!(gfp_mask & __GFP_WAIT))
>  		return CHARGE_WOULDBLOCK;
>  
> +	start = sched_clock();
>  	ret = mem_cgroup_hierarchical_reclaim(mem_over_limit, NULL,
>  					      gfp_mask, flags, NULL);
> +	delta = sched_clock() - start;
> +	if (unlikely(delta < 0))
> +		delta = 0;
> +	memcg_histogram_record(current, delta);
> +
>  	if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
>  		return CHARGE_RETRY;
>  	/*
> @@ -4573,6 +4590,102 @@ static int mem_control_numa_stat_open(struct inode *unused, struct file *file)
>  }
>  #endif /* CONFIG_NUMA */
>  
> +static int mem_cgroup_histogram_seq_read(struct cgroup *cgrp,
> +					struct cftype *cft, struct seq_file *m)
> +{
> +	struct mem_cgroup *mem_cont = mem_cgroup_from_cont(cgrp);
> +	int i, cpu;
> +
> +	seq_printf(m, "page reclaim latency histogram (us):\n");
> +
> +	for (i = 0; i < MEMCG_NUM_HISTO_BUCKETS; i++) {
> +		u64 sum = 0;
> +
> +		for_each_present_cpu(cpu) {
> +			struct memcg_histo *histo;
> +			histo = per_cpu_ptr(mem_cont->memcg_histo, cpu);
> +			sum += histo->count[i];
> +		}
> +
> +		if (i < MEMCG_NUM_HISTO_BUCKETS - 1)
> +			seq_printf(m, "< %-15llu",
> +					mem_cont->memcg_histo_range[i] / 1000);
> +		else
> +			seq_printf(m, "< %-15s", "rest");
> +		seq_printf(m, "%llu\n", sum);
> +	}
> +
> +	return 0;
> +}
> +
> +static int mem_cgroup_histogram_seq_write(struct cgroup *cgrp,
> +					struct cftype *cft, const char *buffer)
> +{
> +	int i;
> +	u64 data[MEMCG_NUM_HISTO_BUCKETS];
> +	char *end;
> +	struct mem_cgroup *mem_cont = mem_cgroup_from_cont(cgrp);
> +
> +	if (simple_strtol(buffer, &end, 10) == -1) {
> +		for_each_present_cpu(i) {
> +			struct memcg_histo *histo;
> +
> +			histo = per_cpu_ptr(mem_cont->memcg_histo, i);
> +			memset(histo, 0, sizeof(*histo));
> +		}
> +		goto out;
> +	}
> +
> +	for (i = 0; i < MEMCG_NUM_HISTO_BUCKETS - 1; i++, buffer = end) {
> +		while ((isspace(*buffer)))
> +			buffer++;
> +		data[i] = simple_strtoull(buffer, &end, 10) * 1000;
> +	}
> +	data[i] = ULLONG_MAX;
> +
> +	for (i = 1; i < MEMCG_NUM_HISTO_BUCKETS; i++)
> +		if (data[i] < data[i - 1])
> +			return -EINVAL;
> +
> +	memcpy(mem_cont->memcg_histo_range, data, sizeof(data));
> +	for_each_present_cpu(i) {
> +		struct memcg_histo *histo;
> +		histo = per_cpu_ptr(mem_cont->memcg_histo, i);
> +		memset(histo->count, 0, sizeof(*histo));
> +	}
> +out:
> +	return 0;
> +}
> +
> +/*
> + * Record values into histogram buckets
> + */
> +void memcg_histogram_record(struct task_struct *tsk, u64 delta)
> +{
> +	u64 *base;
> +	int index, first, last;
> +	struct memcg_histo *histo;
> +	struct mem_cgroup *mem = mem_cgroup_from_task(tsk);
> +
> +	first = 0;
> +	last = MEMCG_NUM_HISTO_BUCKETS - 1;
> +	base = mem->memcg_histo_range;
> +
> +	if (delta >= base[first]) {
> +		while (first < last) {
> +			index = (first + last) / 2;
> +			if (delta >= base[index])
> +				first = index + 1;
> +			else
> +				last = index;
> +		}
> +	}
> +	index = first;
> +
> +	histo = per_cpu_ptr(mem->memcg_histo, smp_processor_id());
> +	histo->count[index]++;
> +}
> +
>  static struct cftype mem_cgroup_files[] = {
>  	{
>  		.name = "usage_in_bytes",
> @@ -4642,6 +4755,12 @@ static struct cftype mem_cgroup_files[] = {
>  		.open = mem_control_numa_stat_open,
>  	},
>  #endif
> +	{
> +		.name = "pgfault_histogram",
> +		.read_seq_string = mem_cgroup_histogram_seq_read,
> +		.write_string = mem_cgroup_histogram_seq_write,
> +		.max_write_len = 21 * MEMCG_NUM_HISTO_BUCKETS,
> +	},
>  };
>  
>  #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
> @@ -4774,6 +4893,7 @@ static void __mem_cgroup_free(struct mem_cgroup *mem)
>  		free_mem_cgroup_per_zone_info(mem, node);
>  
>  	free_percpu(mem->stat);
> +	free_percpu(mem->memcg_histo);
>  	if (sizeof(struct mem_cgroup) < PAGE_SIZE)
>  		kfree(mem);
>  	else
> @@ -4853,6 +4973,7 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont)
>  	struct mem_cgroup *mem, *parent;
>  	long error = -ENOMEM;
>  	int node;
> +	int i;
>  
>  	mem = mem_cgroup_alloc();
>  	if (!mem)
> @@ -4905,6 +5026,15 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont)
>  	atomic_set(&mem->refcnt, 1);
>  	mem->move_charge_at_immigrate = 0;
>  	mutex_init(&mem->thresholds_lock);
> +
> +	mem->memcg_histo = alloc_percpu(typeof(*mem->memcg_histo));
> +	if (!mem->memcg_histo)
> +		goto free_out;
> +
> +	for (i = 0; i < MEMCG_NUM_HISTO_BUCKETS - 1; i++)
> +		mem->memcg_histo_range[i] = (i + 3) * 50000ULL;
> +	mem->memcg_histo_range[i] = ULLONG_MAX;
> +
>  	return &mem->css;
>  free_out:
>  	__mem_cgroup_free(mem);
> -- 
> 1.7.3.1
> 
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
> Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>
> 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>


[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]