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