]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PM: cpuidle/suspend: Add s2idle usage and time state attributes
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 14 Mar 2018 11:27:21 +0000 (12:27 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 29 Mar 2018 11:06:08 +0000 (13:06 +0200)
Add a new attribute group called "s2idle" under the sysfs directory
of each cpuidle state that supports the ->enter_s2idle callback
and put two new attributes, "usage" and "time", into that group to
represent the number of times the given state was requested for
suspend-to-idle and the total time spent in suspend-to-idle after
requesting that state, respectively.

That will allow diagnostic information related to suspend-to-idle
to be collected without enabling advanced debug features and
analyzing dmesg output.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Documentation/ABI/testing/sysfs-devices-system-cpu
drivers/cpuidle/cpuidle.c
drivers/cpuidle/sysfs.c
include/linux/cpuidle.h

index 4ed63b6cfb155cbc8b5aaab64e1030662f19411f..025b7cf3768dc62f3e5e68129fcc36bdf994c659 100644 (file)
@@ -198,6 +198,31 @@ Description:
                time (in microseconds) this cpu should spend in this idle state
                to make the transition worth the effort.
 
+What:          /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/
+Date:          March 2018
+KernelVersion: v4.17
+Contact:       Linux power management list <linux-pm@vger.kernel.org>
+Description:
+               Idle state usage statistics related to suspend-to-idle.
+
+               This attribute group is only present for states that can be
+               used in suspend-to-idle with suspended timekeeping.
+
+What:          /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/time
+Date:          March 2018
+KernelVersion: v4.17
+Contact:       Linux power management list <linux-pm@vger.kernel.org>
+Description:
+               Total time spent by the CPU in suspend-to-idle (with scheduler
+               tick suspended) after requesting this state.
+
+What:          /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/usage
+Date:          March 2018
+KernelVersion: v4.17
+Contact:       Linux power management list <linux-pm@vger.kernel.org>
+Description:
+               Total number of times this state has been requested by the CPU
+               while entering suspend-to-idle.
 
 What:          /sys/devices/system/cpu/cpu#/cpufreq/*
 Date:          pre-git history
index 68a16827f45fd57f8bd89cbb15140662374db3d3..0003e9a02637f1ded004d6465b4eaaeef5817821 100644 (file)
@@ -131,6 +131,10 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
 static void enter_s2idle_proper(struct cpuidle_driver *drv,
                                struct cpuidle_device *dev, int index)
 {
+       ktime_t time_start, time_end;
+
+       time_start = ns_to_ktime(local_clock());
+
        /*
         * trace_suspend_resume() called by tick_freeze() for the last CPU
         * executing it contains RCU usage regarded as invalid in the idle
@@ -152,6 +156,11 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv,
         */
        RCU_NONIDLE(tick_unfreeze());
        start_critical_timings();
+
+       time_end = ns_to_ktime(local_clock());
+
+       dev->states_usage[index].s2idle_time += ktime_us_delta(time_end, time_start);
+       dev->states_usage[index].s2idle_usage++;
 }
 
 /**
index ae948b1da93a379b12d16aaf98be5df6ac762da4..e754c7aae7f7bba331459f2500c7339e2321a7ba 100644 (file)
@@ -330,6 +330,58 @@ struct cpuidle_state_kobj {
        struct kobject kobj;
 };
 
+#ifdef CONFIG_SUSPEND
+#define define_show_state_s2idle_ull_function(_name) \
+static ssize_t show_state_s2idle_##_name(struct cpuidle_state *state, \
+                                        struct cpuidle_state_usage *state_usage, \
+                                        char *buf)                             \
+{ \
+       return sprintf(buf, "%llu\n", state_usage->s2idle_##_name);\
+}
+
+define_show_state_s2idle_ull_function(usage);
+define_show_state_s2idle_ull_function(time);
+
+#define define_one_state_s2idle_ro(_name, show) \
+static struct cpuidle_state_attr attr_s2idle_##_name = \
+       __ATTR(_name, 0444, show, NULL)
+
+define_one_state_s2idle_ro(usage, show_state_s2idle_usage);
+define_one_state_s2idle_ro(time, show_state_s2idle_time);
+
+static struct attribute *cpuidle_state_s2idle_attrs[] = {
+       &attr_s2idle_usage.attr,
+       &attr_s2idle_time.attr,
+       NULL
+};
+
+static const struct attribute_group cpuidle_state_s2idle_group = {
+       .name   = "s2idle",
+       .attrs  = cpuidle_state_s2idle_attrs,
+};
+
+static void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
+{
+       int ret;
+
+       if (!kobj->state->enter_s2idle)
+               return;
+
+       ret = sysfs_create_group(&kobj->kobj, &cpuidle_state_s2idle_group);
+       if (ret)
+               pr_debug("%s: sysfs attribute group not created\n", __func__);
+}
+
+static void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
+{
+       if (kobj->state->enter_s2idle)
+               sysfs_remove_group(&kobj->kobj, &cpuidle_state_s2idle_group);
+}
+#else
+static inline void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
+static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
+#endif /* CONFIG_SUSPEND */
+
 #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
 #define kobj_to_state(k) (kobj_to_state_obj(k)->state)
 #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)
@@ -383,6 +435,7 @@ static struct kobj_type ktype_state_cpuidle = {
 
 static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i)
 {
+       cpuidle_remove_s2idle_attr_group(device->kobjs[i]);
        kobject_put(&device->kobjs[i]->kobj);
        wait_for_completion(&device->kobjs[i]->kobj_unregister);
        kfree(device->kobjs[i]);
@@ -417,6 +470,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
                        kfree(kobj);
                        goto error_state;
                }
+               cpuidle_add_s2idle_attr_group(kobj);
                kobject_uevent(&kobj->kobj, KOBJ_ADD);
                device->kobjs[i] = kobj;
        }
index 0b3fc229086ca6cb98d24645c487ab097d6566e5..a806e94c482f98805ee6a68d3f9a8055f5ca77b2 100644 (file)
@@ -33,6 +33,10 @@ struct cpuidle_state_usage {
        unsigned long long      disable;
        unsigned long long      usage;
        unsigned long long      time; /* in US */
+#ifdef CONFIG_SUSPEND
+       unsigned long long      s2idle_usage;
+       unsigned long long      s2idle_time; /* in US */
+#endif
 };
 
 struct cpuidle_state {