diff mbox

PM: trace events for suspend/resume

Message ID 20140519230226.GA14382@linux.intel.com (mailing list archive)
State Changes Requested, archived
Headers show

Commit Message

Todd Brandt May 19, 2014, 11:02 p.m. UTC
Adds trace events that give finer resolution into suspend/resume. These
events are graphed in the timelines generated by the analyze_suspend.py
script. They represent large areas of time consumed that are typical in
suspend and resume.

The event is triggered by calling the function "trace_suspend_resume"
with two arguments: a string (the name of the event to be displayed
in the timeline), and a boolean (where true is used to denote the start
of the timeline event, and false to denote the end).

Signed-off-by: Todd Brandt <todd.e.brandt@intel.com>
---
 drivers/acpi/nvs.c           |  6 ++++++
 drivers/acpi/osl.c           |  3 +++
 drivers/base/syscore.c       |  5 +++++
 include/trace/events/power.h | 19 +++++++++++++++++++
 kernel/cpu.c                 |  9 +++++++++
 kernel/power/process.c       |  3 +++
 kernel/power/suspend.c       |  4 ++++
 7 files changed, 49 insertions(+)

For more info see:
https://01.org/suspendresume/blogs/tebrandt/2014/custom-timeline-event-support-0

I'm also interested to know if any tools depend on the machine_suspend
tracepoints, as this patch expands on the same info. I'd be willing to
merge the existing machine_suspend calls into this patch and rename
everything to suspend_resume provided it doesn't hurt any downstream tools.

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Rafael J. Wysocki May 22, 2014, 12:01 a.m. UTC | #1
On Monday, May 19, 2014 04:02:26 PM Todd E Brandt wrote:
> Adds trace events that give finer resolution into suspend/resume. These
> events are graphed in the timelines generated by the analyze_suspend.py
> script. They represent large areas of time consumed that are typical in
> suspend and resume.
> 
> The event is triggered by calling the function "trace_suspend_resume"
> with two arguments: a string (the name of the event to be displayed
> in the timeline), and a boolean (where true is used to denote the start
> of the timeline event, and false to denote the end).
> 
> Signed-off-by: Todd Brandt <todd.e.brandt@intel.com>
> ---
>  drivers/acpi/nvs.c           |  6 ++++++
>  drivers/acpi/osl.c           |  3 +++
>  drivers/base/syscore.c       |  5 +++++
>  include/trace/events/power.h | 19 +++++++++++++++++++
>  kernel/cpu.c                 |  9 +++++++++
>  kernel/power/process.c       |  3 +++
>  kernel/power/suspend.c       |  4 ++++
>  7 files changed, 49 insertions(+)
> 
> For more info see:
> https://01.org/suspendresume/blogs/tebrandt/2014/custom-timeline-event-support-0
> 
> I'm also interested to know if any tools depend on the machine_suspend
> tracepoints, as this patch expands on the same info. I'd be willing to
> merge the existing machine_suspend calls into this patch and rename
> everything to suspend_resume provided it doesn't hurt any downstream tools.
> 
> diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
> index de4fe03..b52b686 100644
> --- a/drivers/acpi/nvs.c
> +++ b/drivers/acpi/nvs.c
> @@ -12,6 +12,7 @@
>  #include <linux/mm.h>
>  #include <linux/slab.h>
>  #include <linux/acpi.h>
> +#include <trace/events/power.h>
>  
>  #include "internal.h"
>  
> @@ -171,6 +172,7 @@ int suspend_nvs_save(void)
>  {
>  	struct nvs_page *entry;
>  
> +	trace_suspend_resume("save_nvs_memory", true);
>  	printk(KERN_INFO "PM: Saving platform NVS memory\n");
>  
>  	list_for_each_entry(entry, &nvs_list, node)
> @@ -185,11 +187,13 @@ int suspend_nvs_save(void)
>  			}
>  			if (!entry->kaddr) {
>  				suspend_nvs_free();
> +				trace_suspend_resume("save_nvs_memory", false);
>  				return -ENOMEM;
>  			}
>  			memcpy(entry->data, entry->kaddr, entry->size);
>  		}
>  
> +	trace_suspend_resume("save_nvs_memory", false);
>  	return 0;
>  }
>  
> @@ -203,10 +207,12 @@ void suspend_nvs_restore(void)
>  {
>  	struct nvs_page *entry;
>  
> +	trace_suspend_resume("restore_nvs_memory", true);
>  	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
>  
>  	list_for_each_entry(entry, &nvs_list, node)
>  		if (entry->data)
>  			memcpy(entry->kaddr, entry->data, entry->size);
> +	trace_suspend_resume("restore_nvs_memory", false);
>  }
>  #endif
> diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
> index 6776c59..b65a77a 100644
> --- a/drivers/acpi/osl.c
> +++ b/drivers/acpi/osl.c
> @@ -44,6 +44,7 @@
>  #include <linux/list.h>
>  #include <linux/jiffies.h>
>  #include <linux/semaphore.h>
> +#include <trace/events/power.h>
>  
>  #include <asm/io.h>
>  #include <asm/uaccess.h>
> @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
>  
>  void acpi_os_sleep(u64 ms)
>  {
> +	trace_suspend_resume("acpi_os_sleep", true);
>  	msleep(ms);
> +	trace_suspend_resume("acpi_os_sleep", false);
>  }

This function doesn't have anything to do with system suspend/resume.
It is for waiting. :-)

The rest of the patch looks OK to me.

Steven, what about the tracing angle?

>  
>  void acpi_os_stall(u32 us)
> diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
> index e8d11b6..c2fd5d4 100644
> --- a/drivers/base/syscore.c
> +++ b/drivers/base/syscore.c
> @@ -10,6 +10,7 @@
>  #include <linux/mutex.h>
>  #include <linux/module.h>
>  #include <linux/interrupt.h>
> +#include <trace/events/power.h>
>  
>  static LIST_HEAD(syscore_ops_list);
>  static DEFINE_MUTEX(syscore_ops_lock);
> @@ -49,6 +50,7 @@ int syscore_suspend(void)
>  	struct syscore_ops *ops;
>  	int ret = 0;
>  
> +	trace_suspend_resume("syscore_suspend", true);
>  	pr_debug("Checking wakeup interrupts\n");
>  
>  	/* Return error code if there are any wakeup interrupts pending. */
> @@ -70,6 +72,7 @@ int syscore_suspend(void)
>  				"Interrupts enabled after %pF\n", ops->suspend);
>  		}
>  
> +	trace_suspend_resume("syscore_suspend", false);
>  	return 0;
>  
>   err_out:
> @@ -92,6 +95,7 @@ void syscore_resume(void)
>  {
>  	struct syscore_ops *ops;
>  
> +	trace_suspend_resume("syscore_resume", true);
>  	WARN_ONCE(!irqs_disabled(),
>  		"Interrupts enabled before system core resume.\n");
>  
> @@ -103,6 +107,7 @@ void syscore_resume(void)
>  			WARN_ONCE(!irqs_disabled(),
>  				"Interrupts enabled after %pF\n", ops->resume);
>  		}
> +	trace_suspend_resume("syscore_resume", false);
>  }
>  EXPORT_SYMBOL_GPL(syscore_resume);
>  #endif /* CONFIG_PM_SLEEP */
> diff --git a/include/trace/events/power.h b/include/trace/events/power.h
> index 9a7e08d..1d9fd76 100644
> --- a/include/trace/events/power.h
> +++ b/include/trace/events/power.h
> @@ -151,6 +151,25 @@ TRACE_EVENT(device_pm_report_time,
>  		__entry->ops_time, __entry->error)
>  );
>  
> +TRACE_EVENT(suspend_resume,
> +
> +	TP_PROTO(char *action, bool start),
> +
> +	TP_ARGS(action, start),
> +
> +	TP_STRUCT__entry(
> +		__string(action, action)
> +		__field(bool, start)
> +	),
> +
> +	TP_fast_assign(
> +		__assign_str(action, action);
> +		__entry->start = start;
> +	),
> +
> +	TP_printk("%s %s", __get_str(action), (__entry->start)?"begin":"end")
> +);
> +
>  DECLARE_EVENT_CLASS(wakeup_source,
>  
>  	TP_PROTO(const char *name, unsigned int state),
> diff --git a/kernel/cpu.c b/kernel/cpu.c
> index a9e710e..7aa30ec 100644
> --- a/kernel/cpu.c
> +++ b/kernel/cpu.c
> @@ -20,6 +20,7 @@
>  #include <linux/gfp.h>
>  #include <linux/suspend.h>
>  #include <linux/lockdep.h>
> +#include <trace/events/power.h>
>  
>  #include "smpboot.h"
>  
> @@ -509,6 +510,7 @@ static cpumask_var_t frozen_cpus;
>  int disable_nonboot_cpus(void)
>  {
>  	int cpu, first_cpu, error = 0;
> +	char buf[12];
>  
>  	cpu_maps_update_begin();
>  	first_cpu = cpumask_first(cpu_online_mask);
> @@ -522,7 +524,10 @@ int disable_nonboot_cpus(void)
>  	for_each_online_cpu(cpu) {
>  		if (cpu == first_cpu)
>  			continue;
> +		sprintf(buf, "CPU%u_OFF", cpu);
> +		trace_suspend_resume(buf, true);
>  		error = _cpu_down(cpu, 1);
> +		trace_suspend_resume(buf, false);
>  		if (!error)
>  			cpumask_set_cpu(cpu, frozen_cpus);
>  		else {
> @@ -554,6 +559,7 @@ void __weak arch_enable_nonboot_cpus_end(void)
>  void __ref enable_nonboot_cpus(void)
>  {
>  	int cpu, error;
> +	char buf[10];
>  
>  	/* Allow everyone to use the CPU hotplug again */
>  	cpu_maps_update_begin();
> @@ -566,7 +572,10 @@ void __ref enable_nonboot_cpus(void)
>  	arch_enable_nonboot_cpus_begin();
>  
>  	for_each_cpu(cpu, frozen_cpus) {
> +		sprintf(buf, "CPU%u_ON", cpu);
> +		trace_suspend_resume(buf, true);
>  		error = _cpu_up(cpu, 1);
> +		trace_suspend_resume(buf, false);
>  		if (!error) {
>  			printk(KERN_INFO "CPU%d is up\n", cpu);
>  			continue;
> diff --git a/kernel/power/process.c b/kernel/power/process.c
> index 06ec886..4aec2d9 100644
> --- a/kernel/power/process.c
> +++ b/kernel/power/process.c
> @@ -17,6 +17,7 @@
>  #include <linux/delay.h>
>  #include <linux/workqueue.h>
>  #include <linux/kmod.h>
> +#include <trace/events/power.h>
>  
>  /* 
>   * Timeout for stopping processes
> @@ -175,6 +176,7 @@ void thaw_processes(void)
>  	struct task_struct *g, *p;
>  	struct task_struct *curr = current;
>  
> +	trace_suspend_resume("thaw_processes", true);
>  	if (pm_freezing)
>  		atomic_dec(&system_freezing_cnt);
>  	pm_freezing = false;
> @@ -201,6 +203,7 @@ void thaw_processes(void)
>  
>  	schedule();
>  	printk("done.\n");
> +	trace_suspend_resume("thaw_processes", false);
>  }
>  
>  void thaw_kernel_threads(void)
> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> index 8233cd4..c7e3e9f 100644
> --- a/kernel/power/suspend.c
> +++ b/kernel/power/suspend.c
> @@ -147,7 +147,9 @@ static int suspend_prepare(suspend_state_t state)
>  	if (error)
>  		goto Finish;
>  
> +	trace_suspend_resume("freeze_processes", true);
>  	error = suspend_freeze_processes();
> +	trace_suspend_resume("freeze_processes", false);
>  	if (!error)
>  		return 0;
>  
> @@ -337,9 +339,11 @@ static int enter_state(suspend_state_t state)
>  	if (state == PM_SUSPEND_FREEZE)
>  		freeze_begin();
>  
> +	trace_suspend_resume("sync_filesystems", true);
>  	printk(KERN_INFO "PM: Syncing filesystems ... ");
>  	sys_sync();
>  	printk("done.\n");
> +	trace_suspend_resume("sync_filesystems", false);
>  
>  	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
>  	error = suspend_prepare(state);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Todd Brandt May 22, 2014, 5:07 p.m. UTC | #2
On Thu, May 22, 2014 at 02:01:44AM +0200, Rafael J. Wysocki wrote:
> On Monday, May 19, 2014 04:02:26 PM Todd E Brandt wrote:
> > Adds trace events that give finer resolution into suspend/resume. These
> > events are graphed in the timelines generated by the analyze_suspend.py
> > script. They represent large areas of time consumed that are typical in
> > suspend and resume.
> > 
> > The event is triggered by calling the function "trace_suspend_resume"
> > with two arguments: a string (the name of the event to be displayed
> > in the timeline), and a boolean (where true is used to denote the start
> > of the timeline event, and false to denote the end).
> > 
> > Signed-off-by: Todd Brandt <todd.e.brandt@intel.com>
> > ---
> >  drivers/acpi/nvs.c           |  6 ++++++
> >  drivers/acpi/osl.c           |  3 +++
> >  drivers/base/syscore.c       |  5 +++++
> >  include/trace/events/power.h | 19 +++++++++++++++++++
> >  kernel/cpu.c                 |  9 +++++++++
> >  kernel/power/process.c       |  3 +++
> >  kernel/power/suspend.c       |  4 ++++
> >  7 files changed, 49 insertions(+)
> > 
> > For more info see:
> > https://01.org/suspendresume/blogs/tebrandt/2014/custom-timeline-event-support-0
> > 
> > I'm also interested to know if any tools depend on the machine_suspend
> > tracepoints, as this patch expands on the same info. I'd be willing to
> > merge the existing machine_suspend calls into this patch and rename
> > everything to suspend_resume provided it doesn't hurt any downstream tools.
> > 
> > diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
> > index de4fe03..b52b686 100644
> > --- a/drivers/acpi/nvs.c
> > +++ b/drivers/acpi/nvs.c
> > @@ -12,6 +12,7 @@
> >  #include <linux/mm.h>
> >  #include <linux/slab.h>
> >  #include <linux/acpi.h>
> > +#include <trace/events/power.h>
> >  
> >  #include "internal.h"
> >  
> > @@ -171,6 +172,7 @@ int suspend_nvs_save(void)
> >  {
> >  	struct nvs_page *entry;
> >  
> > +	trace_suspend_resume("save_nvs_memory", true);
> >  	printk(KERN_INFO "PM: Saving platform NVS memory\n");
> >  
> >  	list_for_each_entry(entry, &nvs_list, node)
> > @@ -185,11 +187,13 @@ int suspend_nvs_save(void)
> >  			}
> >  			if (!entry->kaddr) {
> >  				suspend_nvs_free();
> > +				trace_suspend_resume("save_nvs_memory", false);
> >  				return -ENOMEM;
> >  			}
> >  			memcpy(entry->data, entry->kaddr, entry->size);
> >  		}
> >  
> > +	trace_suspend_resume("save_nvs_memory", false);
> >  	return 0;
> >  }
> >  
> > @@ -203,10 +207,12 @@ void suspend_nvs_restore(void)
> >  {
> >  	struct nvs_page *entry;
> >  
> > +	trace_suspend_resume("restore_nvs_memory", true);
> >  	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
> >  
> >  	list_for_each_entry(entry, &nvs_list, node)
> >  		if (entry->data)
> >  			memcpy(entry->kaddr, entry->data, entry->size);
> > +	trace_suspend_resume("restore_nvs_memory", false);
> >  }
> >  #endif
> > diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
> > index 6776c59..b65a77a 100644
> > --- a/drivers/acpi/osl.c
> > +++ b/drivers/acpi/osl.c
> > @@ -44,6 +44,7 @@
> >  #include <linux/list.h>
> >  #include <linux/jiffies.h>
> >  #include <linux/semaphore.h>
> > +#include <trace/events/power.h>
> >  
> >  #include <asm/io.h>
> >  #include <asm/uaccess.h>
> > @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
> >  
> >  void acpi_os_sleep(u64 ms)
> >  {
> > +	trace_suspend_resume("acpi_os_sleep", true);
> >  	msleep(ms);
> > +	trace_suspend_resume("acpi_os_sleep", false);
> >  }
> 
> This function doesn't have anything to do with system suspend/resume.
> It is for waiting. :-)

Yea but it really helps you diagnose bad firmware during suspend/resume
(for instance when there's just an arbitrary sleep 100ms in the ACPI code).
But it doesn't matter for general performance testing, I'll remove it on v2
submit.

> 
> The rest of the patch looks OK to me.
> 
> Steven, what about the tracing angle?
> 
> >  
> >  void acpi_os_stall(u32 us)
> > diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
> > index e8d11b6..c2fd5d4 100644
> > --- a/drivers/base/syscore.c
> > +++ b/drivers/base/syscore.c
> > @@ -10,6 +10,7 @@
> >  #include <linux/mutex.h>
> >  #include <linux/module.h>
> >  #include <linux/interrupt.h>
> > +#include <trace/events/power.h>
> >  
> >  static LIST_HEAD(syscore_ops_list);
> >  static DEFINE_MUTEX(syscore_ops_lock);
> > @@ -49,6 +50,7 @@ int syscore_suspend(void)
> >  	struct syscore_ops *ops;
> >  	int ret = 0;
> >  
> > +	trace_suspend_resume("syscore_suspend", true);
> >  	pr_debug("Checking wakeup interrupts\n");
> >  
> >  	/* Return error code if there are any wakeup interrupts pending. */
> > @@ -70,6 +72,7 @@ int syscore_suspend(void)
> >  				"Interrupts enabled after %pF\n", ops->suspend);
> >  		}
> >  
> > +	trace_suspend_resume("syscore_suspend", false);
> >  	return 0;
> >  
> >   err_out:
> > @@ -92,6 +95,7 @@ void syscore_resume(void)
> >  {
> >  	struct syscore_ops *ops;
> >  
> > +	trace_suspend_resume("syscore_resume", true);
> >  	WARN_ONCE(!irqs_disabled(),
> >  		"Interrupts enabled before system core resume.\n");
> >  
> > @@ -103,6 +107,7 @@ void syscore_resume(void)
> >  			WARN_ONCE(!irqs_disabled(),
> >  				"Interrupts enabled after %pF\n", ops->resume);
> >  		}
> > +	trace_suspend_resume("syscore_resume", false);
> >  }
> >  EXPORT_SYMBOL_GPL(syscore_resume);
> >  #endif /* CONFIG_PM_SLEEP */
> > diff --git a/include/trace/events/power.h b/include/trace/events/power.h
> > index 9a7e08d..1d9fd76 100644
> > --- a/include/trace/events/power.h
> > +++ b/include/trace/events/power.h
> > @@ -151,6 +151,25 @@ TRACE_EVENT(device_pm_report_time,
> >  		__entry->ops_time, __entry->error)
> >  );
> >  
> > +TRACE_EVENT(suspend_resume,
> > +
> > +	TP_PROTO(char *action, bool start),
> > +
> > +	TP_ARGS(action, start),
> > +
> > +	TP_STRUCT__entry(
> > +		__string(action, action)
> > +		__field(bool, start)
> > +	),
> > +
> > +	TP_fast_assign(
> > +		__assign_str(action, action);
> > +		__entry->start = start;
> > +	),
> > +
> > +	TP_printk("%s %s", __get_str(action), (__entry->start)?"begin":"end")
> > +);
> > +
> >  DECLARE_EVENT_CLASS(wakeup_source,
> >  
> >  	TP_PROTO(const char *name, unsigned int state),
> > diff --git a/kernel/cpu.c b/kernel/cpu.c
> > index a9e710e..7aa30ec 100644
> > --- a/kernel/cpu.c
> > +++ b/kernel/cpu.c
> > @@ -20,6 +20,7 @@
> >  #include <linux/gfp.h>
> >  #include <linux/suspend.h>
> >  #include <linux/lockdep.h>
> > +#include <trace/events/power.h>
> >  
> >  #include "smpboot.h"
> >  
> > @@ -509,6 +510,7 @@ static cpumask_var_t frozen_cpus;
> >  int disable_nonboot_cpus(void)
> >  {
> >  	int cpu, first_cpu, error = 0;
> > +	char buf[12];
> >  
> >  	cpu_maps_update_begin();
> >  	first_cpu = cpumask_first(cpu_online_mask);
> > @@ -522,7 +524,10 @@ int disable_nonboot_cpus(void)
> >  	for_each_online_cpu(cpu) {
> >  		if (cpu == first_cpu)
> >  			continue;
> > +		sprintf(buf, "CPU%u_OFF", cpu);
> > +		trace_suspend_resume(buf, true);
> >  		error = _cpu_down(cpu, 1);
> > +		trace_suspend_resume(buf, false);
> >  		if (!error)
> >  			cpumask_set_cpu(cpu, frozen_cpus);
> >  		else {
> > @@ -554,6 +559,7 @@ void __weak arch_enable_nonboot_cpus_end(void)
> >  void __ref enable_nonboot_cpus(void)
> >  {
> >  	int cpu, error;
> > +	char buf[10];
> >  
> >  	/* Allow everyone to use the CPU hotplug again */
> >  	cpu_maps_update_begin();
> > @@ -566,7 +572,10 @@ void __ref enable_nonboot_cpus(void)
> >  	arch_enable_nonboot_cpus_begin();
> >  
> >  	for_each_cpu(cpu, frozen_cpus) {
> > +		sprintf(buf, "CPU%u_ON", cpu);
> > +		trace_suspend_resume(buf, true);
> >  		error = _cpu_up(cpu, 1);
> > +		trace_suspend_resume(buf, false);
> >  		if (!error) {
> >  			printk(KERN_INFO "CPU%d is up\n", cpu);
> >  			continue;
> > diff --git a/kernel/power/process.c b/kernel/power/process.c
> > index 06ec886..4aec2d9 100644
> > --- a/kernel/power/process.c
> > +++ b/kernel/power/process.c
> > @@ -17,6 +17,7 @@
> >  #include <linux/delay.h>
> >  #include <linux/workqueue.h>
> >  #include <linux/kmod.h>
> > +#include <trace/events/power.h>
> >  
> >  /* 
> >   * Timeout for stopping processes
> > @@ -175,6 +176,7 @@ void thaw_processes(void)
> >  	struct task_struct *g, *p;
> >  	struct task_struct *curr = current;
> >  
> > +	trace_suspend_resume("thaw_processes", true);
> >  	if (pm_freezing)
> >  		atomic_dec(&system_freezing_cnt);
> >  	pm_freezing = false;
> > @@ -201,6 +203,7 @@ void thaw_processes(void)
> >  
> >  	schedule();
> >  	printk("done.\n");
> > +	trace_suspend_resume("thaw_processes", false);
> >  }
> >  
> >  void thaw_kernel_threads(void)
> > diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> > index 8233cd4..c7e3e9f 100644
> > --- a/kernel/power/suspend.c
> > +++ b/kernel/power/suspend.c
> > @@ -147,7 +147,9 @@ static int suspend_prepare(suspend_state_t state)
> >  	if (error)
> >  		goto Finish;
> >  
> > +	trace_suspend_resume("freeze_processes", true);
> >  	error = suspend_freeze_processes();
> > +	trace_suspend_resume("freeze_processes", false);
> >  	if (!error)
> >  		return 0;
> >  
> > @@ -337,9 +339,11 @@ static int enter_state(suspend_state_t state)
> >  	if (state == PM_SUSPEND_FREEZE)
> >  		freeze_begin();
> >  
> > +	trace_suspend_resume("sync_filesystems", true);
> >  	printk(KERN_INFO "PM: Syncing filesystems ... ");
> >  	sys_sync();
> >  	printk("done.\n");
> > +	trace_suspend_resume("sync_filesystems", false);
> >  
> >  	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
> >  	error = suspend_prepare(state);
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> -- 
> I speak only for myself.
> Rafael J. Wysocki, Intel Open Source Technology Center.
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Srivatsa S. Bhat May 22, 2014, 10:16 p.m. UTC | #3
On 05/22/2014 05:31 AM, Rafael J. Wysocki wrote:
> On Monday, May 19, 2014 04:02:26 PM Todd E Brandt wrote:
>> Adds trace events that give finer resolution into suspend/resume. These
>> events are graphed in the timelines generated by the analyze_suspend.py
>> script. They represent large areas of time consumed that are typical in
>> suspend and resume.
>>
>> The event is triggered by calling the function "trace_suspend_resume"
>> with two arguments: a string (the name of the event to be displayed
>> in the timeline), and a boolean (where true is used to denote the start
>> of the timeline event, and false to denote the end).
>>
>> Signed-off-by: Todd Brandt <todd.e.brandt@intel.com>
>> ---
>>  drivers/acpi/nvs.c           |  6 ++++++
>>  drivers/acpi/osl.c           |  3 +++
>>  drivers/base/syscore.c       |  5 +++++
>>  include/trace/events/power.h | 19 +++++++++++++++++++
>>  kernel/cpu.c                 |  9 +++++++++
>>  kernel/power/process.c       |  3 +++
>>  kernel/power/suspend.c       |  4 ++++
>>  7 files changed, 49 insertions(+)
>>
>> For more info see:
>> https://01.org/suspendresume/blogs/tebrandt/2014/custom-timeline-event-support-0
>>
>> I'm also interested to know if any tools depend on the machine_suspend
>> tracepoints, as this patch expands on the same info. I'd be willing to
>> merge the existing machine_suspend calls into this patch and rename
>> everything to suspend_resume provided it doesn't hurt any downstream tools.
>>
>> diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
>> index de4fe03..b52b686 100644
>> --- a/drivers/acpi/nvs.c
>> +++ b/drivers/acpi/nvs.c
>> @@ -12,6 +12,7 @@
>>  #include <linux/mm.h>
>>  #include <linux/slab.h>
>>  #include <linux/acpi.h>
>> +#include <trace/events/power.h>
>>  
>>  #include "internal.h"
>>  
>> @@ -171,6 +172,7 @@ int suspend_nvs_save(void)
>>  {
>>  	struct nvs_page *entry;
>>  
>> +	trace_suspend_resume("save_nvs_memory", true);
>>  	printk(KERN_INFO "PM: Saving platform NVS memory\n");
>>  
>>  	list_for_each_entry(entry, &nvs_list, node)
>> @@ -185,11 +187,13 @@ int suspend_nvs_save(void)
>>  			}
>>  			if (!entry->kaddr) {
>>  				suspend_nvs_free();
>> +				trace_suspend_resume("save_nvs_memory", false);
>>  				return -ENOMEM;
>>  			}
>>  			memcpy(entry->data, entry->kaddr, entry->size);
>>  		}
>>  
>> +	trace_suspend_resume("save_nvs_memory", false);
>>  	return 0;
>>  }
>>  
>> @@ -203,10 +207,12 @@ void suspend_nvs_restore(void)
>>  {
>>  	struct nvs_page *entry;
>>  
>> +	trace_suspend_resume("restore_nvs_memory", true);
>>  	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
>>  
>>  	list_for_each_entry(entry, &nvs_list, node)
>>  		if (entry->data)
>>  			memcpy(entry->kaddr, entry->data, entry->size);
>> +	trace_suspend_resume("restore_nvs_memory", false);
>>  }
>>  #endif
>> diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
>> index 6776c59..b65a77a 100644
>> --- a/drivers/acpi/osl.c
>> +++ b/drivers/acpi/osl.c
>> @@ -44,6 +44,7 @@
>>  #include <linux/list.h>
>>  #include <linux/jiffies.h>
>>  #include <linux/semaphore.h>
>> +#include <trace/events/power.h>
>>  
>>  #include <asm/io.h>
>>  #include <asm/uaccess.h>
>> @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
>>  
>>  void acpi_os_sleep(u64 ms)
>>  {
>> +	trace_suspend_resume("acpi_os_sleep", true);
>>  	msleep(ms);
>> +	trace_suspend_resume("acpi_os_sleep", false);
>>  }
> 
> This function doesn't have anything to do with system suspend/resume.
> It is for waiting. :-)
> 
> The rest of the patch looks OK to me.
> 
> Steven, what about the tracing angle?
> 

Hi Todd,

This might be a silly question, but the suspend_enter() function invokes
ftrace_stop() before suspending the machine, and restarts ftrace after
resume. And your patch seems to instrument code further down (deeper)
in the suspend/resume path (such as disable_nonboot_cpus() for example).
Doesn't the ftrace stop/start pose any problem for this patch?

Regards,
Srivatsa S. Bhat


>>  
>>  void acpi_os_stall(u32 us)
>> diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
>> index e8d11b6..c2fd5d4 100644
>> --- a/drivers/base/syscore.c
>> +++ b/drivers/base/syscore.c
>> @@ -10,6 +10,7 @@
>>  #include <linux/mutex.h>
>>  #include <linux/module.h>
>>  #include <linux/interrupt.h>
>> +#include <trace/events/power.h>
>>  
>>  static LIST_HEAD(syscore_ops_list);
>>  static DEFINE_MUTEX(syscore_ops_lock);
>> @@ -49,6 +50,7 @@ int syscore_suspend(void)
>>  	struct syscore_ops *ops;
>>  	int ret = 0;
>>  
>> +	trace_suspend_resume("syscore_suspend", true);
>>  	pr_debug("Checking wakeup interrupts\n");
>>  
>>  	/* Return error code if there are any wakeup interrupts pending. */
>> @@ -70,6 +72,7 @@ int syscore_suspend(void)
>>  				"Interrupts enabled after %pF\n", ops->suspend);
>>  		}
>>  
>> +	trace_suspend_resume("syscore_suspend", false);
>>  	return 0;
>>  
>>   err_out:
>> @@ -92,6 +95,7 @@ void syscore_resume(void)
>>  {
>>  	struct syscore_ops *ops;
>>  
>> +	trace_suspend_resume("syscore_resume", true);
>>  	WARN_ONCE(!irqs_disabled(),
>>  		"Interrupts enabled before system core resume.\n");
>>  
>> @@ -103,6 +107,7 @@ void syscore_resume(void)
>>  			WARN_ONCE(!irqs_disabled(),
>>  				"Interrupts enabled after %pF\n", ops->resume);
>>  		}
>> +	trace_suspend_resume("syscore_resume", false);
>>  }
>>  EXPORT_SYMBOL_GPL(syscore_resume);
>>  #endif /* CONFIG_PM_SLEEP */
>> diff --git a/include/trace/events/power.h b/include/trace/events/power.h
>> index 9a7e08d..1d9fd76 100644
>> --- a/include/trace/events/power.h
>> +++ b/include/trace/events/power.h
>> @@ -151,6 +151,25 @@ TRACE_EVENT(device_pm_report_time,
>>  		__entry->ops_time, __entry->error)
>>  );
>>  
>> +TRACE_EVENT(suspend_resume,
>> +
>> +	TP_PROTO(char *action, bool start),
>> +
>> +	TP_ARGS(action, start),
>> +
>> +	TP_STRUCT__entry(
>> +		__string(action, action)
>> +		__field(bool, start)
>> +	),
>> +
>> +	TP_fast_assign(
>> +		__assign_str(action, action);
>> +		__entry->start = start;
>> +	),
>> +
>> +	TP_printk("%s %s", __get_str(action), (__entry->start)?"begin":"end")
>> +);
>> +
>>  DECLARE_EVENT_CLASS(wakeup_source,
>>  
>>  	TP_PROTO(const char *name, unsigned int state),
>> diff --git a/kernel/cpu.c b/kernel/cpu.c
>> index a9e710e..7aa30ec 100644
>> --- a/kernel/cpu.c
>> +++ b/kernel/cpu.c
>> @@ -20,6 +20,7 @@
>>  #include <linux/gfp.h>
>>  #include <linux/suspend.h>
>>  #include <linux/lockdep.h>
>> +#include <trace/events/power.h>
>>  
>>  #include "smpboot.h"
>>  
>> @@ -509,6 +510,7 @@ static cpumask_var_t frozen_cpus;
>>  int disable_nonboot_cpus(void)
>>  {
>>  	int cpu, first_cpu, error = 0;
>> +	char buf[12];
>>  
>>  	cpu_maps_update_begin();
>>  	first_cpu = cpumask_first(cpu_online_mask);
>> @@ -522,7 +524,10 @@ int disable_nonboot_cpus(void)
>>  	for_each_online_cpu(cpu) {
>>  		if (cpu == first_cpu)
>>  			continue;
>> +		sprintf(buf, "CPU%u_OFF", cpu);
>> +		trace_suspend_resume(buf, true);
>>  		error = _cpu_down(cpu, 1);
>> +		trace_suspend_resume(buf, false);
>>  		if (!error)
>>  			cpumask_set_cpu(cpu, frozen_cpus);
>>  		else {
>> @@ -554,6 +559,7 @@ void __weak arch_enable_nonboot_cpus_end(void)
>>  void __ref enable_nonboot_cpus(void)
>>  {
>>  	int cpu, error;
>> +	char buf[10];
>>  
>>  	/* Allow everyone to use the CPU hotplug again */
>>  	cpu_maps_update_begin();
>> @@ -566,7 +572,10 @@ void __ref enable_nonboot_cpus(void)
>>  	arch_enable_nonboot_cpus_begin();
>>  
>>  	for_each_cpu(cpu, frozen_cpus) {
>> +		sprintf(buf, "CPU%u_ON", cpu);
>> +		trace_suspend_resume(buf, true);
>>  		error = _cpu_up(cpu, 1);
>> +		trace_suspend_resume(buf, false);
>>  		if (!error) {
>>  			printk(KERN_INFO "CPU%d is up\n", cpu);
>>  			continue;
>> diff --git a/kernel/power/process.c b/kernel/power/process.c
>> index 06ec886..4aec2d9 100644
>> --- a/kernel/power/process.c
>> +++ b/kernel/power/process.c
>> @@ -17,6 +17,7 @@
>>  #include <linux/delay.h>
>>  #include <linux/workqueue.h>
>>  #include <linux/kmod.h>
>> +#include <trace/events/power.h>
>>  
>>  /* 
>>   * Timeout for stopping processes
>> @@ -175,6 +176,7 @@ void thaw_processes(void)
>>  	struct task_struct *g, *p;
>>  	struct task_struct *curr = current;
>>  
>> +	trace_suspend_resume("thaw_processes", true);
>>  	if (pm_freezing)
>>  		atomic_dec(&system_freezing_cnt);
>>  	pm_freezing = false;
>> @@ -201,6 +203,7 @@ void thaw_processes(void)
>>  
>>  	schedule();
>>  	printk("done.\n");
>> +	trace_suspend_resume("thaw_processes", false);
>>  }
>>  
>>  void thaw_kernel_threads(void)
>> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
>> index 8233cd4..c7e3e9f 100644
>> --- a/kernel/power/suspend.c
>> +++ b/kernel/power/suspend.c
>> @@ -147,7 +147,9 @@ static int suspend_prepare(suspend_state_t state)
>>  	if (error)
>>  		goto Finish;
>>  
>> +	trace_suspend_resume("freeze_processes", true);
>>  	error = suspend_freeze_processes();
>> +	trace_suspend_resume("freeze_processes", false);
>>  	if (!error)
>>  		return 0;
>>  
>> @@ -337,9 +339,11 @@ static int enter_state(suspend_state_t state)
>>  	if (state == PM_SUSPEND_FREEZE)
>>  		freeze_begin();
>>  
>> +	trace_suspend_resume("sync_filesystems", true);
>>  	printk(KERN_INFO "PM: Syncing filesystems ... ");
>>  	sys_sync();
>>  	printk("done.\n");
>> +	trace_suspend_resume("sync_filesystems", false);
>>  
>>  	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
>>  	error = suspend_prepare(state);


--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Steven Rostedt May 22, 2014, 10:41 p.m. UTC | #4
On Thu, 2014-05-22 at 10:07 -0700, Todd E Brandt wrote:
>  
> > >  #include <asm/io.h>
> > >  #include <asm/uaccess.h>
> > > @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
> > >  
> > >  void acpi_os_sleep(u64 ms)
> > >  {
> > > +	trace_suspend_resume("acpi_os_sleep", true);
> > >  	msleep(ms);
> > > +	trace_suspend_resume("acpi_os_sleep", false);
> > >  }
> > 
> > This function doesn't have anything to do with system suspend/resume.
> > It is for waiting. :-)
> 
> Yea but it really helps you diagnose bad firmware during suspend/resume
> (for instance when there's just an arbitrary sleep 100ms in the ACPI code).
> But it doesn't matter for general performance testing, I'll remove it on v2
> submit.
> 
> > 
> > The rest of the patch looks OK to me.
> > 
> > Steven, what about the tracing angle?

It should be under an ACPI system (if there is one). Looks rather silly
to call it for suspend.

-- Steve

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Steven Rostedt May 22, 2014, 10:44 p.m. UTC | #5
On Fri, 2014-05-23 at 03:46 +0530, Srivatsa S. Bhat wrote:
> Hi Todd,
> 
> This might be a silly question, but the suspend_enter() function invokes
> ftrace_stop() before suspending the machine, and restarts ftrace after
> resume. And your patch seems to instrument code further down (deeper)
> in the suspend/resume path (such as disable_nonboot_cpus() for example).
> Doesn't the ftrace stop/start pose any problem for this patch?

ftrace_start/stop() only affects the function tracer and not the
tracepoints. Those will still be recorded.

Note, we have an invested interest in getting rid of
ftrace_stop/start(). I rather mark all the suspend code with notrace or
find another method to protect suspend from function tracing. The
ftrace_stop/start() was more of a big hammer approach and I had plans
for getting rid of it in the future. That future is approaching fast :-)

-- Steve


--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael J. Wysocki May 22, 2014, 11:06 p.m. UTC | #6
On Thursday, May 22, 2014 06:41:45 PM Steven Rostedt wrote:
> On Thu, 2014-05-22 at 10:07 -0700, Todd E Brandt wrote:
> >  
> > > >  #include <asm/io.h>
> > > >  #include <asm/uaccess.h>
> > > > @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
> > > >  
> > > >  void acpi_os_sleep(u64 ms)
> > > >  {
> > > > +	trace_suspend_resume("acpi_os_sleep", true);
> > > >  	msleep(ms);
> > > > +	trace_suspend_resume("acpi_os_sleep", false);
> > > >  }
> > > 
> > > This function doesn't have anything to do with system suspend/resume.
> > > It is for waiting. :-)
> > 
> > Yea but it really helps you diagnose bad firmware during suspend/resume
> > (for instance when there's just an arbitrary sleep 100ms in the ACPI code).
> > But it doesn't matter for general performance testing, I'll remove it on v2
> > submit.
> > 
> > > 
> > > The rest of the patch looks OK to me.
> > > 
> > > Steven, what about the tracing angle?
> 
> It should be under an ACPI system (if there is one). Looks rather silly
> to call it for suspend.

I meant the rest of the patch, not this particular hunk. :-)

Rafael

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Todd Brandt May 22, 2014, 11:50 p.m. UTC | #7
On Fri, May 23, 2014 at 03:46:54AM +0530, Srivatsa S. Bhat wrote:
> On 05/22/2014 05:31 AM, Rafael J. Wysocki wrote:
> > On Monday, May 19, 2014 04:02:26 PM Todd E Brandt wrote:
> >> Adds trace events that give finer resolution into suspend/resume. These
> >> events are graphed in the timelines generated by the analyze_suspend.py
> >> script. They represent large areas of time consumed that are typical in
> >> suspend and resume.
> >>
> >> The event is triggered by calling the function "trace_suspend_resume"
> >> with two arguments: a string (the name of the event to be displayed
> >> in the timeline), and a boolean (where true is used to denote the start
> >> of the timeline event, and false to denote the end).
> >>
> >> Signed-off-by: Todd Brandt <todd.e.brandt@intel.com>
> >> ---
> >>  drivers/acpi/nvs.c           |  6 ++++++
> >>  drivers/acpi/osl.c           |  3 +++
> >>  drivers/base/syscore.c       |  5 +++++
> >>  include/trace/events/power.h | 19 +++++++++++++++++++
> >>  kernel/cpu.c                 |  9 +++++++++
> >>  kernel/power/process.c       |  3 +++
> >>  kernel/power/suspend.c       |  4 ++++
> >>  7 files changed, 49 insertions(+)
> >>
> >> For more info see:
> >> https://01.org/suspendresume/blogs/tebrandt/2014/custom-timeline-event-support-0
> >>
> >> I'm also interested to know if any tools depend on the machine_suspend
> >> tracepoints, as this patch expands on the same info. I'd be willing to
> >> merge the existing machine_suspend calls into this patch and rename
> >> everything to suspend_resume provided it doesn't hurt any downstream tools.
> >>
> >> diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
> >> index de4fe03..b52b686 100644
> >> --- a/drivers/acpi/nvs.c
> >> +++ b/drivers/acpi/nvs.c
> >> @@ -12,6 +12,7 @@
> >>  #include <linux/mm.h>
> >>  #include <linux/slab.h>
> >>  #include <linux/acpi.h>
> >> +#include <trace/events/power.h>
> >>  
> >>  #include "internal.h"
> >>  
> >> @@ -171,6 +172,7 @@ int suspend_nvs_save(void)
> >>  {
> >>  	struct nvs_page *entry;
> >>  
> >> +	trace_suspend_resume("save_nvs_memory", true);
> >>  	printk(KERN_INFO "PM: Saving platform NVS memory\n");
> >>  
> >>  	list_for_each_entry(entry, &nvs_list, node)
> >> @@ -185,11 +187,13 @@ int suspend_nvs_save(void)
> >>  			}
> >>  			if (!entry->kaddr) {
> >>  				suspend_nvs_free();
> >> +				trace_suspend_resume("save_nvs_memory", false);
> >>  				return -ENOMEM;
> >>  			}
> >>  			memcpy(entry->data, entry->kaddr, entry->size);
> >>  		}
> >>  
> >> +	trace_suspend_resume("save_nvs_memory", false);
> >>  	return 0;
> >>  }
> >>  
> >> @@ -203,10 +207,12 @@ void suspend_nvs_restore(void)
> >>  {
> >>  	struct nvs_page *entry;
> >>  
> >> +	trace_suspend_resume("restore_nvs_memory", true);
> >>  	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
> >>  
> >>  	list_for_each_entry(entry, &nvs_list, node)
> >>  		if (entry->data)
> >>  			memcpy(entry->kaddr, entry->data, entry->size);
> >> +	trace_suspend_resume("restore_nvs_memory", false);
> >>  }
> >>  #endif
> >> diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
> >> index 6776c59..b65a77a 100644
> >> --- a/drivers/acpi/osl.c
> >> +++ b/drivers/acpi/osl.c
> >> @@ -44,6 +44,7 @@
> >>  #include <linux/list.h>
> >>  #include <linux/jiffies.h>
> >>  #include <linux/semaphore.h>
> >> +#include <trace/events/power.h>
> >>  
> >>  #include <asm/io.h>
> >>  #include <asm/uaccess.h>
> >> @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
> >>  
> >>  void acpi_os_sleep(u64 ms)
> >>  {
> >> +	trace_suspend_resume("acpi_os_sleep", true);
> >>  	msleep(ms);
> >> +	trace_suspend_resume("acpi_os_sleep", false);
> >>  }
> > 
> > This function doesn't have anything to do with system suspend/resume.
> > It is for waiting. :-)
> > 
> > The rest of the patch looks OK to me.
> > 
> > Steven, what about the tracing angle?
> > 
> 
> Hi Todd,
> 
> This might be a silly question, but the suspend_enter() function invokes
> ftrace_stop() before suspending the machine, and restarts ftrace after
> resume. And your patch seems to instrument code further down (deeper)
> in the suspend/resume path (such as disable_nonboot_cpus() for example).
> Doesn't the ftrace stop/start pose any problem for this patch?

I put a patch in the kernel a few months back that allowed ftrace to
function all the way through to disable/enable_nonboot_cpus. So the
actual trace code includes everything up to the actual CPU suspend/resume.
And also (as Steven mentioned), the tracepoint activity isn't controlled
via ftrace_start/ftrace_stop, those are for tracer based logging.

> 
> Regards,
> Srivatsa S. Bhat
> 
> 
> >>  
> >>  void acpi_os_stall(u32 us)
> >> diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
> >> index e8d11b6..c2fd5d4 100644
> >> --- a/drivers/base/syscore.c
> >> +++ b/drivers/base/syscore.c
> >> @@ -10,6 +10,7 @@
> >>  #include <linux/mutex.h>
> >>  #include <linux/module.h>
> >>  #include <linux/interrupt.h>
> >> +#include <trace/events/power.h>
> >>  
> >>  static LIST_HEAD(syscore_ops_list);
> >>  static DEFINE_MUTEX(syscore_ops_lock);
> >> @@ -49,6 +50,7 @@ int syscore_suspend(void)
> >>  	struct syscore_ops *ops;
> >>  	int ret = 0;
> >>  
> >> +	trace_suspend_resume("syscore_suspend", true);
> >>  	pr_debug("Checking wakeup interrupts\n");
> >>  
> >>  	/* Return error code if there are any wakeup interrupts pending. */
> >> @@ -70,6 +72,7 @@ int syscore_suspend(void)
> >>  				"Interrupts enabled after %pF\n", ops->suspend);
> >>  		}
> >>  
> >> +	trace_suspend_resume("syscore_suspend", false);
> >>  	return 0;
> >>  
> >>   err_out:
> >> @@ -92,6 +95,7 @@ void syscore_resume(void)
> >>  {
> >>  	struct syscore_ops *ops;
> >>  
> >> +	trace_suspend_resume("syscore_resume", true);
> >>  	WARN_ONCE(!irqs_disabled(),
> >>  		"Interrupts enabled before system core resume.\n");
> >>  
> >> @@ -103,6 +107,7 @@ void syscore_resume(void)
> >>  			WARN_ONCE(!irqs_disabled(),
> >>  				"Interrupts enabled after %pF\n", ops->resume);
> >>  		}
> >> +	trace_suspend_resume("syscore_resume", false);
> >>  }
> >>  EXPORT_SYMBOL_GPL(syscore_resume);
> >>  #endif /* CONFIG_PM_SLEEP */
> >> diff --git a/include/trace/events/power.h b/include/trace/events/power.h
> >> index 9a7e08d..1d9fd76 100644
> >> --- a/include/trace/events/power.h
> >> +++ b/include/trace/events/power.h
> >> @@ -151,6 +151,25 @@ TRACE_EVENT(device_pm_report_time,
> >>  		__entry->ops_time, __entry->error)
> >>  );
> >>  
> >> +TRACE_EVENT(suspend_resume,
> >> +
> >> +	TP_PROTO(char *action, bool start),
> >> +
> >> +	TP_ARGS(action, start),
> >> +
> >> +	TP_STRUCT__entry(
> >> +		__string(action, action)
> >> +		__field(bool, start)
> >> +	),
> >> +
> >> +	TP_fast_assign(
> >> +		__assign_str(action, action);
> >> +		__entry->start = start;
> >> +	),
> >> +
> >> +	TP_printk("%s %s", __get_str(action), (__entry->start)?"begin":"end")
> >> +);
> >> +
> >>  DECLARE_EVENT_CLASS(wakeup_source,
> >>  
> >>  	TP_PROTO(const char *name, unsigned int state),
> >> diff --git a/kernel/cpu.c b/kernel/cpu.c
> >> index a9e710e..7aa30ec 100644
> >> --- a/kernel/cpu.c
> >> +++ b/kernel/cpu.c
> >> @@ -20,6 +20,7 @@
> >>  #include <linux/gfp.h>
> >>  #include <linux/suspend.h>
> >>  #include <linux/lockdep.h>
> >> +#include <trace/events/power.h>
> >>  
> >>  #include "smpboot.h"
> >>  
> >> @@ -509,6 +510,7 @@ static cpumask_var_t frozen_cpus;
> >>  int disable_nonboot_cpus(void)
> >>  {
> >>  	int cpu, first_cpu, error = 0;
> >> +	char buf[12];
> >>  
> >>  	cpu_maps_update_begin();
> >>  	first_cpu = cpumask_first(cpu_online_mask);
> >> @@ -522,7 +524,10 @@ int disable_nonboot_cpus(void)
> >>  	for_each_online_cpu(cpu) {
> >>  		if (cpu == first_cpu)
> >>  			continue;
> >> +		sprintf(buf, "CPU%u_OFF", cpu);
> >> +		trace_suspend_resume(buf, true);
> >>  		error = _cpu_down(cpu, 1);
> >> +		trace_suspend_resume(buf, false);
> >>  		if (!error)
> >>  			cpumask_set_cpu(cpu, frozen_cpus);
> >>  		else {
> >> @@ -554,6 +559,7 @@ void __weak arch_enable_nonboot_cpus_end(void)
> >>  void __ref enable_nonboot_cpus(void)
> >>  {
> >>  	int cpu, error;
> >> +	char buf[10];
> >>  
> >>  	/* Allow everyone to use the CPU hotplug again */
> >>  	cpu_maps_update_begin();
> >> @@ -566,7 +572,10 @@ void __ref enable_nonboot_cpus(void)
> >>  	arch_enable_nonboot_cpus_begin();
> >>  
> >>  	for_each_cpu(cpu, frozen_cpus) {
> >> +		sprintf(buf, "CPU%u_ON", cpu);
> >> +		trace_suspend_resume(buf, true);
> >>  		error = _cpu_up(cpu, 1);
> >> +		trace_suspend_resume(buf, false);
> >>  		if (!error) {
> >>  			printk(KERN_INFO "CPU%d is up\n", cpu);
> >>  			continue;
> >> diff --git a/kernel/power/process.c b/kernel/power/process.c
> >> index 06ec886..4aec2d9 100644
> >> --- a/kernel/power/process.c
> >> +++ b/kernel/power/process.c
> >> @@ -17,6 +17,7 @@
> >>  #include <linux/delay.h>
> >>  #include <linux/workqueue.h>
> >>  #include <linux/kmod.h>
> >> +#include <trace/events/power.h>
> >>  
> >>  /* 
> >>   * Timeout for stopping processes
> >> @@ -175,6 +176,7 @@ void thaw_processes(void)
> >>  	struct task_struct *g, *p;
> >>  	struct task_struct *curr = current;
> >>  
> >> +	trace_suspend_resume("thaw_processes", true);
> >>  	if (pm_freezing)
> >>  		atomic_dec(&system_freezing_cnt);
> >>  	pm_freezing = false;
> >> @@ -201,6 +203,7 @@ void thaw_processes(void)
> >>  
> >>  	schedule();
> >>  	printk("done.\n");
> >> +	trace_suspend_resume("thaw_processes", false);
> >>  }
> >>  
> >>  void thaw_kernel_threads(void)
> >> diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
> >> index 8233cd4..c7e3e9f 100644
> >> --- a/kernel/power/suspend.c
> >> +++ b/kernel/power/suspend.c
> >> @@ -147,7 +147,9 @@ static int suspend_prepare(suspend_state_t state)
> >>  	if (error)
> >>  		goto Finish;
> >>  
> >> +	trace_suspend_resume("freeze_processes", true);
> >>  	error = suspend_freeze_processes();
> >> +	trace_suspend_resume("freeze_processes", false);
> >>  	if (!error)
> >>  		return 0;
> >>  
> >> @@ -337,9 +339,11 @@ static int enter_state(suspend_state_t state)
> >>  	if (state == PM_SUSPEND_FREEZE)
> >>  		freeze_begin();
> >>  
> >> +	trace_suspend_resume("sync_filesystems", true);
> >>  	printk(KERN_INFO "PM: Syncing filesystems ... ");
> >>  	sys_sync();
> >>  	printk("done.\n");
> >> +	trace_suspend_resume("sync_filesystems", false);
> >>  
> >>  	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
> >>  	error = suspend_prepare(state);
> 
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Srivatsa S. Bhat May 23, 2014, 6:36 a.m. UTC | #8
On 05/23/2014 04:14 AM, Steven Rostedt wrote:
> On Fri, 2014-05-23 at 03:46 +0530, Srivatsa S. Bhat wrote:
>> Hi Todd,
>>
>> This might be a silly question, but the suspend_enter() function invokes
>> ftrace_stop() before suspending the machine, and restarts ftrace after
>> resume. And your patch seems to instrument code further down (deeper)
>> in the suspend/resume path (such as disable_nonboot_cpus() for example).
>> Doesn't the ftrace stop/start pose any problem for this patch?
> 
> ftrace_start/stop() only affects the function tracer and not the
> tracepoints. Those will still be recorded.
> 

Ah, thank you for the clarification.

> Note, we have an invested interest in getting rid of
> ftrace_stop/start(). I rather mark all the suspend code with notrace or
> find another method to protect suspend from function tracing. The
> ftrace_stop/start() was more of a big hammer approach and I had plans
> for getting rid of it in the future. That future is approaching fast :-)
>

Ok.. :-)

 
Regards,
Srivatsa S. Bhat

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Srivatsa S. Bhat May 23, 2014, 6:37 a.m. UTC | #9
On 05/23/2014 05:20 AM, Todd E Brandt wrote:
> On Fri, May 23, 2014 at 03:46:54AM +0530, Srivatsa S. Bhat wrote:
>> On 05/22/2014 05:31 AM, Rafael J. Wysocki wrote:
>>> On Monday, May 19, 2014 04:02:26 PM Todd E Brandt wrote:
>>>> Adds trace events that give finer resolution into suspend/resume. These
>>>> events are graphed in the timelines generated by the analyze_suspend.py
>>>> script. They represent large areas of time consumed that are typical in
>>>> suspend and resume.
>>>>
>>>> The event is triggered by calling the function "trace_suspend_resume"
>>>> with two arguments: a string (the name of the event to be displayed
>>>> in the timeline), and a boolean (where true is used to denote the start
>>>> of the timeline event, and false to denote the end).
>>>>
>>>> Signed-off-by: Todd Brandt <todd.e.brandt@intel.com>
>>>> ---
>>>>  drivers/acpi/nvs.c           |  6 ++++++
>>>>  drivers/acpi/osl.c           |  3 +++
>>>>  drivers/base/syscore.c       |  5 +++++
>>>>  include/trace/events/power.h | 19 +++++++++++++++++++
>>>>  kernel/cpu.c                 |  9 +++++++++
>>>>  kernel/power/process.c       |  3 +++
>>>>  kernel/power/suspend.c       |  4 ++++
>>>>  7 files changed, 49 insertions(+)
>>>>
>>>> For more info see:
>>>> https://01.org/suspendresume/blogs/tebrandt/2014/custom-timeline-event-support-0
>>>>
>>>> I'm also interested to know if any tools depend on the machine_suspend
>>>> tracepoints, as this patch expands on the same info. I'd be willing to
>>>> merge the existing machine_suspend calls into this patch and rename
>>>> everything to suspend_resume provided it doesn't hurt any downstream tools.
>>>>
>>>> diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
>>>> index de4fe03..b52b686 100644
>>>> --- a/drivers/acpi/nvs.c
>>>> +++ b/drivers/acpi/nvs.c
>>>> @@ -12,6 +12,7 @@
>>>>  #include <linux/mm.h>
>>>>  #include <linux/slab.h>
>>>>  #include <linux/acpi.h>
>>>> +#include <trace/events/power.h>
>>>>  
>>>>  #include "internal.h"
>>>>  
>>>> @@ -171,6 +172,7 @@ int suspend_nvs_save(void)
>>>>  {
>>>>  	struct nvs_page *entry;
>>>>  
>>>> +	trace_suspend_resume("save_nvs_memory", true);
>>>>  	printk(KERN_INFO "PM: Saving platform NVS memory\n");
>>>>  
>>>>  	list_for_each_entry(entry, &nvs_list, node)
>>>> @@ -185,11 +187,13 @@ int suspend_nvs_save(void)
>>>>  			}
>>>>  			if (!entry->kaddr) {
>>>>  				suspend_nvs_free();
>>>> +				trace_suspend_resume("save_nvs_memory", false);
>>>>  				return -ENOMEM;
>>>>  			}
>>>>  			memcpy(entry->data, entry->kaddr, entry->size);
>>>>  		}
>>>>  
>>>> +	trace_suspend_resume("save_nvs_memory", false);
>>>>  	return 0;
>>>>  }
>>>>  
>>>> @@ -203,10 +207,12 @@ void suspend_nvs_restore(void)
>>>>  {
>>>>  	struct nvs_page *entry;
>>>>  
>>>> +	trace_suspend_resume("restore_nvs_memory", true);
>>>>  	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
>>>>  
>>>>  	list_for_each_entry(entry, &nvs_list, node)
>>>>  		if (entry->data)
>>>>  			memcpy(entry->kaddr, entry->data, entry->size);
>>>> +	trace_suspend_resume("restore_nvs_memory", false);
>>>>  }
>>>>  #endif
>>>> diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
>>>> index 6776c59..b65a77a 100644
>>>> --- a/drivers/acpi/osl.c
>>>> +++ b/drivers/acpi/osl.c
>>>> @@ -44,6 +44,7 @@
>>>>  #include <linux/list.h>
>>>>  #include <linux/jiffies.h>
>>>>  #include <linux/semaphore.h>
>>>> +#include <trace/events/power.h>
>>>>  
>>>>  #include <asm/io.h>
>>>>  #include <asm/uaccess.h>
>>>> @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
>>>>  
>>>>  void acpi_os_sleep(u64 ms)
>>>>  {
>>>> +	trace_suspend_resume("acpi_os_sleep", true);
>>>>  	msleep(ms);
>>>> +	trace_suspend_resume("acpi_os_sleep", false);
>>>>  }
>>>
>>> This function doesn't have anything to do with system suspend/resume.
>>> It is for waiting. :-)
>>>
>>> The rest of the patch looks OK to me.
>>>
>>> Steven, what about the tracing angle?
>>>
>>
>> Hi Todd,
>>
>> This might be a silly question, but the suspend_enter() function invokes
>> ftrace_stop() before suspending the machine, and restarts ftrace after
>> resume. And your patch seems to instrument code further down (deeper)
>> in the suspend/resume path (such as disable_nonboot_cpus() for example).
>> Doesn't the ftrace stop/start pose any problem for this patch?
> 
> I put a patch in the kernel a few months back that allowed ftrace to
> function all the way through to disable/enable_nonboot_cpus. So the
> actual trace code includes everything up to the actual CPU suspend/resume.
> And also (as Steven mentioned), the tracepoint activity isn't controlled
> via ftrace_start/ftrace_stop, those are for tracer based logging.
> 

Cool, thanks!

Regards,
Srivatsa S. Bhat

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Todd Brandt May 30, 2014, 2:49 p.m. UTC | #10
Hi guys, I'm not hearing any objections to revamping the machine_suspend trace
event, so I'll resubmit a patch with it removed and supplanted with the
new suspend_resume event. I've also discovered that this first patch doesn't
completely cover freeze, so I'm moving a couple of the calls around.

On Fri, May 23, 2014 at 01:06:32AM +0200, Rafael J. Wysocki wrote:
> On Thursday, May 22, 2014 06:41:45 PM Steven Rostedt wrote:
> > On Thu, 2014-05-22 at 10:07 -0700, Todd E Brandt wrote:
> > >  
> > > > >  #include <asm/io.h>
> > > > >  #include <asm/uaccess.h>
> > > > > @@ -835,7 +836,9 @@ acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
> > > > >  
> > > > >  void acpi_os_sleep(u64 ms)
> > > > >  {
> > > > > +	trace_suspend_resume("acpi_os_sleep", true);
> > > > >  	msleep(ms);
> > > > > +	trace_suspend_resume("acpi_os_sleep", false);
> > > > >  }
> > > > 
> > > > This function doesn't have anything to do with system suspend/resume.
> > > > It is for waiting. :-)
> > > 
> > > Yea but it really helps you diagnose bad firmware during suspend/resume
> > > (for instance when there's just an arbitrary sleep 100ms in the ACPI code).
> > > But it doesn't matter for general performance testing, I'll remove it on v2
> > > submit.
> > > 
> > > > 
> > > > The rest of the patch looks OK to me.
> > > > 
> > > > Steven, what about the tracing angle?
> > 
> > It should be under an ACPI system (if there is one). Looks rather silly
> > to call it for suspend.
> 
> I meant the rest of the patch, not this particular hunk. :-)
> 
> Rafael
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c
index de4fe03..b52b686 100644
--- a/drivers/acpi/nvs.c
+++ b/drivers/acpi/nvs.c
@@ -12,6 +12,7 @@ 
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
+#include <trace/events/power.h>
 
 #include "internal.h"
 
@@ -171,6 +172,7 @@  int suspend_nvs_save(void)
 {
 	struct nvs_page *entry;
 
+	trace_suspend_resume("save_nvs_memory", true);
 	printk(KERN_INFO "PM: Saving platform NVS memory\n");
 
 	list_for_each_entry(entry, &nvs_list, node)
@@ -185,11 +187,13 @@  int suspend_nvs_save(void)
 			}
 			if (!entry->kaddr) {
 				suspend_nvs_free();
+				trace_suspend_resume("save_nvs_memory", false);
 				return -ENOMEM;
 			}
 			memcpy(entry->data, entry->kaddr, entry->size);
 		}
 
+	trace_suspend_resume("save_nvs_memory", false);
 	return 0;
 }
 
@@ -203,10 +207,12 @@  void suspend_nvs_restore(void)
 {
 	struct nvs_page *entry;
 
+	trace_suspend_resume("restore_nvs_memory", true);
 	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
 
 	list_for_each_entry(entry, &nvs_list, node)
 		if (entry->data)
 			memcpy(entry->kaddr, entry->data, entry->size);
+	trace_suspend_resume("restore_nvs_memory", false);
 }
 #endif
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 6776c59..b65a77a 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -44,6 +44,7 @@ 
 #include <linux/list.h>
 #include <linux/jiffies.h>
 #include <linux/semaphore.h>
+#include <trace/events/power.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -835,7 +836,9 @@  acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
 
 void acpi_os_sleep(u64 ms)
 {
+	trace_suspend_resume("acpi_os_sleep", true);
 	msleep(ms);
+	trace_suspend_resume("acpi_os_sleep", false);
 }
 
 void acpi_os_stall(u32 us)
diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index e8d11b6..c2fd5d4 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -10,6 +10,7 @@ 
 #include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <trace/events/power.h>
 
 static LIST_HEAD(syscore_ops_list);
 static DEFINE_MUTEX(syscore_ops_lock);
@@ -49,6 +50,7 @@  int syscore_suspend(void)
 	struct syscore_ops *ops;
 	int ret = 0;
 
+	trace_suspend_resume("syscore_suspend", true);
 	pr_debug("Checking wakeup interrupts\n");
 
 	/* Return error code if there are any wakeup interrupts pending. */
@@ -70,6 +72,7 @@  int syscore_suspend(void)
 				"Interrupts enabled after %pF\n", ops->suspend);
 		}
 
+	trace_suspend_resume("syscore_suspend", false);
 	return 0;
 
  err_out:
@@ -92,6 +95,7 @@  void syscore_resume(void)
 {
 	struct syscore_ops *ops;
 
+	trace_suspend_resume("syscore_resume", true);
 	WARN_ONCE(!irqs_disabled(),
 		"Interrupts enabled before system core resume.\n");
 
@@ -103,6 +107,7 @@  void syscore_resume(void)
 			WARN_ONCE(!irqs_disabled(),
 				"Interrupts enabled after %pF\n", ops->resume);
 		}
+	trace_suspend_resume("syscore_resume", false);
 }
 EXPORT_SYMBOL_GPL(syscore_resume);
 #endif /* CONFIG_PM_SLEEP */
diff --git a/include/trace/events/power.h b/include/trace/events/power.h
index 9a7e08d..1d9fd76 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -151,6 +151,25 @@  TRACE_EVENT(device_pm_report_time,
 		__entry->ops_time, __entry->error)
 );
 
+TRACE_EVENT(suspend_resume,
+
+	TP_PROTO(char *action, bool start),
+
+	TP_ARGS(action, start),
+
+	TP_STRUCT__entry(
+		__string(action, action)
+		__field(bool, start)
+	),
+
+	TP_fast_assign(
+		__assign_str(action, action);
+		__entry->start = start;
+	),
+
+	TP_printk("%s %s", __get_str(action), (__entry->start)?"begin":"end")
+);
+
 DECLARE_EVENT_CLASS(wakeup_source,
 
 	TP_PROTO(const char *name, unsigned int state),
diff --git a/kernel/cpu.c b/kernel/cpu.c
index a9e710e..7aa30ec 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -20,6 +20,7 @@ 
 #include <linux/gfp.h>
 #include <linux/suspend.h>
 #include <linux/lockdep.h>
+#include <trace/events/power.h>
 
 #include "smpboot.h"
 
@@ -509,6 +510,7 @@  static cpumask_var_t frozen_cpus;
 int disable_nonboot_cpus(void)
 {
 	int cpu, first_cpu, error = 0;
+	char buf[12];
 
 	cpu_maps_update_begin();
 	first_cpu = cpumask_first(cpu_online_mask);
@@ -522,7 +524,10 @@  int disable_nonboot_cpus(void)
 	for_each_online_cpu(cpu) {
 		if (cpu == first_cpu)
 			continue;
+		sprintf(buf, "CPU%u_OFF", cpu);
+		trace_suspend_resume(buf, true);
 		error = _cpu_down(cpu, 1);
+		trace_suspend_resume(buf, false);
 		if (!error)
 			cpumask_set_cpu(cpu, frozen_cpus);
 		else {
@@ -554,6 +559,7 @@  void __weak arch_enable_nonboot_cpus_end(void)
 void __ref enable_nonboot_cpus(void)
 {
 	int cpu, error;
+	char buf[10];
 
 	/* Allow everyone to use the CPU hotplug again */
 	cpu_maps_update_begin();
@@ -566,7 +572,10 @@  void __ref enable_nonboot_cpus(void)
 	arch_enable_nonboot_cpus_begin();
 
 	for_each_cpu(cpu, frozen_cpus) {
+		sprintf(buf, "CPU%u_ON", cpu);
+		trace_suspend_resume(buf, true);
 		error = _cpu_up(cpu, 1);
+		trace_suspend_resume(buf, false);
 		if (!error) {
 			printk(KERN_INFO "CPU%d is up\n", cpu);
 			continue;
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 06ec886..4aec2d9 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -17,6 +17,7 @@ 
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/kmod.h>
+#include <trace/events/power.h>
 
 /* 
  * Timeout for stopping processes
@@ -175,6 +176,7 @@  void thaw_processes(void)
 	struct task_struct *g, *p;
 	struct task_struct *curr = current;
 
+	trace_suspend_resume("thaw_processes", true);
 	if (pm_freezing)
 		atomic_dec(&system_freezing_cnt);
 	pm_freezing = false;
@@ -201,6 +203,7 @@  void thaw_processes(void)
 
 	schedule();
 	printk("done.\n");
+	trace_suspend_resume("thaw_processes", false);
 }
 
 void thaw_kernel_threads(void)
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 8233cd4..c7e3e9f 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -147,7 +147,9 @@  static int suspend_prepare(suspend_state_t state)
 	if (error)
 		goto Finish;
 
+	trace_suspend_resume("freeze_processes", true);
 	error = suspend_freeze_processes();
+	trace_suspend_resume("freeze_processes", false);
 	if (!error)
 		return 0;
 
@@ -337,9 +339,11 @@  static int enter_state(suspend_state_t state)
 	if (state == PM_SUSPEND_FREEZE)
 		freeze_begin();
 
+	trace_suspend_resume("sync_filesystems", true);
 	printk(KERN_INFO "PM: Syncing filesystems ... ");
 	sys_sync();
 	printk("done.\n");
+	trace_suspend_resume("sync_filesystems", false);
 
 	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
 	error = suspend_prepare(state);