Message ID | 159528289856.993790.11787167534159675987.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | ACPI/NVDIMM: Runtime Firmware Activation | expand |
Hi Dan, Documentation comments below: On 7/20/20 3:08 PM, Dan Williams wrote: > Abstract platform specific mechanics for nvdimm firmware activation > behind a handful of generic ops. At the bus level ->activate_state() > indicates the unified state (idle, busy, armed) of all DIMMs on the bus, > and ->capability() indicates the system state expectations for activate. > At the DIMM level ->activate_state() indicates the per-DIMM state, > ->activate_result() indicates the outcome of the last activation > attempt, and ->arm() attempts to transition the DIMM from 'idle' to > 'armed'. > > A new hibernate_quiet_exec() facility is added to support firmware > activation in an OS defined system quiesce state. It leverages the fact > that the hibernate-freeze state wants to assert that a memory > hibernation snapshot can be taken. This is in contrast to a platform > firmware defined quiesce state that may forcefully quiet the memory > controller independent of whether an individual device-driver properly > supports hibernate-freeze. > > The libnvdimm sysfs interface is extended to support detection of a > firmware activate capability. The mechanism supports enumeration and > triggering of firmware activate, optionally in the > hibernate_quiet_exec() context. > > Cc: Pavel Machek <pavel@ucw.cz> > Cc: Ira Weiny <ira.weiny@intel.com> > Cc: Len Brown <len.brown@intel.com> > Cc: Jonathan Corbet <corbet@lwn.net> > Cc: Dave Jiang <dave.jiang@intel.com> > Cc: Vishal Verma <vishal.l.verma@intel.com> > [rafael: hibernate_quiet_exec() proposal] > Co-developed-by: "Rafael J. Wysocki" <rjw@rjwysocki.net> > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > --- > Documentation/ABI/testing/sysfs-bus-nvdimm | 2 > .../driver-api/nvdimm/firmware-activate.rst | 86 ++++++++++++ > drivers/nvdimm/core.c | 149 ++++++++++++++++++++ > drivers/nvdimm/dimm_devs.c | 115 +++++++++++++++ > drivers/nvdimm/nd-core.h | 1 > include/linux/libnvdimm.h | 44 ++++++ > include/linux/suspend.h | 6 + > kernel/power/hibernate.c | 97 +++++++++++++ > 8 files changed, 500 insertions(+) > create mode 100644 Documentation/ABI/testing/sysfs-bus-nvdimm > create mode 100644 Documentation/driver-api/nvdimm/firmware-activate.rst > diff --git a/Documentation/driver-api/nvdimm/firmware-activate.rst b/Documentation/driver-api/nvdimm/firmware-activate.rst > new file mode 100644 > index 000000000000..9eb98aa833c5 > --- /dev/null > +++ b/Documentation/driver-api/nvdimm/firmware-activate.rst > @@ -0,0 +1,86 @@ > +.. SPDX-License-Identifier: GPL-2.0 > + > +================================== > +NVDIMM Runtime Firmware Activation > +================================== > + > +Some persistent memory devices run a firmware locally on the device / run firmware > +"DIMM" to perform tasks like media management, capacity provisioning, > +and health monitoring. The process of updating that firmware typically > +involves a reboot because it has implications for in-flight memory > +transactions. However, reboots are disruptive and at least the Intel > +persistent memory platform implementation, described by the Intel ACPI > +DSM specification [1], has added support for activating firmware at that's an Intel spec? just checking. > +runtime. > + > +A native sysfs interface is implemented in libnvdimm to allow platform platforms > +to advertise and control their local runtime firmware activation > +capability. > + > +The libnvdimm bus object, ndbusX, implements an ndbusX/firmware/activate > +attribute that shows the state of the firmware activation as one of 'idle', > +'armed', 'overflow', and 'busy'. or > + > +- idle: > + No devices are set / armed to activate firmware > + > +- armed: > + At least one device is armed > + > +- busy: > + In the busy state armed devices are in the process of transitioning > + back to idle and completing an activation cycle. > + > +- overflow: > + If the platform has a concept of incremental work needed to perform > + the activation it could be the case that too many DIMMs are armed for > + activation. In that scenario the potential for firmware activation to > + timeout is indicated by the 'overflow' state. > + > +The 'ndbusX/firmware/activate' property can be written with a value of > +either 'live', or 'quiesce'. A value of 'quiesce' triggers the kernel to > +run firmware activation from within the equivalent of the hibernation > +'freeze' state where drivers and applications are notified to stop their > +modifications of system memory. A value of 'live' attempts > +firmware-activation without this hibernation cycle. The no hyphen^^ > +'ndbusX/firmware/activate' property will be elided completely if no > +firmware activation capability is detected. > + > +Another property 'ndbusX/firmware/capability' indicates a value of > +'live', or 'quiesce'. Where 'live' indicates that the firmware no comma. no period. So this: +'live' or 'quiesce', where > +does not require or inflict any quiesce period on the system to update > +firmware. A capability value of 'quiesce' indicates that firmware does > +expect and injects a quiet period for the memory controller, but 'live' > +may still be written to 'ndbusX/firmware/activate' as an override to > +assume the risk of racing firmware update with in-flight device and > +application activity. The 'ndbusX/firmware/capability' property will be > +elided completely if no firmware activation capability is detected. > + > +The libnvdimm memory-device / DIMM object, nmemX, implements > +'nmemX/firmware/activate' and 'nmemX/firmware/result' attributes to > +communicate the per-device firmware activation state. Similar to the > +'ndbusX/firmware/activate' attribute, the 'nmemX/firmware/activate' > +attribute indicates 'idle', 'armed', or 'busy'. The state transitions > +from 'armed' to 'idle' when the system is prepared to activate firmware, > +firmware staged + state set to armed, and 'ndbusX/firmware/activate' is > +triggered. After that activation event the nmemX/firmware/result > +attribute reflects the state of the last activation as one of: > + > +- none: > + No runtime activation triggered since the last time the device was reset > + > +- success: > + The last runtime activation completed successfully. > + > +- fail: > + The last runtime activation failed for device-specific reasons. > + > +- not_staged: > + The last runtime activation failed due to a sequencing error of the > + firmware image not being staged. > + > +- need_reset: > + Runtime firmware activation failed, but the firmware can still be > + activated via the legacy method of power-cycling the system. > + > +[1]: https://docs.pmem.io/persistent-memory/ thanks.
On Mon, 2020-07-20 at 17:02 -0700, Randy Dunlap wrote: > Hi Dan, > > Documentation comments below: Dan, Randy, I'm happy to fix these up when applying. > > On 7/20/20 3:08 PM, Dan Williams wrote: > > Abstract platform specific mechanics for nvdimm firmware activation > > behind a handful of generic ops. At the bus level ->activate_state() > > indicates the unified state (idle, busy, armed) of all DIMMs on the bus, > > and ->capability() indicates the system state expectations for activate. > > At the DIMM level ->activate_state() indicates the per-DIMM state, > > ->activate_result() indicates the outcome of the last activation > > attempt, and ->arm() attempts to transition the DIMM from 'idle' to > > 'armed'. > > > > A new hibernate_quiet_exec() facility is added to support firmware > > activation in an OS defined system quiesce state. It leverages the fact > > that the hibernate-freeze state wants to assert that a memory > > hibernation snapshot can be taken. This is in contrast to a platform > > firmware defined quiesce state that may forcefully quiet the memory > > controller independent of whether an individual device-driver properly > > supports hibernate-freeze. > > > > The libnvdimm sysfs interface is extended to support detection of a > > firmware activate capability. The mechanism supports enumeration and > > triggering of firmware activate, optionally in the > > hibernate_quiet_exec() context. > > > > Cc: Pavel Machek <pavel@ucw.cz> > > Cc: Ira Weiny <ira.weiny@intel.com> > > Cc: Len Brown <len.brown@intel.com> > > Cc: Jonathan Corbet <corbet@lwn.net> > > Cc: Dave Jiang <dave.jiang@intel.com> > > Cc: Vishal Verma <vishal.l.verma@intel.com> > > [rafael: hibernate_quiet_exec() proposal] > > Co-developed-by: "Rafael J. Wysocki" <rjw@rjwysocki.net> > > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > > --- > > Documentation/ABI/testing/sysfs-bus-nvdimm | 2 > > .../driver-api/nvdimm/firmware-activate.rst | 86 ++++++++++++ > > drivers/nvdimm/core.c | 149 ++++++++++++++++++++ > > drivers/nvdimm/dimm_devs.c | 115 +++++++++++++++ > > drivers/nvdimm/nd-core.h | 1 > > include/linux/libnvdimm.h | 44 ++++++ > > include/linux/suspend.h | 6 + > > kernel/power/hibernate.c | 97 +++++++++++++ > > 8 files changed, 500 insertions(+) > > create mode 100644 Documentation/ABI/testing/sysfs-bus-nvdimm > > create mode 100644 Documentation/driver-api/nvdimm/firmware-activate.rst > > diff --git a/Documentation/driver-api/nvdimm/firmware-activate.rst b/Documentation/driver-api/nvdimm/firmware-activate.rst > > new file mode 100644 > > index 000000000000..9eb98aa833c5 > > --- /dev/null > > +++ b/Documentation/driver-api/nvdimm/firmware-activate.rst > > @@ -0,0 +1,86 @@ > > +.. SPDX-License-Identifier: GPL-2.0 > > + > > +================================== > > +NVDIMM Runtime Firmware Activation > > +================================== > > + > > +Some persistent memory devices run a firmware locally on the device / > > run firmware > > > +"DIMM" to perform tasks like media management, capacity provisioning, > > +and health monitoring. The process of updating that firmware typically > > +involves a reboot because it has implications for in-flight memory > > +transactions. However, reboots are disruptive and at least the Intel > > +persistent memory platform implementation, described by the Intel ACPI > > +DSM specification [1], has added support for activating firmware at > > that's an Intel spec? just checking. > > > +runtime. > > + > > +A native sysfs interface is implemented in libnvdimm to allow platform > > platforms > > > +to advertise and control their local runtime firmware activation > > +capability. > > + > > +The libnvdimm bus object, ndbusX, implements an ndbusX/firmware/activate > > +attribute that shows the state of the firmware activation as one of 'idle', > > +'armed', 'overflow', and 'busy'. > > or > > > + > > +- idle: > > + No devices are set / armed to activate firmware > > + > > +- armed: > > + At least one device is armed > > + > > +- busy: > > + In the busy state armed devices are in the process of transitioning > > + back to idle and completing an activation cycle. > > + > > +- overflow: > > + If the platform has a concept of incremental work needed to perform > > + the activation it could be the case that too many DIMMs are armed for > > + activation. In that scenario the potential for firmware activation to > > + timeout is indicated by the 'overflow' state. > > + > > +The 'ndbusX/firmware/activate' property can be written with a value of > > +either 'live', or 'quiesce'. A value of 'quiesce' triggers the kernel to > > +run firmware activation from within the equivalent of the hibernation > > +'freeze' state where drivers and applications are notified to stop their > > +modifications of system memory. A value of 'live' attempts > > +firmware-activation without this hibernation cycle. The > > no hyphen^^ > > > +'ndbusX/firmware/activate' property will be elided completely if no > > +firmware activation capability is detected. > > + > > +Another property 'ndbusX/firmware/capability' indicates a value of > > +'live', or 'quiesce'. Where 'live' indicates that the firmware > > no comma. no period. So this: > > +'live' or 'quiesce', where > > > +does not require or inflict any quiesce period on the system to update > > +firmware. A capability value of 'quiesce' indicates that firmware does > > +expect and injects a quiet period for the memory controller, but 'live' > > +may still be written to 'ndbusX/firmware/activate' as an override to > > +assume the risk of racing firmware update with in-flight device and > > +application activity. The 'ndbusX/firmware/capability' property will be > > +elided completely if no firmware activation capability is detected. > > + > > +The libnvdimm memory-device / DIMM object, nmemX, implements > > +'nmemX/firmware/activate' and 'nmemX/firmware/result' attributes to > > +communicate the per-device firmware activation state. Similar to the > > +'ndbusX/firmware/activate' attribute, the 'nmemX/firmware/activate' > > +attribute indicates 'idle', 'armed', or 'busy'. The state transitions > > +from 'armed' to 'idle' when the system is prepared to activate firmware, > > +firmware staged + state set to armed, and 'ndbusX/firmware/activate' is > > +triggered. After that activation event the nmemX/firmware/result > > +attribute reflects the state of the last activation as one of: > > + > > +- none: > > + No runtime activation triggered since the last time the device was reset > > + > > +- success: > > + The last runtime activation completed successfully. > > + > > +- fail: > > + The last runtime activation failed for device-specific reasons. > > + > > +- not_staged: > > + The last runtime activation failed due to a sequencing error of the > > + firmware image not being staged. > > + > > +- need_reset: > > + Runtime firmware activation failed, but the firmware can still be > > + activated via the legacy method of power-cycling the system. > > + > > +[1]: https://docs.pmem.io/persistent-memory/ > > thanks.
On Mon, Jul 20, 2020 at 5:02 PM Randy Dunlap <rdunlap@infradead.org> wrote: > > Hi Dan, > > Documentation comments below: > > On 7/20/20 3:08 PM, Dan Williams wrote: > > Abstract platform specific mechanics for nvdimm firmware activation > > behind a handful of generic ops. At the bus level ->activate_state() > > indicates the unified state (idle, busy, armed) of all DIMMs on the bus, > > and ->capability() indicates the system state expectations for activate. > > At the DIMM level ->activate_state() indicates the per-DIMM state, > > ->activate_result() indicates the outcome of the last activation > > attempt, and ->arm() attempts to transition the DIMM from 'idle' to > > 'armed'. > > > > A new hibernate_quiet_exec() facility is added to support firmware > > activation in an OS defined system quiesce state. It leverages the fact > > that the hibernate-freeze state wants to assert that a memory > > hibernation snapshot can be taken. This is in contrast to a platform > > firmware defined quiesce state that may forcefully quiet the memory > > controller independent of whether an individual device-driver properly > > supports hibernate-freeze. > > > > The libnvdimm sysfs interface is extended to support detection of a > > firmware activate capability. The mechanism supports enumeration and > > triggering of firmware activate, optionally in the > > hibernate_quiet_exec() context. > > > > Cc: Pavel Machek <pavel@ucw.cz> > > Cc: Ira Weiny <ira.weiny@intel.com> > > Cc: Len Brown <len.brown@intel.com> > > Cc: Jonathan Corbet <corbet@lwn.net> > > Cc: Dave Jiang <dave.jiang@intel.com> > > Cc: Vishal Verma <vishal.l.verma@intel.com> > > [rafael: hibernate_quiet_exec() proposal] > > Co-developed-by: "Rafael J. Wysocki" <rjw@rjwysocki.net> > > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > > --- > > Documentation/ABI/testing/sysfs-bus-nvdimm | 2 > > .../driver-api/nvdimm/firmware-activate.rst | 86 ++++++++++++ > > drivers/nvdimm/core.c | 149 ++++++++++++++++++++ > > drivers/nvdimm/dimm_devs.c | 115 +++++++++++++++ > > drivers/nvdimm/nd-core.h | 1 > > include/linux/libnvdimm.h | 44 ++++++ > > include/linux/suspend.h | 6 + > > kernel/power/hibernate.c | 97 +++++++++++++ > > 8 files changed, 500 insertions(+) > > create mode 100644 Documentation/ABI/testing/sysfs-bus-nvdimm > > create mode 100644 Documentation/driver-api/nvdimm/firmware-activate.rst > > > > diff --git a/Documentation/driver-api/nvdimm/firmware-activate.rst b/Documentation/driver-api/nvdimm/firmware-activate.rst > > new file mode 100644 > > index 000000000000..9eb98aa833c5 > > --- /dev/null > > +++ b/Documentation/driver-api/nvdimm/firmware-activate.rst > > @@ -0,0 +1,86 @@ > > +.. SPDX-License-Identifier: GPL-2.0 > > + > > +================================== > > +NVDIMM Runtime Firmware Activation > > +================================== > > + > > +Some persistent memory devices run a firmware locally on the device / > > run firmware That works too. I was going to say "run a firmware image", but "run firmware" is clearer. > > > +"DIMM" to perform tasks like media management, capacity provisioning, > > +and health monitoring. The process of updating that firmware typically > > +involves a reboot because it has implications for in-flight memory > > +transactions. However, reboots are disruptive and at least the Intel > > +persistent memory platform implementation, described by the Intel ACPI > > +DSM specification [1], has added support for activating firmware at > > that's an Intel spec? just checking. Correct. It's a public specification of the ACPI methods that Intel platform BIOS or virtual-machine BIOS deploys to talk to NVDIMM devices. > > > +runtime. > > + > > +A native sysfs interface is implemented in libnvdimm to allow platform > > platforms Ack. > > > +to advertise and control their local runtime firmware activation > > +capability. > > + > > +The libnvdimm bus object, ndbusX, implements an ndbusX/firmware/activate > > +attribute that shows the state of the firmware activation as one of 'idle', > > +'armed', 'overflow', and 'busy'. > > or Yup. > > > + > > +- idle: > > + No devices are set / armed to activate firmware > > + > > +- armed: > > + At least one device is armed > > + > > +- busy: > > + In the busy state armed devices are in the process of transitioning > > + back to idle and completing an activation cycle. > > + > > +- overflow: > > + If the platform has a concept of incremental work needed to perform > > + the activation it could be the case that too many DIMMs are armed for > > + activation. In that scenario the potential for firmware activation to > > + timeout is indicated by the 'overflow' state. > > + > > +The 'ndbusX/firmware/activate' property can be written with a value of > > +either 'live', or 'quiesce'. A value of 'quiesce' triggers the kernel to > > +run firmware activation from within the equivalent of the hibernation > > +'freeze' state where drivers and applications are notified to stop their > > +modifications of system memory. A value of 'live' attempts > > +firmware-activation without this hibernation cycle. The > > no hyphen^^ Agree. > > > +'ndbusX/firmware/activate' property will be elided completely if no > > +firmware activation capability is detected. > > + > > +Another property 'ndbusX/firmware/capability' indicates a value of > > +'live', or 'quiesce'. Where 'live' indicates that the firmware > > no comma. no period. So this: > > +'live' or 'quiesce', where Ok. > > > +does not require or inflict any quiesce period on the system to update > > +firmware. A capability value of 'quiesce' indicates that firmware does > > +expect and injects a quiet period for the memory controller, but 'live' > > +may still be written to 'ndbusX/firmware/activate' as an override to > > +assume the risk of racing firmware update with in-flight device and > > +application activity. The 'ndbusX/firmware/capability' property will be > > +elided completely if no firmware activation capability is detected. > > + > > +The libnvdimm memory-device / DIMM object, nmemX, implements > > +'nmemX/firmware/activate' and 'nmemX/firmware/result' attributes to > > +communicate the per-device firmware activation state. Similar to the > > +'ndbusX/firmware/activate' attribute, the 'nmemX/firmware/activate' > > +attribute indicates 'idle', 'armed', or 'busy'. The state transitions > > +from 'armed' to 'idle' when the system is prepared to activate firmware, > > +firmware staged + state set to armed, and 'ndbusX/firmware/activate' is > > +triggered. After that activation event the nmemX/firmware/result > > +attribute reflects the state of the last activation as one of: > > + > > +- none: > > + No runtime activation triggered since the last time the device was reset > > + > > +- success: > > + The last runtime activation completed successfully. > > + > > +- fail: > > + The last runtime activation failed for device-specific reasons. > > + > > +- not_staged: > > + The last runtime activation failed due to a sequencing error of the > > + firmware image not being staged. > > + > > +- need_reset: > > + Runtime firmware activation failed, but the firmware can still be > > + activated via the legacy method of power-cycling the system. > > + > > +[1]: https://docs.pmem.io/persistent-memory/ > > > thanks. > -- > ~Randy Thanks Randy.
On Mon, Jul 20, 2020 at 5:14 PM Vishal Verma <vishal.l.verma@intel.com> wrote: > > On Mon, 2020-07-20 at 17:02 -0700, Randy Dunlap wrote: > > Hi Dan, > > > > Documentation comments below: > > Dan, Randy, > > I'm happy to fix these up when applying. Sounds good. Thanks Vishal.
Hi Dan, I love your patch! Perhaps something to improve: [auto build test WARNING on 48778464bb7d346b47157d21ffde2af6b2d39110] url: https://github.com/0day-ci/linux/commits/Dan-Williams/ACPI-NVDIMM-Runtime-Firmware-Activation/20200721-062902 base: 48778464bb7d346b47157d21ffde2af6b2d39110 :::::: branch date: 8 hours ago :::::: commit date: 8 hours ago config: x86_64-randconfig-s021-20200719 (attached as .config) compiler: gcc-9 (Debian 9.3.0-14) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.2-49-g707c5017-dirty # save the attached .config to linux build tree make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=x86_64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/bluetooth/btusb.c:2245:25: sparse: sparse: cast to restricted __le16 drivers/bluetooth/btusb.c:2254:25: sparse: sparse: cast to restricted __le16 drivers/bluetooth/btusb.c:2255:25: sparse: sparse: cast to restricted __le16 drivers/bluetooth/btusb.c:2256:25: sparse: sparse: cast to restricted __le16 -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/cpufreq/cpufreq.c:471:17: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct notifier_block *nb @@ got struct notifier_block [noderef] __rcu *static [addressable] [toplevel] head @@ drivers/cpufreq/cpufreq.c:471:17: sparse: expected struct notifier_block *nb drivers/cpufreq/cpufreq.c:471:17: sparse: got struct notifier_block [noderef] __rcu *static [addressable] [toplevel] head drivers/cpufreq/cpufreq.c:471:65: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct notifier_block *nb @@ got struct notifier_block [noderef] __rcu *next @@ drivers/cpufreq/cpufreq.c:471:65: sparse: expected struct notifier_block *nb drivers/cpufreq/cpufreq.c:471:65: sparse: got struct notifier_block [noderef] __rcu *next -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/regulator/internal.h:43:42: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:1627:56: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:1629:56: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:455:17: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:455:25: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:469:47: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:3347:65: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:3823:47: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:3965:65: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:5527:54: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/core.c:5528:54: sparse: sparse: restricted suspend_state_t degrades to integer -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/regulator/internal.h:43:42: sparse: sparse: restricted suspend_state_t degrades to integer -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/regulator/of_regulator.c:18:43: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/of_regulator.c:193:22: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/of_regulator.c:196:22: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/of_regulator.c:199:22: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/of_regulator.c:202:22: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/of_regulator.c:203:22: sparse: sparse: restricted suspend_state_t degrades to integer drivers/regulator/of_regulator.c:252:26: sparse: sparse: restricted suspend_state_t degrades to integer -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/regulator/da9063-regulator.c:514:17: sparse: sparse: Initializer entry defined twice drivers/regulator/da9063-regulator.c:515:18: sparse: also defined here -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/tty/sysrq.c:148:13: sparse: sparse: context imbalance in 'sysrq_handle_crash' - unexpected unlock -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/base/firmware_loader/main.c:266:9: sparse: sparse: context imbalance in 'free_fw_priv' - wrong count at exit -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type init/do_mounts.c:408:30: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected char const [noderef] __user * @@ got char * @@ init/do_mounts.c:408:30: sparse: expected char const [noderef] __user * init/do_mounts.c:408:30: sparse: got char * init/do_mounts.c:412:20: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected char const [noderef] __user *filename @@ got char * @@ init/do_mounts.c:412:20: sparse: expected char const [noderef] __user *filename init/do_mounts.c:412:20: sparse: got char * init/do_mounts.c:685:23: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected char const [noderef] __user * @@ got char * @@ init/do_mounts.c:685:23: sparse: expected char const [noderef] __user * init/do_mounts.c:685:23: sparse: got char * init/do_mounts.c:686:21: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected char const [noderef] __user *filename @@ got char * @@ init/do_mounts.c:686:21: sparse: expected char const [noderef] __user *filename init/do_mounts.c:686:21: sparse: got char * init/do_mounts.h:19:21: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected char const [noderef] __user *pathname @@ got char *name @@ init/do_mounts.h:19:21: sparse: expected char const [noderef] __user *pathname init/do_mounts.h:19:21: sparse: got char *name init/do_mounts.h:20:27: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected char const [noderef] __user *filename @@ got char *name @@ init/do_mounts.h:20:27: sparse: expected char const [noderef] __user *filename init/do_mounts.h:20:27: sparse: got char *name -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/umh.c:74:31: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/umh.c:74:31: sparse: expected struct spinlock [usertype] *lock kernel/umh.c:74:31: sparse: got struct spinlock [noderef] __rcu * kernel/umh.c:76:33: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/umh.c:76:33: sparse: expected struct spinlock [usertype] *lock kernel/umh.c:76:33: sparse: got struct spinlock [noderef] __rcu * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sys.c:1878:19: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct file [noderef] __rcu *__ret @@ got struct file *[assigned] file @@ kernel/sys.c:1878:19: sparse: expected struct file [noderef] __rcu *__ret kernel/sys.c:1878:19: sparse: got struct file *[assigned] file kernel/sys.c:1878:17: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct file *old_exe @@ got struct file [noderef] __rcu *[assigned] __ret @@ kernel/sys.c:1878:17: sparse: expected struct file *old_exe kernel/sys.c:1878:17: sparse: got struct file [noderef] __rcu *[assigned] __ret kernel/sys.c:2240:16: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void const volatile [noderef] __user * @@ got int [noderef] __user **tid_addr @@ kernel/sys.c:2240:16: sparse: expected void const volatile [noderef] __user * kernel/sys.c:2240:16: sparse: got int [noderef] __user **tid_addr kernel/sys.c:1049:32: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *p1 @@ got struct task_struct [noderef] __rcu *real_parent @@ kernel/sys.c:1049:32: sparse: expected struct task_struct *p1 kernel/sys.c:1049:32: sparse: got struct task_struct [noderef] __rcu *real_parent include/linux/sched/signal.h:693:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ include/linux/sched/signal.h:693:37: sparse: expected struct spinlock [usertype] *lock include/linux/sched/signal.h:693:37: sparse: got struct spinlock [noderef] __rcu * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sched/core.c:256:48: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected struct task_struct *p @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/core.c:256:48: sparse: expected struct task_struct *p kernel/sched/core.c:256:48: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/core.c:512:38: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/core.c:512:38: sparse: expected struct task_struct *curr kernel/sched/core.c:512:38: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/core.c:1432:33: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *p @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/core.c:1432:33: sparse: expected struct task_struct *p kernel/sched/core.c:1432:33: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/core.c:1432:68: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *tsk @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/core.c:1432:68: sparse: expected struct task_struct *tsk kernel/sched/core.c:1432:68: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/core.c:3650:38: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/core.c:3650:38: sparse: expected struct task_struct *curr kernel/sched/core.c:3650:38: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/core.c:4083:14: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct task_struct *prev @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/core.c:4083:14: sparse: expected struct task_struct *prev kernel/sched/core.c:4083:14: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/core.c:4506:17: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/core.c:4506:17: sparse: struct task_struct * kernel/sched/core.c:4506:17: sparse: struct task_struct [noderef] __rcu * kernel/sched/core.c:4705:22: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/core.c:4705:22: sparse: struct task_struct [noderef] __rcu * kernel/sched/core.c:4705:22: sparse: struct task_struct * kernel/sched/core.c:8011:25: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *p @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/core.c:8011:25: sparse: expected struct task_struct *p kernel/sched/core.c:8011:25: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/core.c:256:11: sparse: sparse: dereference of noderef expression kernel/sched/core.c:1415:33: sparse: sparse: dereference of noderef expression kernel/sched/core.c:1416:19: sparse: sparse: dereference of noderef expression kernel/sched/core.c:1419:40: sparse: sparse: dereference of noderef expression kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * kernel/sched/sched.h:1803:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1803:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1803:9: sparse: struct task_struct * kernel/sched/sched.h:1803:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1803:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1803:9: sparse: struct task_struct * kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * kernel/sched/sched.h:1803:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1803:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1803:9: sparse: struct task_struct * kernel/sched/sched.h:1809:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1809:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1809:9: sparse: struct task_struct * kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * kernel/sched/sched.h:1803:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1803:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1803:9: sparse: struct task_struct * kernel/sched/sched.h:1809:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1809:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1809:9: sparse: struct task_struct * kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * kernel/sched/sched.h:1803:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1803:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1803:9: sparse: struct task_struct * kernel/sched/sched.h:1809:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1809:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1809:9: sparse: struct task_struct * kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * kernel/sched/sched.h:1803:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1803:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1803:9: sparse: struct task_struct * kernel/sched/sched.h:1809:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1809:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1809:9: sparse: struct task_struct * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sched/cputime.c:316:17: sparse: sparse: context imbalance in 'thread_group_cputime' - different lock contexts for basic block -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sched/fair.c:881:34: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct sched_entity *se @@ got struct sched_entity [noderef] __rcu * @@ kernel/sched/fair.c:881:34: sparse: expected struct sched_entity *se kernel/sched/fair.c:881:34: sparse: got struct sched_entity [noderef] __rcu * kernel/sched/fair.c:5386:38: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/fair.c:5386:38: sparse: struct task_struct [noderef] __rcu * kernel/sched/fair.c:5386:38: sparse: struct task_struct * kernel/sched/fair.c:5401:38: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/fair.c:5401:38: sparse: expected struct task_struct *curr kernel/sched/fair.c:5401:38: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/fair.c:6880:38: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/fair.c:6880:38: sparse: expected struct task_struct *curr kernel/sched/fair.c:6880:38: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/fair.c:7131:38: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/fair.c:7131:38: sparse: expected struct task_struct *curr kernel/sched/fair.c:7131:38: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/fair.c:10692:22: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/fair.c:10692:22: sparse: struct task_struct [noderef] __rcu * kernel/sched/fair.c:10692:22: sparse: struct task_struct * kernel/sched/fair.c:10825:30: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/fair.c:10825:30: sparse: struct task_struct [noderef] __rcu * kernel/sched/fair.c:10825:30: sparse: struct task_struct * kernel/sched/fair.c:5330:35: sparse: sparse: marked inline, but without a definition kernel/sched/sched.h:1803:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1803:9: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1803:9: sparse: struct task_struct * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sched/rt.c:912:70: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/rt.c:912:70: sparse: struct task_struct [noderef] __rcu * kernel/sched/rt.c:912:70: sparse: struct task_struct * kernel/sched/rt.c:529:54: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/rt.c:529:54: sparse: expected struct task_struct *curr kernel/sched/rt.c:529:54: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/rt.c:998:38: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/rt.c:998:38: sparse: expected struct task_struct *curr kernel/sched/rt.c:998:38: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/rt.c:1424:31: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected struct task_struct *p @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/rt.c:1424:31: sparse: expected struct task_struct *p kernel/sched/rt.c:1424:31: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/rt.c:2300:46: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/rt.c:2300:46: sparse: struct task_struct [noderef] __rcu * kernel/sched/rt.c:2300:46: sparse: struct task_struct * kernel/sched/rt.c:2320:22: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/rt.c:2320:22: sparse: struct task_struct [noderef] __rcu * kernel/sched/rt.c:2320:22: sparse: struct task_struct * kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sched/deadline.c:1721:42: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected struct sched_dl_entity *b @@ got struct sched_dl_entity [noderef] __rcu * @@ kernel/sched/deadline.c:1721:42: sparse: expected struct sched_dl_entity *b kernel/sched/deadline.c:1721:42: sparse: got struct sched_dl_entity [noderef] __rcu * kernel/sched/deadline.c:1054:23: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *p @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/deadline.c:1054:23: sparse: expected struct task_struct *p kernel/sched/deadline.c:1054:23: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/deadline.c:1183:38: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *curr @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/deadline.c:1183:38: sparse: expected struct task_struct *curr kernel/sched/deadline.c:1183:38: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/deadline.c:2385:22: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/deadline.c:2385:22: sparse: struct task_struct [noderef] __rcu * kernel/sched/deadline.c:2385:22: sparse: struct task_struct * kernel/sched/deadline.c:2404:46: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/deadline.c:2404:46: sparse: struct task_struct [noderef] __rcu * kernel/sched/deadline.c:2404:46: sparse: struct task_struct * kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type include/linux/sched/signal.h:693:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ include/linux/sched/signal.h:693:37: sparse: expected struct spinlock [usertype] *lock include/linux/sched/signal.h:693:37: sparse: got struct spinlock [noderef] __rcu * include/linux/sched/signal.h:693:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ include/linux/sched/signal.h:693:37: sparse: expected struct spinlock [usertype] *lock include/linux/sched/signal.h:693:37: sparse: got struct spinlock [noderef] __rcu * include/linux/sched/signal.h:693:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ include/linux/sched/signal.h:693:37: sparse: expected struct spinlock [usertype] *lock include/linux/sched/signal.h:693:37: sparse: got struct spinlock [noderef] __rcu * include/linux/sched/signal.h:693:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ include/linux/sched/signal.h:693:37: sparse: expected struct spinlock [usertype] *lock include/linux/sched/signal.h:693:37: sparse: got struct spinlock [noderef] __rcu * include/linux/sched/signal.h:693:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ include/linux/sched/signal.h:693:37: sparse: expected struct spinlock [usertype] *lock include/linux/sched/signal.h:693:37: sparse: got struct spinlock [noderef] __rcu * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sched/debug.c:435:22: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/debug.c:435:22: sparse: struct task_struct [noderef] __rcu * kernel/sched/debug.c:435:22: sparse: struct task_struct * kernel/sched/debug.c:643:9: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *tsk @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/debug.c:643:9: sparse: expected struct task_struct *tsk kernel/sched/debug.c:643:9: sparse: got struct task_struct [noderef] __rcu *curr kernel/sched/debug.c:643:9: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *tsk @@ got struct task_struct [noderef] __rcu *curr @@ kernel/sched/debug.c:643:9: sparse: expected struct task_struct *tsk kernel/sched/debug.c:643:9: sparse: got struct task_struct [noderef] __rcu *curr -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type kernel/sched/psi.c:1205:9: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/psi.c:1205:9: sparse: void [noderef] __rcu * kernel/sched/psi.c:1205:9: sparse: void * kernel/sched/psi.c:734:30: sparse: sparse: dereference of noderef expression kernel/sched/sched.h:1657:25: sparse: sparse: incompatible types in comparison expression (different address spaces): kernel/sched/sched.h:1657:25: sparse: struct task_struct [noderef] __rcu * kernel/sched/sched.h:1657:25: sparse: struct task_struct * -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type include/linux/gfp.h:325:27: sparse: sparse: restricted gfp_t degrades to integer -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/ata/libata-scsi.c:1784:13: sparse: sparse: context imbalance in 'ata_scsi_rbuf_get' - wrong count at exit drivers/ata/libata-scsi.c:1814:31: sparse: sparse: context imbalance in 'ata_scsi_rbuf_fill' - unexpected unlock -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/pci/pci-driver.c:494:42: sparse: sparse: restricted pci_power_t degrades to integer drivers/pci/pci-driver.c:494:61: sparse: sparse: restricted pci_power_t degrades to integer -- >> include/linux/suspend.h:470:15: sparse: sparse: 'hibernate_quiet_exec()' has implicit return type drivers/acpi/bus.c:37:20: sparse: sparse: symbol 'acpi_root' was not declared. Should it be static? # https://github.com/0day-ci/linux/commit/d55d8fef1e62acab40273b953e45a9d58f7e73c9 git remote add linux-review https://github.com/0day-ci/linux git remote update linux-review git checkout d55d8fef1e62acab40273b953e45a9d58f7e73c9 vim +470 include/linux/suspend.h d55d8fef1e62ac Dan Williams 2020-07-20 469 d55d8fef1e62ac Dan Williams 2020-07-20 @470 static inline hibernate_quiet_exec(int (*func)(void *data), void *data) { d55d8fef1e62ac Dan Williams 2020-07-20 471 return -ENOTSUPP; d55d8fef1e62ac Dan Williams 2020-07-20 472 } fce2b111fae915 Cornelia Huck 2009-06-10 473 #endif /* CONFIG_HIBERNATION */ fce2b111fae915 Cornelia Huck 2009-06-10 474 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
On Tue, Jul 21, 2020 at 12:24 AM Dan Williams <dan.j.williams@intel.com> wrote: > > Abstract platform specific mechanics for nvdimm firmware activation > behind a handful of generic ops. At the bus level ->activate_state() > indicates the unified state (idle, busy, armed) of all DIMMs on the bus, > and ->capability() indicates the system state expectations for activate. > At the DIMM level ->activate_state() indicates the per-DIMM state, > ->activate_result() indicates the outcome of the last activation > attempt, and ->arm() attempts to transition the DIMM from 'idle' to > 'armed'. > > A new hibernate_quiet_exec() facility is added to support firmware > activation in an OS defined system quiesce state. It leverages the fact > that the hibernate-freeze state wants to assert that a memory > hibernation snapshot can be taken. This is in contrast to a platform > firmware defined quiesce state that may forcefully quiet the memory > controller independent of whether an individual device-driver properly > supports hibernate-freeze. > > The libnvdimm sysfs interface is extended to support detection of a > firmware activate capability. The mechanism supports enumeration and > triggering of firmware activate, optionally in the > hibernate_quiet_exec() context. > > Cc: Pavel Machek <pavel@ucw.cz> > Cc: Ira Weiny <ira.weiny@intel.com> > Cc: Len Brown <len.brown@intel.com> > Cc: Jonathan Corbet <corbet@lwn.net> > Cc: Dave Jiang <dave.jiang@intel.com> > Cc: Vishal Verma <vishal.l.verma@intel.com> > [rafael: hibernate_quiet_exec() proposal] > Co-developed-by: "Rafael J. Wysocki" <rjw@rjwysocki.net> IMO it's better to change this to Co-developed-by: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> and please to add Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> to it as per the development process documentation. > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > --- > Documentation/ABI/testing/sysfs-bus-nvdimm | 2 > .../driver-api/nvdimm/firmware-activate.rst | 86 ++++++++++++ > drivers/nvdimm/core.c | 149 ++++++++++++++++++++ > drivers/nvdimm/dimm_devs.c | 115 +++++++++++++++ > drivers/nvdimm/nd-core.h | 1 > include/linux/libnvdimm.h | 44 ++++++ > include/linux/suspend.h | 6 + > kernel/power/hibernate.c | 97 +++++++++++++ > 8 files changed, 500 insertions(+) > create mode 100644 Documentation/ABI/testing/sysfs-bus-nvdimm > create mode 100644 Documentation/driver-api/nvdimm/firmware-activate.rst > > diff --git a/Documentation/ABI/testing/sysfs-bus-nvdimm b/Documentation/ABI/testing/sysfs-bus-nvdimm > new file mode 100644 > index 000000000000..d64380262be8 > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-bus-nvdimm > @@ -0,0 +1,2 @@ > +The libnvdimm sub-system implements a common sysfs interface for > +platform nvdimm resources. See Documentation/driver-api/nvdimm/. > diff --git a/Documentation/driver-api/nvdimm/firmware-activate.rst b/Documentation/driver-api/nvdimm/firmware-activate.rst > new file mode 100644 > index 000000000000..9eb98aa833c5 > --- /dev/null > +++ b/Documentation/driver-api/nvdimm/firmware-activate.rst > @@ -0,0 +1,86 @@ > +.. SPDX-License-Identifier: GPL-2.0 > + > +================================== > +NVDIMM Runtime Firmware Activation > +================================== > + > +Some persistent memory devices run a firmware locally on the device / > +"DIMM" to perform tasks like media management, capacity provisioning, > +and health monitoring. The process of updating that firmware typically > +involves a reboot because it has implications for in-flight memory > +transactions. However, reboots are disruptive and at least the Intel > +persistent memory platform implementation, described by the Intel ACPI > +DSM specification [1], has added support for activating firmware at > +runtime. > + > +A native sysfs interface is implemented in libnvdimm to allow platform > +to advertise and control their local runtime firmware activation > +capability. > + > +The libnvdimm bus object, ndbusX, implements an ndbusX/firmware/activate > +attribute that shows the state of the firmware activation as one of 'idle', > +'armed', 'overflow', and 'busy'. > + > +- idle: > + No devices are set / armed to activate firmware > + > +- armed: > + At least one device is armed > + > +- busy: > + In the busy state armed devices are in the process of transitioning > + back to idle and completing an activation cycle. > + > +- overflow: > + If the platform has a concept of incremental work needed to perform > + the activation it could be the case that too many DIMMs are armed for > + activation. In that scenario the potential for firmware activation to > + timeout is indicated by the 'overflow' state. > + > +The 'ndbusX/firmware/activate' property can be written with a value of > +either 'live', or 'quiesce'. A value of 'quiesce' triggers the kernel to > +run firmware activation from within the equivalent of the hibernation > +'freeze' state where drivers and applications are notified to stop their > +modifications of system memory. A value of 'live' attempts > +firmware-activation without this hibernation cycle. The > +'ndbusX/firmware/activate' property will be elided completely if no > +firmware activation capability is detected. > + > +Another property 'ndbusX/firmware/capability' indicates a value of > +'live', or 'quiesce'. Where 'live' indicates that the firmware > +does not require or inflict any quiesce period on the system to update > +firmware. A capability value of 'quiesce' indicates that firmware does > +expect and injects a quiet period for the memory controller, but 'live' > +may still be written to 'ndbusX/firmware/activate' as an override to > +assume the risk of racing firmware update with in-flight device and > +application activity. The 'ndbusX/firmware/capability' property will be > +elided completely if no firmware activation capability is detected. > + > +The libnvdimm memory-device / DIMM object, nmemX, implements > +'nmemX/firmware/activate' and 'nmemX/firmware/result' attributes to > +communicate the per-device firmware activation state. Similar to the > +'ndbusX/firmware/activate' attribute, the 'nmemX/firmware/activate' > +attribute indicates 'idle', 'armed', or 'busy'. The state transitions > +from 'armed' to 'idle' when the system is prepared to activate firmware, > +firmware staged + state set to armed, and 'ndbusX/firmware/activate' is > +triggered. After that activation event the nmemX/firmware/result > +attribute reflects the state of the last activation as one of: > + > +- none: > + No runtime activation triggered since the last time the device was reset > + > +- success: > + The last runtime activation completed successfully. > + > +- fail: > + The last runtime activation failed for device-specific reasons. > + > +- not_staged: > + The last runtime activation failed due to a sequencing error of the > + firmware image not being staged. > + > +- need_reset: > + Runtime firmware activation failed, but the firmware can still be > + activated via the legacy method of power-cycling the system. > + > +[1]: https://docs.pmem.io/persistent-memory/ > diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c > index fe9bd6febdd2..c21ba0602029 100644 > --- a/drivers/nvdimm/core.c > +++ b/drivers/nvdimm/core.c > @@ -4,6 +4,7 @@ > */ > #include <linux/libnvdimm.h> > #include <linux/badblocks.h> > +#include <linux/suspend.h> > #include <linux/export.h> > #include <linux/module.h> > #include <linux/blkdev.h> > @@ -389,8 +390,156 @@ static const struct attribute_group nvdimm_bus_attribute_group = { > .attrs = nvdimm_bus_attributes, > }; > > +static ssize_t capability_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); > + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; > + enum nvdimm_fwa_capability cap; > + > + if (!nd_desc->fw_ops) > + return -EOPNOTSUPP; > + > + nvdimm_bus_lock(dev); > + cap = nd_desc->fw_ops->capability(nd_desc); > + nvdimm_bus_unlock(dev); > + > + switch (cap) { > + case NVDIMM_FWA_CAP_QUIESCE: > + return sprintf(buf, "quiesce\n"); > + case NVDIMM_FWA_CAP_LIVE: > + return sprintf(buf, "live\n"); > + default: > + return -EOPNOTSUPP; > + } > +} > + > +static DEVICE_ATTR_RO(capability); > + > +static ssize_t activate_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); > + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; > + enum nvdimm_fwa_capability cap; > + enum nvdimm_fwa_state state; > + > + if (!nd_desc->fw_ops) > + return -EOPNOTSUPP; > + > + nvdimm_bus_lock(dev); > + cap = nd_desc->fw_ops->capability(nd_desc); > + state = nd_desc->fw_ops->activate_state(nd_desc); > + nvdimm_bus_unlock(dev); > + > + if (cap < NVDIMM_FWA_CAP_QUIESCE) > + return -EOPNOTSUPP; > + > + switch (state) { > + case NVDIMM_FWA_IDLE: > + return sprintf(buf, "idle\n"); > + case NVDIMM_FWA_BUSY: > + return sprintf(buf, "busy\n"); > + case NVDIMM_FWA_ARMED: > + return sprintf(buf, "armed\n"); > + case NVDIMM_FWA_ARM_OVERFLOW: > + return sprintf(buf, "overflow\n"); > + default: > + return -ENXIO; > + } > +} > + > +static int exec_firmware_activate(void *data) > +{ > + struct nvdimm_bus_descriptor *nd_desc = data; > + > + return nd_desc->fw_ops->activate(nd_desc); > +} > + > +static ssize_t activate_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); > + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; > + enum nvdimm_fwa_state state; > + bool quiesce; > + ssize_t rc; > + > + if (!nd_desc->fw_ops) > + return -EOPNOTSUPP; > + > + if (sysfs_streq(buf, "live")) > + quiesce = false; > + else if (sysfs_streq(buf, "quiesce")) > + quiesce = true; > + else > + return -EINVAL; > + > + nvdimm_bus_lock(dev); > + state = nd_desc->fw_ops->activate_state(nd_desc); > + > + switch (state) { > + case NVDIMM_FWA_BUSY: > + rc = -EBUSY; > + break; > + case NVDIMM_FWA_ARMED: > + case NVDIMM_FWA_ARM_OVERFLOW: > + if (quiesce) > + rc = hibernate_quiet_exec(exec_firmware_activate, nd_desc); > + else > + rc = nd_desc->fw_ops->activate(nd_desc); > + break; > + case NVDIMM_FWA_IDLE: > + default: > + rc = -ENXIO; > + } > + nvdimm_bus_unlock(dev); > + > + if (rc == 0) > + rc = len; > + return rc; > +} > + > +static DEVICE_ATTR_ADMIN_RW(activate); > + > +static umode_t nvdimm_bus_firmware_visible(struct kobject *kobj, struct attribute *a, int n) > +{ > + struct device *dev = container_of(kobj, typeof(*dev), kobj); > + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); > + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; > + enum nvdimm_fwa_capability cap; > + > + /* > + * Both 'activate' and 'capability' disappear when no ops > + * detected, or a negative capability is indicated. > + */ > + if (!nd_desc->fw_ops) > + return 0; > + > + nvdimm_bus_lock(dev); > + cap = nd_desc->fw_ops->capability(nd_desc); > + nvdimm_bus_unlock(dev); > + > + if (cap < NVDIMM_FWA_CAP_QUIESCE) > + return 0; > + > + return a->mode; > +} > +static struct attribute *nvdimm_bus_firmware_attributes[] = { > + &dev_attr_activate.attr, > + &dev_attr_capability.attr, > + NULL, > +}; > + > +static const struct attribute_group nvdimm_bus_firmware_attribute_group = { > + .name = "firmware", > + .attrs = nvdimm_bus_firmware_attributes, > + .is_visible = nvdimm_bus_firmware_visible, > +}; > + > const struct attribute_group *nvdimm_bus_attribute_groups[] = { > &nvdimm_bus_attribute_group, > + &nvdimm_bus_firmware_attribute_group, > NULL, > }; > > diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c > index b7b77e8d9027..85b53a7f44f2 100644 > --- a/drivers/nvdimm/dimm_devs.c > +++ b/drivers/nvdimm/dimm_devs.c > @@ -446,9 +446,124 @@ static const struct attribute_group nvdimm_attribute_group = { > .is_visible = nvdimm_visible, > }; > > +static ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf) > +{ > + struct nvdimm *nvdimm = to_nvdimm(dev); > + enum nvdimm_fwa_result result; > + > + if (!nvdimm->fw_ops) > + return -EOPNOTSUPP; > + > + nvdimm_bus_lock(dev); > + result = nvdimm->fw_ops->activate_result(nvdimm); > + nvdimm_bus_unlock(dev); > + > + switch (result) { > + case NVDIMM_FWA_RESULT_NONE: > + return sprintf(buf, "none\n"); > + case NVDIMM_FWA_RESULT_SUCCESS: > + return sprintf(buf, "success\n"); > + case NVDIMM_FWA_RESULT_FAIL: > + return sprintf(buf, "fail\n"); > + case NVDIMM_FWA_RESULT_NOTSTAGED: > + return sprintf(buf, "not_staged\n"); > + case NVDIMM_FWA_RESULT_NEEDRESET: > + return sprintf(buf, "need_reset\n"); > + default: > + return -ENXIO; > + } > +} > +static DEVICE_ATTR_ADMIN_RO(result); > + > +static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf) > +{ > + struct nvdimm *nvdimm = to_nvdimm(dev); > + enum nvdimm_fwa_state state; > + > + if (!nvdimm->fw_ops) > + return -EOPNOTSUPP; > + > + nvdimm_bus_lock(dev); > + state = nvdimm->fw_ops->activate_state(nvdimm); > + nvdimm_bus_unlock(dev); > + > + switch (state) { > + case NVDIMM_FWA_IDLE: > + return sprintf(buf, "idle\n"); > + case NVDIMM_FWA_BUSY: > + return sprintf(buf, "busy\n"); > + case NVDIMM_FWA_ARMED: > + return sprintf(buf, "armed\n"); > + default: > + return -ENXIO; > + } > +} > + > +static ssize_t activate_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct nvdimm *nvdimm = to_nvdimm(dev); > + enum nvdimm_fwa_trigger arg; > + int rc; > + > + if (!nvdimm->fw_ops) > + return -EOPNOTSUPP; > + > + if (sysfs_streq(buf, "arm")) > + arg = NVDIMM_FWA_ARM; > + else if (sysfs_streq(buf, "disarm")) > + arg = NVDIMM_FWA_DISARM; > + else > + return -EINVAL; > + > + nvdimm_bus_lock(dev); > + rc = nvdimm->fw_ops->arm(nvdimm, arg); > + nvdimm_bus_unlock(dev); > + > + if (rc < 0) > + return rc; > + return len; > +} > +static DEVICE_ATTR_ADMIN_RW(activate); > + > +static struct attribute *nvdimm_firmware_attributes[] = { > + &dev_attr_activate.attr, > + &dev_attr_result.attr, > +}; > + > +static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n) > +{ > + struct device *dev = container_of(kobj, typeof(*dev), kobj); > + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); > + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; > + struct nvdimm *nvdimm = to_nvdimm(dev); > + enum nvdimm_fwa_capability cap; > + > + if (!nd_desc->fw_ops) > + return 0; > + if (!nvdimm->fw_ops) > + return 0; > + > + nvdimm_bus_lock(dev); > + cap = nd_desc->fw_ops->capability(nd_desc); > + nvdimm_bus_unlock(dev); > + > + if (cap < NVDIMM_FWA_CAP_QUIESCE) > + return 0; > + > + return a->mode; > +} > + > +static const struct attribute_group nvdimm_firmware_attribute_group = { > + .name = "firmware", > + .attrs = nvdimm_firmware_attributes, > + .is_visible = nvdimm_firmware_visible, > +}; > + > static const struct attribute_group *nvdimm_attribute_groups[] = { > &nd_device_attribute_group, > &nvdimm_attribute_group, > + &nvdimm_firmware_attribute_group, > NULL, > }; > > diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h > index ddb9d97d9129..564faa36a3ca 100644 > --- a/drivers/nvdimm/nd-core.h > +++ b/drivers/nvdimm/nd-core.h > @@ -45,6 +45,7 @@ struct nvdimm { > struct kernfs_node *overwrite_state; > } sec; > struct delayed_work dwork; > + const struct nvdimm_fw_ops *fw_ops; > }; > > static inline unsigned long nvdimm_security_flags( > diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h > index ad9898ece7d3..15dbcb718316 100644 > --- a/include/linux/libnvdimm.h > +++ b/include/linux/libnvdimm.h > @@ -86,6 +86,7 @@ struct nvdimm_bus_descriptor { > int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc); > int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc, > struct nvdimm *nvdimm, unsigned int cmd, void *data); > + const struct nvdimm_bus_fw_ops *fw_ops; > }; > > struct nd_cmd_desc { > @@ -200,6 +201,49 @@ struct nvdimm_security_ops { > int (*query_overwrite)(struct nvdimm *nvdimm); > }; > > +enum nvdimm_fwa_state { > + NVDIMM_FWA_INVALID, > + NVDIMM_FWA_IDLE, > + NVDIMM_FWA_ARMED, > + NVDIMM_FWA_BUSY, > + NVDIMM_FWA_ARM_OVERFLOW, > +}; > + > +enum nvdimm_fwa_trigger { > + NVDIMM_FWA_ARM, > + NVDIMM_FWA_DISARM, > +}; > + > +enum nvdimm_fwa_capability { > + NVDIMM_FWA_CAP_INVALID, > + NVDIMM_FWA_CAP_NONE, > + NVDIMM_FWA_CAP_QUIESCE, > + NVDIMM_FWA_CAP_LIVE, > +}; > + > +enum nvdimm_fwa_result { > + NVDIMM_FWA_RESULT_INVALID, > + NVDIMM_FWA_RESULT_NONE, > + NVDIMM_FWA_RESULT_SUCCESS, > + NVDIMM_FWA_RESULT_NOTSTAGED, > + NVDIMM_FWA_RESULT_NEEDRESET, > + NVDIMM_FWA_RESULT_FAIL, > +}; > + > +struct nvdimm_bus_fw_ops { > + enum nvdimm_fwa_state (*activate_state) > + (struct nvdimm_bus_descriptor *nd_desc); > + enum nvdimm_fwa_capability (*capability) > + (struct nvdimm_bus_descriptor *nd_desc); > + int (*activate)(struct nvdimm_bus_descriptor *nd_desc); > +}; > + > +struct nvdimm_fw_ops { > + enum nvdimm_fwa_state (*activate_state)(struct nvdimm *nvdimm); > + enum nvdimm_fwa_result (*activate_result)(struct nvdimm *nvdimm); > + int (*arm)(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arg); > +}; > + > void badrange_init(struct badrange *badrange); > int badrange_add(struct badrange *badrange, u64 addr, u64 length); > void badrange_forget(struct badrange *badrange, phys_addr_t start, > diff --git a/include/linux/suspend.h b/include/linux/suspend.h > index b960098acfb0..045499699b86 100644 > --- a/include/linux/suspend.h > +++ b/include/linux/suspend.h > @@ -453,6 +453,8 @@ extern bool hibernation_available(void); > asmlinkage int swsusp_save(void); > extern struct pbe *restore_pblist; > int pfn_is_nosave(unsigned long pfn); > + > +int hibernate_quiet_exec(int (*func)(void *data), void *data); > #else /* CONFIG_HIBERNATION */ > static inline void register_nosave_region(unsigned long b, unsigned long e) {} > static inline void register_nosave_region_late(unsigned long b, unsigned long e) {} > @@ -464,6 +466,10 @@ static inline void hibernation_set_ops(const struct platform_hibernation_ops *op > static inline int hibernate(void) { return -ENOSYS; } > static inline bool system_entering_hibernation(void) { return false; } > static inline bool hibernation_available(void) { return false; } > + > +static inline hibernate_quiet_exec(int (*func)(void *data), void *data) { This needs to be "static inline int". Thanks!
On Mon, 2020-07-27 at 14:37 +0200, Rafael J. Wysocki wrote: > On Tue, Jul 21, 2020 at 12:24 AM Dan Williams <dan.j.williams@intel.com> wrote: > > Abstract platform specific mechanics for nvdimm firmware activation > > behind a handful of generic ops. At the bus level ->activate_state() > > indicates the unified state (idle, busy, armed) of all DIMMs on the bus, > > and ->capability() indicates the system state expectations for activate. > > At the DIMM level ->activate_state() indicates the per-DIMM state, > > ->activate_result() indicates the outcome of the last activation > > attempt, and ->arm() attempts to transition the DIMM from 'idle' to > > 'armed'. > > > > A new hibernate_quiet_exec() facility is added to support firmware > > activation in an OS defined system quiesce state. It leverages the fact > > that the hibernate-freeze state wants to assert that a memory > > hibernation snapshot can be taken. This is in contrast to a platform > > firmware defined quiesce state that may forcefully quiet the memory > > controller independent of whether an individual device-driver properly > > supports hibernate-freeze. > > > > The libnvdimm sysfs interface is extended to support detection of a > > firmware activate capability. The mechanism supports enumeration and > > triggering of firmware activate, optionally in the > > hibernate_quiet_exec() context. > > > > Cc: Pavel Machek <pavel@ucw.cz> > > Cc: Ira Weiny <ira.weiny@intel.com> > > Cc: Len Brown <len.brown@intel.com> > > Cc: Jonathan Corbet <corbet@lwn.net> > > Cc: Dave Jiang <dave.jiang@intel.com> > > Cc: Vishal Verma <vishal.l.verma@intel.com> > > [rafael: hibernate_quiet_exec() proposal] > > Co-developed-by: "Rafael J. Wysocki" <rjw@rjwysocki.net> > > IMO it's better to change this to > > Co-developed-by: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> > > and please to add > > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > to it as per the development process documentation. Thanks Rafael, I've fixed this up in the branch I've prepared for the pull request: https://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git/log/?h=libnvdimm-for-next > > > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > > --- > > Documentation/ABI/testing/sysfs-bus-nvdimm | 2 > > .../driver-api/nvdimm/firmware-activate.rst | 86 ++++++++++++ > > drivers/nvdimm/core.c | 149 ++++++++++++++++++++ > > drivers/nvdimm/dimm_devs.c | 115 +++++++++++++++ > > drivers/nvdimm/nd-core.h | 1 > > include/linux/libnvdimm.h | 44 ++++++ > > include/linux/suspend.h | 6 + > > kernel/power/hibernate.c | 97 +++++++++++++ > > 8 files changed, 500 insertions(+) > > create mode 100644 Documentation/ABI/testing/sysfs-bus-nvdimm > > create mode 100644 Documentation/driver-api/nvdimm/firmware-activate.rst > > [..] > > @@ -464,6 +466,10 @@ static inline void hibernation_set_ops(const struct platform_hibernation_ops *op > > static inline int hibernate(void) { return -ENOSYS; } > > static inline bool system_entering_hibernation(void) { return false; } > > static inline bool hibernation_available(void) { return false; } > > + > > +static inline hibernate_quiet_exec(int (*func)(void *data), void *data) { > > This needs to be "static inline int". > Yep I got a build warning for this and also fixed it up. Thanks, -Vishal
On Wed, Jul 29, 2020 at 3:35 AM Vishal Verma <vishal.l.verma@intel.com> wrote: > > On Mon, 2020-07-27 at 14:37 +0200, Rafael J. Wysocki wrote: > > On Tue, Jul 21, 2020 at 12:24 AM Dan Williams <dan.j.williams@intel.com> wrote: > > > Abstract platform specific mechanics for nvdimm firmware activation > > > behind a handful of generic ops. At the bus level ->activate_state() > > > indicates the unified state (idle, busy, armed) of all DIMMs on the bus, > > > and ->capability() indicates the system state expectations for activate. > > > At the DIMM level ->activate_state() indicates the per-DIMM state, > > > ->activate_result() indicates the outcome of the last activation > > > attempt, and ->arm() attempts to transition the DIMM from 'idle' to > > > 'armed'. > > > > > > A new hibernate_quiet_exec() facility is added to support firmware > > > activation in an OS defined system quiesce state. It leverages the fact > > > that the hibernate-freeze state wants to assert that a memory > > > hibernation snapshot can be taken. This is in contrast to a platform > > > firmware defined quiesce state that may forcefully quiet the memory > > > controller independent of whether an individual device-driver properly > > > supports hibernate-freeze. > > > > > > The libnvdimm sysfs interface is extended to support detection of a > > > firmware activate capability. The mechanism supports enumeration and > > > triggering of firmware activate, optionally in the > > > hibernate_quiet_exec() context. > > > > > > Cc: Pavel Machek <pavel@ucw.cz> > > > Cc: Ira Weiny <ira.weiny@intel.com> > > > Cc: Len Brown <len.brown@intel.com> > > > Cc: Jonathan Corbet <corbet@lwn.net> > > > Cc: Dave Jiang <dave.jiang@intel.com> > > > Cc: Vishal Verma <vishal.l.verma@intel.com> > > > [rafael: hibernate_quiet_exec() proposal] > > > Co-developed-by: "Rafael J. Wysocki" <rjw@rjwysocki.net> > > > > IMO it's better to change this to > > > > Co-developed-by: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> > > > > and please to add > > > > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > > > to it as per the development process documentation. > > Thanks Rafael, I've fixed this up in the branch I've prepared for the pull > request: > > https://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git/log/?h=libnvdimm-for-next LGTM, thanks!
diff --git a/Documentation/ABI/testing/sysfs-bus-nvdimm b/Documentation/ABI/testing/sysfs-bus-nvdimm new file mode 100644 index 000000000000..d64380262be8 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-nvdimm @@ -0,0 +1,2 @@ +The libnvdimm sub-system implements a common sysfs interface for +platform nvdimm resources. See Documentation/driver-api/nvdimm/. diff --git a/Documentation/driver-api/nvdimm/firmware-activate.rst b/Documentation/driver-api/nvdimm/firmware-activate.rst new file mode 100644 index 000000000000..9eb98aa833c5 --- /dev/null +++ b/Documentation/driver-api/nvdimm/firmware-activate.rst @@ -0,0 +1,86 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================== +NVDIMM Runtime Firmware Activation +================================== + +Some persistent memory devices run a firmware locally on the device / +"DIMM" to perform tasks like media management, capacity provisioning, +and health monitoring. The process of updating that firmware typically +involves a reboot because it has implications for in-flight memory +transactions. However, reboots are disruptive and at least the Intel +persistent memory platform implementation, described by the Intel ACPI +DSM specification [1], has added support for activating firmware at +runtime. + +A native sysfs interface is implemented in libnvdimm to allow platform +to advertise and control their local runtime firmware activation +capability. + +The libnvdimm bus object, ndbusX, implements an ndbusX/firmware/activate +attribute that shows the state of the firmware activation as one of 'idle', +'armed', 'overflow', and 'busy'. + +- idle: + No devices are set / armed to activate firmware + +- armed: + At least one device is armed + +- busy: + In the busy state armed devices are in the process of transitioning + back to idle and completing an activation cycle. + +- overflow: + If the platform has a concept of incremental work needed to perform + the activation it could be the case that too many DIMMs are armed for + activation. In that scenario the potential for firmware activation to + timeout is indicated by the 'overflow' state. + +The 'ndbusX/firmware/activate' property can be written with a value of +either 'live', or 'quiesce'. A value of 'quiesce' triggers the kernel to +run firmware activation from within the equivalent of the hibernation +'freeze' state where drivers and applications are notified to stop their +modifications of system memory. A value of 'live' attempts +firmware-activation without this hibernation cycle. The +'ndbusX/firmware/activate' property will be elided completely if no +firmware activation capability is detected. + +Another property 'ndbusX/firmware/capability' indicates a value of +'live', or 'quiesce'. Where 'live' indicates that the firmware +does not require or inflict any quiesce period on the system to update +firmware. A capability value of 'quiesce' indicates that firmware does +expect and injects a quiet period for the memory controller, but 'live' +may still be written to 'ndbusX/firmware/activate' as an override to +assume the risk of racing firmware update with in-flight device and +application activity. The 'ndbusX/firmware/capability' property will be +elided completely if no firmware activation capability is detected. + +The libnvdimm memory-device / DIMM object, nmemX, implements +'nmemX/firmware/activate' and 'nmemX/firmware/result' attributes to +communicate the per-device firmware activation state. Similar to the +'ndbusX/firmware/activate' attribute, the 'nmemX/firmware/activate' +attribute indicates 'idle', 'armed', or 'busy'. The state transitions +from 'armed' to 'idle' when the system is prepared to activate firmware, +firmware staged + state set to armed, and 'ndbusX/firmware/activate' is +triggered. After that activation event the nmemX/firmware/result +attribute reflects the state of the last activation as one of: + +- none: + No runtime activation triggered since the last time the device was reset + +- success: + The last runtime activation completed successfully. + +- fail: + The last runtime activation failed for device-specific reasons. + +- not_staged: + The last runtime activation failed due to a sequencing error of the + firmware image not being staged. + +- need_reset: + Runtime firmware activation failed, but the firmware can still be + activated via the legacy method of power-cycling the system. + +[1]: https://docs.pmem.io/persistent-memory/ diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index fe9bd6febdd2..c21ba0602029 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -4,6 +4,7 @@ */ #include <linux/libnvdimm.h> #include <linux/badblocks.h> +#include <linux/suspend.h> #include <linux/export.h> #include <linux/module.h> #include <linux/blkdev.h> @@ -389,8 +390,156 @@ static const struct attribute_group nvdimm_bus_attribute_group = { .attrs = nvdimm_bus_attributes, }; +static ssize_t capability_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; + enum nvdimm_fwa_capability cap; + + if (!nd_desc->fw_ops) + return -EOPNOTSUPP; + + nvdimm_bus_lock(dev); + cap = nd_desc->fw_ops->capability(nd_desc); + nvdimm_bus_unlock(dev); + + switch (cap) { + case NVDIMM_FWA_CAP_QUIESCE: + return sprintf(buf, "quiesce\n"); + case NVDIMM_FWA_CAP_LIVE: + return sprintf(buf, "live\n"); + default: + return -EOPNOTSUPP; + } +} + +static DEVICE_ATTR_RO(capability); + +static ssize_t activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; + enum nvdimm_fwa_capability cap; + enum nvdimm_fwa_state state; + + if (!nd_desc->fw_ops) + return -EOPNOTSUPP; + + nvdimm_bus_lock(dev); + cap = nd_desc->fw_ops->capability(nd_desc); + state = nd_desc->fw_ops->activate_state(nd_desc); + nvdimm_bus_unlock(dev); + + if (cap < NVDIMM_FWA_CAP_QUIESCE) + return -EOPNOTSUPP; + + switch (state) { + case NVDIMM_FWA_IDLE: + return sprintf(buf, "idle\n"); + case NVDIMM_FWA_BUSY: + return sprintf(buf, "busy\n"); + case NVDIMM_FWA_ARMED: + return sprintf(buf, "armed\n"); + case NVDIMM_FWA_ARM_OVERFLOW: + return sprintf(buf, "overflow\n"); + default: + return -ENXIO; + } +} + +static int exec_firmware_activate(void *data) +{ + struct nvdimm_bus_descriptor *nd_desc = data; + + return nd_desc->fw_ops->activate(nd_desc); +} + +static ssize_t activate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; + enum nvdimm_fwa_state state; + bool quiesce; + ssize_t rc; + + if (!nd_desc->fw_ops) + return -EOPNOTSUPP; + + if (sysfs_streq(buf, "live")) + quiesce = false; + else if (sysfs_streq(buf, "quiesce")) + quiesce = true; + else + return -EINVAL; + + nvdimm_bus_lock(dev); + state = nd_desc->fw_ops->activate_state(nd_desc); + + switch (state) { + case NVDIMM_FWA_BUSY: + rc = -EBUSY; + break; + case NVDIMM_FWA_ARMED: + case NVDIMM_FWA_ARM_OVERFLOW: + if (quiesce) + rc = hibernate_quiet_exec(exec_firmware_activate, nd_desc); + else + rc = nd_desc->fw_ops->activate(nd_desc); + break; + case NVDIMM_FWA_IDLE: + default: + rc = -ENXIO; + } + nvdimm_bus_unlock(dev); + + if (rc == 0) + rc = len; + return rc; +} + +static DEVICE_ATTR_ADMIN_RW(activate); + +static umode_t nvdimm_bus_firmware_visible(struct kobject *kobj, struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, typeof(*dev), kobj); + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; + enum nvdimm_fwa_capability cap; + + /* + * Both 'activate' and 'capability' disappear when no ops + * detected, or a negative capability is indicated. + */ + if (!nd_desc->fw_ops) + return 0; + + nvdimm_bus_lock(dev); + cap = nd_desc->fw_ops->capability(nd_desc); + nvdimm_bus_unlock(dev); + + if (cap < NVDIMM_FWA_CAP_QUIESCE) + return 0; + + return a->mode; +} +static struct attribute *nvdimm_bus_firmware_attributes[] = { + &dev_attr_activate.attr, + &dev_attr_capability.attr, + NULL, +}; + +static const struct attribute_group nvdimm_bus_firmware_attribute_group = { + .name = "firmware", + .attrs = nvdimm_bus_firmware_attributes, + .is_visible = nvdimm_bus_firmware_visible, +}; + const struct attribute_group *nvdimm_bus_attribute_groups[] = { &nvdimm_bus_attribute_group, + &nvdimm_bus_firmware_attribute_group, NULL, }; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index b7b77e8d9027..85b53a7f44f2 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -446,9 +446,124 @@ static const struct attribute_group nvdimm_attribute_group = { .is_visible = nvdimm_visible, }; +static ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + enum nvdimm_fwa_result result; + + if (!nvdimm->fw_ops) + return -EOPNOTSUPP; + + nvdimm_bus_lock(dev); + result = nvdimm->fw_ops->activate_result(nvdimm); + nvdimm_bus_unlock(dev); + + switch (result) { + case NVDIMM_FWA_RESULT_NONE: + return sprintf(buf, "none\n"); + case NVDIMM_FWA_RESULT_SUCCESS: + return sprintf(buf, "success\n"); + case NVDIMM_FWA_RESULT_FAIL: + return sprintf(buf, "fail\n"); + case NVDIMM_FWA_RESULT_NOTSTAGED: + return sprintf(buf, "not_staged\n"); + case NVDIMM_FWA_RESULT_NEEDRESET: + return sprintf(buf, "need_reset\n"); + default: + return -ENXIO; + } +} +static DEVICE_ATTR_ADMIN_RO(result); + +static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + enum nvdimm_fwa_state state; + + if (!nvdimm->fw_ops) + return -EOPNOTSUPP; + + nvdimm_bus_lock(dev); + state = nvdimm->fw_ops->activate_state(nvdimm); + nvdimm_bus_unlock(dev); + + switch (state) { + case NVDIMM_FWA_IDLE: + return sprintf(buf, "idle\n"); + case NVDIMM_FWA_BUSY: + return sprintf(buf, "busy\n"); + case NVDIMM_FWA_ARMED: + return sprintf(buf, "armed\n"); + default: + return -ENXIO; + } +} + +static ssize_t activate_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + enum nvdimm_fwa_trigger arg; + int rc; + + if (!nvdimm->fw_ops) + return -EOPNOTSUPP; + + if (sysfs_streq(buf, "arm")) + arg = NVDIMM_FWA_ARM; + else if (sysfs_streq(buf, "disarm")) + arg = NVDIMM_FWA_DISARM; + else + return -EINVAL; + + nvdimm_bus_lock(dev); + rc = nvdimm->fw_ops->arm(nvdimm, arg); + nvdimm_bus_unlock(dev); + + if (rc < 0) + return rc; + return len; +} +static DEVICE_ATTR_ADMIN_RW(activate); + +static struct attribute *nvdimm_firmware_attributes[] = { + &dev_attr_activate.attr, + &dev_attr_result.attr, +}; + +static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, typeof(*dev), kobj); + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); + struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; + struct nvdimm *nvdimm = to_nvdimm(dev); + enum nvdimm_fwa_capability cap; + + if (!nd_desc->fw_ops) + return 0; + if (!nvdimm->fw_ops) + return 0; + + nvdimm_bus_lock(dev); + cap = nd_desc->fw_ops->capability(nd_desc); + nvdimm_bus_unlock(dev); + + if (cap < NVDIMM_FWA_CAP_QUIESCE) + return 0; + + return a->mode; +} + +static const struct attribute_group nvdimm_firmware_attribute_group = { + .name = "firmware", + .attrs = nvdimm_firmware_attributes, + .is_visible = nvdimm_firmware_visible, +}; + static const struct attribute_group *nvdimm_attribute_groups[] = { &nd_device_attribute_group, &nvdimm_attribute_group, + &nvdimm_firmware_attribute_group, NULL, }; diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index ddb9d97d9129..564faa36a3ca 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -45,6 +45,7 @@ struct nvdimm { struct kernfs_node *overwrite_state; } sec; struct delayed_work dwork; + const struct nvdimm_fw_ops *fw_ops; }; static inline unsigned long nvdimm_security_flags( diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index ad9898ece7d3..15dbcb718316 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -86,6 +86,7 @@ struct nvdimm_bus_descriptor { int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc); int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *data); + const struct nvdimm_bus_fw_ops *fw_ops; }; struct nd_cmd_desc { @@ -200,6 +201,49 @@ struct nvdimm_security_ops { int (*query_overwrite)(struct nvdimm *nvdimm); }; +enum nvdimm_fwa_state { + NVDIMM_FWA_INVALID, + NVDIMM_FWA_IDLE, + NVDIMM_FWA_ARMED, + NVDIMM_FWA_BUSY, + NVDIMM_FWA_ARM_OVERFLOW, +}; + +enum nvdimm_fwa_trigger { + NVDIMM_FWA_ARM, + NVDIMM_FWA_DISARM, +}; + +enum nvdimm_fwa_capability { + NVDIMM_FWA_CAP_INVALID, + NVDIMM_FWA_CAP_NONE, + NVDIMM_FWA_CAP_QUIESCE, + NVDIMM_FWA_CAP_LIVE, +}; + +enum nvdimm_fwa_result { + NVDIMM_FWA_RESULT_INVALID, + NVDIMM_FWA_RESULT_NONE, + NVDIMM_FWA_RESULT_SUCCESS, + NVDIMM_FWA_RESULT_NOTSTAGED, + NVDIMM_FWA_RESULT_NEEDRESET, + NVDIMM_FWA_RESULT_FAIL, +}; + +struct nvdimm_bus_fw_ops { + enum nvdimm_fwa_state (*activate_state) + (struct nvdimm_bus_descriptor *nd_desc); + enum nvdimm_fwa_capability (*capability) + (struct nvdimm_bus_descriptor *nd_desc); + int (*activate)(struct nvdimm_bus_descriptor *nd_desc); +}; + +struct nvdimm_fw_ops { + enum nvdimm_fwa_state (*activate_state)(struct nvdimm *nvdimm); + enum nvdimm_fwa_result (*activate_result)(struct nvdimm *nvdimm); + int (*arm)(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arg); +}; + void badrange_init(struct badrange *badrange); int badrange_add(struct badrange *badrange, u64 addr, u64 length); void badrange_forget(struct badrange *badrange, phys_addr_t start, diff --git a/include/linux/suspend.h b/include/linux/suspend.h index b960098acfb0..045499699b86 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -453,6 +453,8 @@ extern bool hibernation_available(void); asmlinkage int swsusp_save(void); extern struct pbe *restore_pblist; int pfn_is_nosave(unsigned long pfn); + +int hibernate_quiet_exec(int (*func)(void *data), void *data); #else /* CONFIG_HIBERNATION */ static inline void register_nosave_region(unsigned long b, unsigned long e) {} static inline void register_nosave_region_late(unsigned long b, unsigned long e) {} @@ -464,6 +466,10 @@ static inline void hibernation_set_ops(const struct platform_hibernation_ops *op static inline int hibernate(void) { return -ENOSYS; } static inline bool system_entering_hibernation(void) { return false; } static inline bool hibernation_available(void) { return false; } + +static inline hibernate_quiet_exec(int (*func)(void *data), void *data) { + return -ENOTSUPP; +} #endif /* CONFIG_HIBERNATION */ #ifdef CONFIG_HIBERNATION_SNAPSHOT_DEV diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 02ec716a4927..e6fab3f09c98 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -795,6 +795,103 @@ int hibernate(void) return error; } +/** + * hibernate_quiet_exec - Execute a function with all devices frozen. + * @func: Function to execute. + * @data: Data pointer to pass to @func. + * + * Return the @func return value or an error code if it cannot be executed. + */ +int hibernate_quiet_exec(int (*func)(void *data), void *data) +{ + int error, nr_calls = 0; + + lock_system_sleep(); + + if (!hibernate_acquire()) { + error = -EBUSY; + goto unlock; + } + + pm_prepare_console(); + + error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); + if (error) { + nr_calls--; + goto exit; + } + + error = freeze_processes(); + if (error) + goto exit; + + lock_device_hotplug(); + + pm_suspend_clear_flags(); + + error = platform_begin(true); + if (error) + goto thaw; + + error = freeze_kernel_threads(); + if (error) + goto thaw; + + error = dpm_prepare(PMSG_FREEZE); + if (error) + goto dpm_complete; + + suspend_console(); + + error = dpm_suspend(PMSG_FREEZE); + if (error) + goto dpm_resume; + + error = dpm_suspend_end(PMSG_FREEZE); + if (error) + goto dpm_resume; + + error = platform_pre_snapshot(true); + if (error) + goto skip; + + error = func(data); + +skip: + platform_finish(true); + + dpm_resume_start(PMSG_THAW); + +dpm_resume: + dpm_resume(PMSG_THAW); + + resume_console(); + +dpm_complete: + dpm_complete(PMSG_THAW); + + thaw_kernel_threads(); + +thaw: + platform_end(true); + + unlock_device_hotplug(); + + thaw_processes(); + +exit: + __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL); + + pm_restore_console(); + + hibernate_release(); + +unlock: + unlock_system_sleep(); + + return error; +} +EXPORT_SYMBOL_GPL(hibernate_quiet_exec); /** * software_resume - Resume from a saved hibernation image.
Abstract platform specific mechanics for nvdimm firmware activation behind a handful of generic ops. At the bus level ->activate_state() indicates the unified state (idle, busy, armed) of all DIMMs on the bus, and ->capability() indicates the system state expectations for activate. At the DIMM level ->activate_state() indicates the per-DIMM state, ->activate_result() indicates the outcome of the last activation attempt, and ->arm() attempts to transition the DIMM from 'idle' to 'armed'. A new hibernate_quiet_exec() facility is added to support firmware activation in an OS defined system quiesce state. It leverages the fact that the hibernate-freeze state wants to assert that a memory hibernation snapshot can be taken. This is in contrast to a platform firmware defined quiesce state that may forcefully quiet the memory controller independent of whether an individual device-driver properly supports hibernate-freeze. The libnvdimm sysfs interface is extended to support detection of a firmware activate capability. The mechanism supports enumeration and triggering of firmware activate, optionally in the hibernate_quiet_exec() context. Cc: Pavel Machek <pavel@ucw.cz> Cc: Ira Weiny <ira.weiny@intel.com> Cc: Len Brown <len.brown@intel.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Dave Jiang <dave.jiang@intel.com> Cc: Vishal Verma <vishal.l.verma@intel.com> [rafael: hibernate_quiet_exec() proposal] Co-developed-by: "Rafael J. Wysocki" <rjw@rjwysocki.net> Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- Documentation/ABI/testing/sysfs-bus-nvdimm | 2 .../driver-api/nvdimm/firmware-activate.rst | 86 ++++++++++++ drivers/nvdimm/core.c | 149 ++++++++++++++++++++ drivers/nvdimm/dimm_devs.c | 115 +++++++++++++++ drivers/nvdimm/nd-core.h | 1 include/linux/libnvdimm.h | 44 ++++++ include/linux/suspend.h | 6 + kernel/power/hibernate.c | 97 +++++++++++++ 8 files changed, 500 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-nvdimm create mode 100644 Documentation/driver-api/nvdimm/firmware-activate.rst