History

PM, libnvdimm: Add runtime firmware activation support

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. [rafael: hibernate_quiet_exec() proposal] [vishal: fix up sparse warning, grammar in Documentation/]

Dan Williams committed 6 years ago

Commit 48001ea1 parent 5cf81ce8 files · +500 −0Browse files

Unified · Split

8 files
  1. Documentation/ABI/testing/sysfs-bus-nvdimm +2 −0
  2. Documentation/driver-api/nvdimm/firmware-activate.rst +86 −0
  3. drivers/nvdimm/core.c +149 −0
  4. drivers/nvdimm/dimm_devs.c +115 −0
  5. drivers/nvdimm/nd-core.h +1 −0
  6. include/linux/libnvdimm.h +44 −0
  7. include/linux/suspend.h +6 −0
  8. kernel/power/hibernate.c +97 −0
Documentation/ABI/testing/sysfs-bus-nvdimm+2−0
@@ -0,0 +1,2 @@
1+The libnvdimm sub-system implements a common sysfs interface for
2+platform nvdimm resources. See Documentation/driver-api/nvdimm/.
Documentation/driver-api/nvdimm/firmware-activate.rst+86−0
@@ -0,0 +1,86 @@
1+.. SPDX-License-Identifier: GPL-2.0
2+
3+==================================
4+NVDIMM Runtime Firmware Activation
5+==================================
6+
7+Some persistent memory devices run a firmware locally on the device /
8+"DIMM" to perform tasks like media management, capacity provisioning,
9+and health monitoring. The process of updating that firmware typically
10+involves a reboot because it has implications for in-flight memory
11+transactions. However, reboots are disruptive and at least the Intel
12+persistent memory platform implementation, described by the Intel ACPI
13+DSM specification [1], has added support for activating firmware at
14+runtime.
15+
16+A native sysfs interface is implemented in libnvdimm to allow platform
17+to advertise and control their local runtime firmware activation
18+capability.
19+
20+The libnvdimm bus object, ndbusX, implements an ndbusX/firmware/activate
21+attribute that shows the state of the firmware activation as one of 'idle',
22+'armed', 'overflow', and 'busy'.
23+
24+- idle:
25+ No devices are set / armed to activate firmware
26+
27+- armed:
28+ At least one device is armed
29+
30+- busy:
31+ In the busy state armed devices are in the process of transitioning
32+ back to idle and completing an activation cycle.
33+
34+- overflow:
35+ If the platform has a concept of incremental work needed to perform
36+ the activation it could be the case that too many DIMMs are armed for
37+ activation. In that scenario the potential for firmware activation to
38+ timeout is indicated by the 'overflow' state.
39+
40+The 'ndbusX/firmware/activate' property can be written with a value of
41+either 'live', or 'quiesce'. A value of 'quiesce' triggers the kernel to
42+run firmware activation from within the equivalent of the hibernation
43+'freeze' state where drivers and applications are notified to stop their
44+modifications of system memory. A value of 'live' attempts
45+firmware activation without this hibernation cycle. The
46+'ndbusX/firmware/activate' property will be elided completely if no
47+firmware activation capability is detected.
48+
49+Another property 'ndbusX/firmware/capability' indicates a value of
50+'live' or 'quiesce', where 'live' indicates that the firmware
51+does not require or inflict any quiesce period on the system to update
52+firmware. A capability value of 'quiesce' indicates that firmware does
53+expect and injects a quiet period for the memory controller, but 'live'
54+may still be written to 'ndbusX/firmware/activate' as an override to
55+assume the risk of racing firmware update with in-flight device and
56+application activity. The 'ndbusX/firmware/capability' property will be
57+elided completely if no firmware activation capability is detected.
58+
59+The libnvdimm memory-device / DIMM object, nmemX, implements
60+'nmemX/firmware/activate' and 'nmemX/firmware/result' attributes to
61+communicate the per-device firmware activation state. Similar to the
62+'ndbusX/firmware/activate' attribute, the 'nmemX/firmware/activate'
63+attribute indicates 'idle', 'armed', or 'busy'. The state transitions
64+from 'armed' to 'idle' when the system is prepared to activate firmware,
65+firmware staged + state set to armed, and 'ndbusX/firmware/activate' is
66+triggered. After that activation event the nmemX/firmware/result
67+attribute reflects the state of the last activation as one of:
68+
69+- none:
70+ No runtime activation triggered since the last time the device was reset
71+
72+- success:
73+ The last runtime activation completed successfully.
74+
75+- fail:
76+ The last runtime activation failed for device-specific reasons.
77+
78+- not_staged:
79+ The last runtime activation failed due to a sequencing error of the
80+ firmware image not being staged.
81+
82+- need_reset:
83+ Runtime firmware activation failed, but the firmware can still be
84+ activated via the legacy method of power-cycling the system.
85+
86+[1]: https://docs.pmem.io/persistent-memory/
drivers/nvdimm/core.c+149−0
@@ -4,6 +4,7 @@ · 3 unmodified lines
44 */
55 #include <linux/libnvdimm.h>
66 #include <linux/badblocks.h>
7+#include <linux/suspend.h>
78 #include <linux/export.h>
89 #include <linux/module.h>
910 #include <linux/blkdev.h>
@@ -389,8 +390,156 @@ static const struct attribute_group nvdimm_bus_attribute_group = { · 379 unmodified lines
389390 .attrs = nvdimm_bus_attributes,
390391 };
391392
393+static ssize_t capability_show(struct device *dev,
394+ struct device_attribute *attr, char *buf)
395+{
396+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
397+ struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
398+ enum nvdimm_fwa_capability cap;
399+
400+ if (!nd_desc->fw_ops)
401+ return -EOPNOTSUPP;
402+
403+ nvdimm_bus_lock(dev);
404+ cap = nd_desc->fw_ops->capability(nd_desc);
405+ nvdimm_bus_unlock(dev);
406+
407+ switch (cap) {
408+ case NVDIMM_FWA_CAP_QUIESCE:
409+ return sprintf(buf, "quiesce\n");
410+ case NVDIMM_FWA_CAP_LIVE:
411+ return sprintf(buf, "live\n");
412+ default:
413+ return -EOPNOTSUPP;
414+ }
415+}
416+
417+static DEVICE_ATTR_RO(capability);
418+
419+static ssize_t activate_show(struct device *dev,
420+ struct device_attribute *attr, char *buf)
421+{
422+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
423+ struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
424+ enum nvdimm_fwa_capability cap;
425+ enum nvdimm_fwa_state state;
426+
427+ if (!nd_desc->fw_ops)
428+ return -EOPNOTSUPP;
429+
430+ nvdimm_bus_lock(dev);
431+ cap = nd_desc->fw_ops->capability(nd_desc);
432+ state = nd_desc->fw_ops->activate_state(nd_desc);
433+ nvdimm_bus_unlock(dev);
434+
435+ if (cap < NVDIMM_FWA_CAP_QUIESCE)
436+ return -EOPNOTSUPP;
437+
438+ switch (state) {
439+ case NVDIMM_FWA_IDLE:
440+ return sprintf(buf, "idle\n");
441+ case NVDIMM_FWA_BUSY:
442+ return sprintf(buf, "busy\n");
443+ case NVDIMM_FWA_ARMED:
444+ return sprintf(buf, "armed\n");
445+ case NVDIMM_FWA_ARM_OVERFLOW:
446+ return sprintf(buf, "overflow\n");
447+ default:
448+ return -ENXIO;
449+ }
450+}
451+
452+static int exec_firmware_activate(void *data)
453+{
454+ struct nvdimm_bus_descriptor *nd_desc = data;
455+
456+ return nd_desc->fw_ops->activate(nd_desc);
457+}
458+
459+static ssize_t activate_store(struct device *dev,
460+ struct device_attribute *attr, const char *buf, size_t len)
461+{
462+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
463+ struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
464+ enum nvdimm_fwa_state state;
465+ bool quiesce;
466+ ssize_t rc;
467+
468+ if (!nd_desc->fw_ops)
469+ return -EOPNOTSUPP;
470+
471+ if (sysfs_streq(buf, "live"))
472+ quiesce = false;
473+ else if (sysfs_streq(buf, "quiesce"))
474+ quiesce = true;
475+ else
476+ return -EINVAL;
477+
478+ nvdimm_bus_lock(dev);
479+ state = nd_desc->fw_ops->activate_state(nd_desc);
480+
481+ switch (state) {
482+ case NVDIMM_FWA_BUSY:
483+ rc = -EBUSY;
484+ break;
485+ case NVDIMM_FWA_ARMED:
486+ case NVDIMM_FWA_ARM_OVERFLOW:
487+ if (quiesce)
488+ rc = hibernate_quiet_exec(exec_firmware_activate, nd_desc);
489+ else
490+ rc = nd_desc->fw_ops->activate(nd_desc);
491+ break;
492+ case NVDIMM_FWA_IDLE:
493+ default:
494+ rc = -ENXIO;
495+ }
496+ nvdimm_bus_unlock(dev);
497+
498+ if (rc == 0)
499+ rc = len;
500+ return rc;
501+}
502+
503+static DEVICE_ATTR_ADMIN_RW(activate);
504+
505+static umode_t nvdimm_bus_firmware_visible(struct kobject *kobj, struct attribute *a, int n)
506+{
507+ struct device *dev = container_of(kobj, typeof(*dev), kobj);
508+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
509+ struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
510+ enum nvdimm_fwa_capability cap;
511+
512+ /*
513+ * Both 'activate' and 'capability' disappear when no ops
514+ * detected, or a negative capability is indicated.
515+ */
516+ if (!nd_desc->fw_ops)
517+ return 0;
518+
519+ nvdimm_bus_lock(dev);
520+ cap = nd_desc->fw_ops->capability(nd_desc);
521+ nvdimm_bus_unlock(dev);
522+
523+ if (cap < NVDIMM_FWA_CAP_QUIESCE)
524+ return 0;
525+
526+ return a->mode;
527+}
528+static struct attribute *nvdimm_bus_firmware_attributes[] = {
529+ &dev_attr_activate.attr,
530+ &dev_attr_capability.attr,
531+ NULL,
532+};
533+
534+static const struct attribute_group nvdimm_bus_firmware_attribute_group = {
535+ .name = "firmware",
536+ .attrs = nvdimm_bus_firmware_attributes,
537+ .is_visible = nvdimm_bus_firmware_visible,
538+};
539+
392540 const struct attribute_group *nvdimm_bus_attribute_groups[] = {
393541 &nvdimm_bus_attribute_group,
542+ &nvdimm_bus_firmware_attribute_group,
394543 NULL,
395544 };
396545
drivers/nvdimm/dimm_devs.c+115−0
@@ -446,9 +446,124 @@ static const struct attribute_group nvdimm_attribute_group = { · 445 unmodified lines
446446 .is_visible = nvdimm_visible,
447447 };
448448
449+static ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf)
450+{
451+ struct nvdimm *nvdimm = to_nvdimm(dev);
452+ enum nvdimm_fwa_result result;
453+
454+ if (!nvdimm->fw_ops)
455+ return -EOPNOTSUPP;
456+
457+ nvdimm_bus_lock(dev);
458+ result = nvdimm->fw_ops->activate_result(nvdimm);
459+ nvdimm_bus_unlock(dev);
460+
461+ switch (result) {
462+ case NVDIMM_FWA_RESULT_NONE:
463+ return sprintf(buf, "none\n");
464+ case NVDIMM_FWA_RESULT_SUCCESS:
465+ return sprintf(buf, "success\n");
466+ case NVDIMM_FWA_RESULT_FAIL:
467+ return sprintf(buf, "fail\n");
468+ case NVDIMM_FWA_RESULT_NOTSTAGED:
469+ return sprintf(buf, "not_staged\n");
470+ case NVDIMM_FWA_RESULT_NEEDRESET:
471+ return sprintf(buf, "need_reset\n");
472+ default:
473+ return -ENXIO;
474+ }
475+}
476+static DEVICE_ATTR_ADMIN_RO(result);
477+
478+static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf)
479+{
480+ struct nvdimm *nvdimm = to_nvdimm(dev);
481+ enum nvdimm_fwa_state state;
482+
483+ if (!nvdimm->fw_ops)
484+ return -EOPNOTSUPP;
485+
486+ nvdimm_bus_lock(dev);
487+ state = nvdimm->fw_ops->activate_state(nvdimm);
488+ nvdimm_bus_unlock(dev);
489+
490+ switch (state) {
491+ case NVDIMM_FWA_IDLE:
492+ return sprintf(buf, "idle\n");
493+ case NVDIMM_FWA_BUSY:
494+ return sprintf(buf, "busy\n");
495+ case NVDIMM_FWA_ARMED:
496+ return sprintf(buf, "armed\n");
497+ default:
498+ return -ENXIO;
499+ }
500+}
501+
502+static ssize_t activate_store(struct device *dev, struct device_attribute *attr,
503+ const char *buf, size_t len)
504+{
505+ struct nvdimm *nvdimm = to_nvdimm(dev);
506+ enum nvdimm_fwa_trigger arg;
507+ int rc;
508+
509+ if (!nvdimm->fw_ops)
510+ return -EOPNOTSUPP;
511+
512+ if (sysfs_streq(buf, "arm"))
513+ arg = NVDIMM_FWA_ARM;
514+ else if (sysfs_streq(buf, "disarm"))
515+ arg = NVDIMM_FWA_DISARM;
516+ else
517+ return -EINVAL;
518+
519+ nvdimm_bus_lock(dev);
520+ rc = nvdimm->fw_ops->arm(nvdimm, arg);
521+ nvdimm_bus_unlock(dev);
522+
523+ if (rc < 0)
524+ return rc;
525+ return len;
526+}
527+static DEVICE_ATTR_ADMIN_RW(activate);
528+
529+static struct attribute *nvdimm_firmware_attributes[] = {
530+ &dev_attr_activate.attr,
531+ &dev_attr_result.attr,
532+};
533+
534+static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n)
535+{
536+ struct device *dev = container_of(kobj, typeof(*dev), kobj);
537+ struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
538+ struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
539+ struct nvdimm *nvdimm = to_nvdimm(dev);
540+ enum nvdimm_fwa_capability cap;
541+
542+ if (!nd_desc->fw_ops)
543+ return 0;
544+ if (!nvdimm->fw_ops)
545+ return 0;
546+
547+ nvdimm_bus_lock(dev);
548+ cap = nd_desc->fw_ops->capability(nd_desc);
549+ nvdimm_bus_unlock(dev);
550+
551+ if (cap < NVDIMM_FWA_CAP_QUIESCE)
552+ return 0;
553+
554+ return a->mode;
555+}
556+
557+static const struct attribute_group nvdimm_firmware_attribute_group = {
558+ .name = "firmware",
559+ .attrs = nvdimm_firmware_attributes,
560+ .is_visible = nvdimm_firmware_visible,
561+};
562+
449563 static const struct attribute_group *nvdimm_attribute_groups[] = {
450564 &nd_device_attribute_group,
451565 &nvdimm_attribute_group,
566+ &nvdimm_firmware_attribute_group,
452567 NULL,
453568 };
454569
drivers/nvdimm/nd-core.h+1−0
@@ -45,6 +45,7 @@ struct nvdimm { · 44 unmodified lines
4545 struct kernfs_node *overwrite_state;
4646 } sec;
4747 struct delayed_work dwork;
48+ const struct nvdimm_fw_ops *fw_ops;
4849 };
4950
5051 static inline unsigned long nvdimm_security_flags(
include/linux/libnvdimm.h+44−0
@@ -86,6 +86,7 @@ struct nvdimm_bus_descriptor { · 85 unmodified lines
8686 int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc);
8787 int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc,
8888 struct nvdimm *nvdimm, unsigned int cmd, void *data);
89+ const struct nvdimm_bus_fw_ops *fw_ops;
8990 };
9091
9192 struct nd_cmd_desc {
@@ -200,6 +201,49 @@ struct nvdimm_security_ops { · 108 unmodified lines
200201 int (*query_overwrite)(struct nvdimm *nvdimm);
201202 };
202203
204+enum nvdimm_fwa_state {
205+ NVDIMM_FWA_INVALID,
206+ NVDIMM_FWA_IDLE,
207+ NVDIMM_FWA_ARMED,
208+ NVDIMM_FWA_BUSY,
209+ NVDIMM_FWA_ARM_OVERFLOW,
210+};
211+
212+enum nvdimm_fwa_trigger {
213+ NVDIMM_FWA_ARM,
214+ NVDIMM_FWA_DISARM,
215+};
216+
217+enum nvdimm_fwa_capability {
218+ NVDIMM_FWA_CAP_INVALID,
219+ NVDIMM_FWA_CAP_NONE,
220+ NVDIMM_FWA_CAP_QUIESCE,
221+ NVDIMM_FWA_CAP_LIVE,
222+};
223+
224+enum nvdimm_fwa_result {
225+ NVDIMM_FWA_RESULT_INVALID,
226+ NVDIMM_FWA_RESULT_NONE,
227+ NVDIMM_FWA_RESULT_SUCCESS,
228+ NVDIMM_FWA_RESULT_NOTSTAGED,
229+ NVDIMM_FWA_RESULT_NEEDRESET,
230+ NVDIMM_FWA_RESULT_FAIL,
231+};
232+
233+struct nvdimm_bus_fw_ops {
234+ enum nvdimm_fwa_state (*activate_state)
235+ (struct nvdimm_bus_descriptor *nd_desc);
236+ enum nvdimm_fwa_capability (*capability)
237+ (struct nvdimm_bus_descriptor *nd_desc);
238+ int (*activate)(struct nvdimm_bus_descriptor *nd_desc);
239+};
240+
241+struct nvdimm_fw_ops {
242+ enum nvdimm_fwa_state (*activate_state)(struct nvdimm *nvdimm);
243+ enum nvdimm_fwa_result (*activate_result)(struct nvdimm *nvdimm);
244+ int (*arm)(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arg);
245+};
246+
203247 void badrange_init(struct badrange *badrange);
204248 int badrange_add(struct badrange *badrange, u64 addr, u64 length);
205249 void badrange_forget(struct badrange *badrange, phys_addr_t start,
include/linux/suspend.h+6−0
@@ -453,6 +453,8 @@ extern bool hibernation_available(void); · 452 unmodified lines
453453 asmlinkage int swsusp_save(void);
454454 extern struct pbe *restore_pblist;
455455 int pfn_is_nosave(unsigned long pfn);
456+
457+int hibernate_quiet_exec(int (*func)(void *data), void *data);
456458 #else /* CONFIG_HIBERNATION */
457459 static inline void register_nosave_region(unsigned long b, unsigned long e) {}
458460 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 · 5 unmodified lines
464466 static inline int hibernate(void) { return -ENOSYS; }
465467 static inline bool system_entering_hibernation(void) { return false; }
466468 static inline bool hibernation_available(void) { return false; }
469+
470+static inline int hibernate_quiet_exec(int (*func)(void *data), void *data) {
471+ return -ENOTSUPP;
472+}
467473 #endif /* CONFIG_HIBERNATION */
468474
469475 #ifdef CONFIG_HIBERNATION_SNAPSHOT_DEV
kernel/power/hibernate.c+97−0
@@ -795,6 +795,103 @@ int hibernate(void) · 794 unmodified lines
795795 return error;
796796 }
797797
798+/**
799+ * hibernate_quiet_exec - Execute a function with all devices frozen.
800+ * @func: Function to execute.
801+ * @data: Data pointer to pass to @func.
802+ *
803+ * Return the @func return value or an error code if it cannot be executed.
804+ */
805+int hibernate_quiet_exec(int (*func)(void *data), void *data)
806+{
807+ int error, nr_calls = 0;
808+
809+ lock_system_sleep();
810+
811+ if (!hibernate_acquire()) {
812+ error = -EBUSY;
813+ goto unlock;
814+ }
815+
816+ pm_prepare_console();
817+
818+ error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls);
819+ if (error) {
820+ nr_calls--;
821+ goto exit;
822+ }
823+
824+ error = freeze_processes();
825+ if (error)
826+ goto exit;
827+
828+ lock_device_hotplug();
829+
830+ pm_suspend_clear_flags();
831+
832+ error = platform_begin(true);
833+ if (error)
834+ goto thaw;
835+
836+ error = freeze_kernel_threads();
837+ if (error)
838+ goto thaw;
839+
840+ error = dpm_prepare(PMSG_FREEZE);
841+ if (error)
842+ goto dpm_complete;
843+
844+ suspend_console();
845+
846+ error = dpm_suspend(PMSG_FREEZE);
847+ if (error)
848+ goto dpm_resume;
849+
850+ error = dpm_suspend_end(PMSG_FREEZE);
851+ if (error)
852+ goto dpm_resume;
853+
854+ error = platform_pre_snapshot(true);
855+ if (error)
856+ goto skip;
857+
858+ error = func(data);
859+
860+skip:
861+ platform_finish(true);
862+
863+ dpm_resume_start(PMSG_THAW);
864+
865+dpm_resume:
866+ dpm_resume(PMSG_THAW);
867+
868+ resume_console();
869+
870+dpm_complete:
871+ dpm_complete(PMSG_THAW);
872+
873+ thaw_kernel_threads();
874+
875+thaw:
876+ platform_end(true);
877+
878+ unlock_device_hotplug();
879+
880+ thaw_processes();
881+
882+exit:
883+ __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
884+
885+ pm_restore_console();
886+
887+ hibernate_release();
888+
889+unlock:
890+ unlock_system_sleep();
891+
892+ return error;
893+}
894+EXPORT_SYMBOL_GPL(hibernate_quiet_exec);
798895
799896 /**
800897 * software_resume - Resume from a saved hibernation image.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t