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/]
Showing the changes to include/linux (2 of 8 files). Show all
Unified · Split
| @@ -86,6 +86,7 @@ struct nvdimm_bus_descriptor { · 85 unmodified lines | ||
| 86 | 86 | int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc); |
| 87 | 87 | int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc, |
| 88 | 88 | struct nvdimm *nvdimm, unsigned int cmd, void *data); |
| 89 | + const struct nvdimm_bus_fw_ops *fw_ops; | |
| 89 | 90 | }; |
| 90 | 91 | |
| 91 | 92 | struct nd_cmd_desc { |
| @@ -200,6 +201,49 @@ struct nvdimm_security_ops { · 108 unmodified lines | ||
| 200 | 201 | int (*query_overwrite)(struct nvdimm *nvdimm); |
| 201 | 202 | }; |
| 202 | 203 | |
| 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 | + | |
| 203 | 247 | void badrange_init(struct badrange *badrange); |
| 204 | 248 | int badrange_add(struct badrange *badrange, u64 addr, u64 length); |
| 205 | 249 | void badrange_forget(struct badrange *badrange, phys_addr_t start, |
| @@ -453,6 +453,8 @@ extern bool hibernation_available(void); · 452 unmodified lines | ||
| 453 | 453 | asmlinkage int swsusp_save(void); |
| 454 | 454 | extern struct pbe *restore_pblist; |
| 455 | 455 | int pfn_is_nosave(unsigned long pfn); |
| 456 | + | |
| 457 | +int hibernate_quiet_exec(int (*func)(void *data), void *data); | |
| 456 | 458 | #else /* CONFIG_HIBERNATION */ |
| 457 | 459 | static inline void register_nosave_region(unsigned long b, unsigned long e) {} |
| 458 | 460 | 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 | ||
| 464 | 466 | static inline int hibernate(void) { return -ENOSYS; } |
| 465 | 467 | static inline bool system_entering_hibernation(void) { return false; } |
| 466 | 468 | 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 | +} | |
| 467 | 473 | #endif /* CONFIG_HIBERNATION */ |
| 468 | 474 | |
| 469 | 475 | #ifdef CONFIG_HIBERNATION_SNAPSHOT_DEV |