Merge branch 'for-5.8/papr_scm' into libnvdimm-for-next

Include the papr_scm health retrieval feature for v5.8-rc2. The
functionality was initially posted well in advance of the merge window,
but review comments and a late build-bot warning kept them out of the
v5.8-rc1 libnvdimm pull request.

Vaibhav notes:
These patches are tied to specific features that were committed to
customers in upcoming distros releases (RHEL and SLES) whose time-lines
are tied to 5.8 kernel release.

Being able to track the health of an nvdimm is critical for our
customers that are running workloads leveraging papr-scm nvdimms.
Missing the 5.8 kernel would mean missing the distro timelines and
shifting forward the availability of this feature in distro kernels by
at least 6 months.

+615 -10
+27
Documentation/ABI/testing/sysfs-bus-papr-pmem
···
··· 1 + What: /sys/bus/nd/devices/nmemX/papr/flags 2 + Date: Apr, 2020 3 + KernelVersion: v5.8 4 + Contact: linuxppc-dev <linuxppc-dev@lists.ozlabs.org>, linux-nvdimm@lists.01.org, 5 + Description: 6 + (RO) Report flags indicating various states of a 7 + papr-pmem NVDIMM device. Each flag maps to a one or 8 + more bits set in the dimm-health-bitmap retrieved in 9 + response to H_SCM_HEALTH hcall. The details of the bit 10 + flags returned in response to this hcall is available 11 + at 'Documentation/powerpc/papr_hcalls.rst' . Below are 12 + the flags reported in this sysfs file: 13 + 14 + * "not_armed" : Indicates that NVDIMM contents will not 15 + survive a power cycle. 16 + * "flush_fail" : Indicates that NVDIMM contents 17 + couldn't be flushed during last 18 + shut-down event. 19 + * "restore_fail": Indicates that NVDIMM contents 20 + couldn't be restored during NVDIMM 21 + initialization. 22 + * "encrypted" : NVDIMM contents are encrypted. 23 + * "smart_notify": There is health event for the NVDIMM. 24 + * "scrubbed" : Indicating that contents of the 25 + NVDIMM have been scrubbed. 26 + * "locked" : Indicating that NVDIMM contents cant 27 + be modified until next power cycle.
+42 -4
Documentation/powerpc/papr_hcalls.rst
··· 220 **H_SCM_HEALTH** 221 222 | Input: drcIndex 223 - | Out: *health-bitmap, health-bit-valid-bitmap* 224 | Return Value: *H_Success, H_Parameter, H_Hardware* 225 226 Given a DRC Index return the info on predictive failure and overall health of 227 - the NVDIMM. The asserted bits in the health-bitmap indicate a single predictive 228 - failure and health-bit-valid-bitmap indicate which bits in health-bitmap are 229 - valid. 230 231 **H_SCM_PERFORMANCE_STATS** 232
··· 220 **H_SCM_HEALTH** 221 222 | Input: drcIndex 223 + | Out: *health-bitmap (r4), health-bit-valid-bitmap (r5)* 224 | Return Value: *H_Success, H_Parameter, H_Hardware* 225 226 Given a DRC Index return the info on predictive failure and overall health of 227 + the PMEM device. The asserted bits in the health-bitmap indicate one or more states 228 + (described in table below) of the PMEM device and health-bit-valid-bitmap indicate 229 + which bits in health-bitmap are valid. The bits are reported in 230 + reverse bit ordering for example a value of 0xC400000000000000 231 + indicates bits 0, 1, and 5 are valid. 232 + 233 + Health Bitmap Flags: 234 + 235 + +------+-----------------------------------------------------------------------+ 236 + | Bit | Definition | 237 + +======+=======================================================================+ 238 + | 00 | PMEM device is unable to persist memory contents. | 239 + | | If the system is powered down, nothing will be saved. | 240 + +------+-----------------------------------------------------------------------+ 241 + | 01 | PMEM device failed to persist memory contents. Either contents were | 242 + | | not saved successfully on power down or were not restored properly on | 243 + | | power up. | 244 + +------+-----------------------------------------------------------------------+ 245 + | 02 | PMEM device contents are persisted from previous IPL. The data from | 246 + | | the last boot were successfully restored. | 247 + +------+-----------------------------------------------------------------------+ 248 + | 03 | PMEM device contents are not persisted from previous IPL. There was no| 249 + | | data to restore from the last boot. | 250 + +------+-----------------------------------------------------------------------+ 251 + | 04 | PMEM device memory life remaining is critically low | 252 + +------+-----------------------------------------------------------------------+ 253 + | 05 | PMEM device will be garded off next IPL due to failure | 254 + +------+-----------------------------------------------------------------------+ 255 + | 06 | PMEM device contents cannot persist due to current platform health | 256 + | | status. A hardware failure may prevent data from being saved or | 257 + | | restored. | 258 + +------+-----------------------------------------------------------------------+ 259 + | 07 | PMEM device is unable to persist memory contents in certain conditions| 260 + +------+-----------------------------------------------------------------------+ 261 + | 08 | PMEM device is encrypted | 262 + +------+-----------------------------------------------------------------------+ 263 + | 09 | PMEM device has successfully completed a requested erase or secure | 264 + | | erase procedure. | 265 + +------+-----------------------------------------------------------------------+ 266 + |10:63 | Reserved / Unused | 267 + +------+-----------------------------------------------------------------------+ 268 269 **H_SCM_PERFORMANCE_STATS** 270
+132
arch/powerpc/include/uapi/asm/papr_pdsm.h
···
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + /* 3 + * PAPR nvDimm Specific Methods (PDSM) and structs for libndctl 4 + * 5 + * (C) Copyright IBM 2020 6 + * 7 + * Author: Vaibhav Jain <vaibhav at linux.ibm.com> 8 + */ 9 + 10 + #ifndef _UAPI_ASM_POWERPC_PAPR_PDSM_H_ 11 + #define _UAPI_ASM_POWERPC_PAPR_PDSM_H_ 12 + 13 + #include <linux/types.h> 14 + #include <linux/ndctl.h> 15 + 16 + /* 17 + * PDSM Envelope: 18 + * 19 + * The ioctl ND_CMD_CALL exchange data between user-space and kernel via 20 + * envelope which consists of 2 headers sections and payload sections as 21 + * illustrated below: 22 + * +-----------------+---------------+---------------------------+ 23 + * | 64-Bytes | 8-Bytes | Max 184-Bytes | 24 + * +-----------------+---------------+---------------------------+ 25 + * | ND-HEADER | PDSM-HEADER | PDSM-PAYLOAD | 26 + * +-----------------+---------------+---------------------------+ 27 + * | nd_family | | | 28 + * | nd_size_out | cmd_status | | 29 + * | nd_size_in | reserved | nd_pdsm_payload | 30 + * | nd_command | payload --> | | 31 + * | nd_fw_size | | | 32 + * | nd_payload ---> | | | 33 + * +---------------+-----------------+---------------------------+ 34 + * 35 + * ND Header: 36 + * This is the generic libnvdimm header described as 'struct nd_cmd_pkg' 37 + * which is interpreted by libnvdimm before passed on to papr_scm. Important 38 + * member fields used are: 39 + * 'nd_family' : (In) NVDIMM_FAMILY_PAPR_SCM 40 + * 'nd_size_in' : (In) PDSM-HEADER + PDSM-IN-PAYLOAD (usually 0) 41 + * 'nd_size_out' : (In) PDSM-HEADER + PDSM-RETURN-PAYLOAD 42 + * 'nd_command' : (In) One of PAPR_PDSM_XXX 43 + * 'nd_fw_size' : (Out) PDSM-HEADER + size of actual payload returned 44 + * 45 + * PDSM Header: 46 + * This is papr-scm specific header that precedes the payload. This is defined 47 + * as nd_cmd_pdsm_pkg. Following fields aare available in this header: 48 + * 49 + * 'cmd_status' : (Out) Errors if any encountered while servicing PDSM. 50 + * 'reserved' : Not used, reserved for future and should be set to 0. 51 + * 'payload' : A union of all the possible payload structs 52 + * 53 + * PDSM Payload: 54 + * 55 + * The layout of the PDSM Payload is defined by various structs shared between 56 + * papr_scm and libndctl so that contents of payload can be interpreted. As such 57 + * its defined as a union of all possible payload structs as 58 + * 'union nd_pdsm_payload'. Based on the value of 'nd_cmd_pkg.nd_command' 59 + * appropriate member of the union is accessed. 60 + */ 61 + 62 + /* Max payload size that we can handle */ 63 + #define ND_PDSM_PAYLOAD_MAX_SIZE 184 64 + 65 + /* Max payload size that we can handle */ 66 + #define ND_PDSM_HDR_SIZE \ 67 + (sizeof(struct nd_pkg_pdsm) - ND_PDSM_PAYLOAD_MAX_SIZE) 68 + 69 + /* Various nvdimm health indicators */ 70 + #define PAPR_PDSM_DIMM_HEALTHY 0 71 + #define PAPR_PDSM_DIMM_UNHEALTHY 1 72 + #define PAPR_PDSM_DIMM_CRITICAL 2 73 + #define PAPR_PDSM_DIMM_FATAL 3 74 + 75 + /* 76 + * Struct exchanged between kernel & ndctl in for PAPR_PDSM_HEALTH 77 + * Various flags indicate the health status of the dimm. 78 + * 79 + * extension_flags : Any extension fields present in the struct. 80 + * dimm_unarmed : Dimm not armed. So contents wont persist. 81 + * dimm_bad_shutdown : Previous shutdown did not persist contents. 82 + * dimm_bad_restore : Contents from previous shutdown werent restored. 83 + * dimm_scrubbed : Contents of the dimm have been scrubbed. 84 + * dimm_locked : Contents of the dimm cant be modified until CEC reboot 85 + * dimm_encrypted : Contents of dimm are encrypted. 86 + * dimm_health : Dimm health indicator. One of PAPR_PDSM_DIMM_XXXX 87 + */ 88 + struct nd_papr_pdsm_health { 89 + union { 90 + struct { 91 + __u32 extension_flags; 92 + __u8 dimm_unarmed; 93 + __u8 dimm_bad_shutdown; 94 + __u8 dimm_bad_restore; 95 + __u8 dimm_scrubbed; 96 + __u8 dimm_locked; 97 + __u8 dimm_encrypted; 98 + __u16 dimm_health; 99 + }; 100 + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; 101 + }; 102 + }; 103 + 104 + /* 105 + * Methods to be embedded in ND_CMD_CALL request. These are sent to the kernel 106 + * via 'nd_cmd_pkg.nd_command' member of the ioctl struct 107 + */ 108 + enum papr_pdsm { 109 + PAPR_PDSM_MIN = 0x0, 110 + PAPR_PDSM_HEALTH, 111 + PAPR_PDSM_MAX, 112 + }; 113 + 114 + /* Maximal union that can hold all possible payload types */ 115 + union nd_pdsm_payload { 116 + struct nd_papr_pdsm_health health; 117 + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; 118 + } __packed; 119 + 120 + /* 121 + * PDSM-header + payload expected with ND_CMD_CALL ioctl from libnvdimm 122 + * Valid member of union 'payload' is identified via 'nd_cmd_pkg.nd_command' 123 + * that should always precede this struct when sent to papr_scm via CMD_CALL 124 + * interface. 125 + */ 126 + struct nd_pkg_pdsm { 127 + __s32 cmd_status; /* Out: Sub-cmd status returned back */ 128 + __u16 reserved[2]; /* Ignored and to be set as '0' */ 129 + union nd_pdsm_payload payload; 130 + } __packed; 131 + 132 + #endif /* _UAPI_ASM_POWERPC_PAPR_PDSM_H_ */
+412 -6
arch/powerpc/platforms/pseries/papr_scm.c
··· 12 #include <linux/libnvdimm.h> 13 #include <linux/platform_device.h> 14 #include <linux/delay.h> 15 16 #include <asm/plpar_wrappers.h> 17 18 #define BIND_ANY_ADDR (~0ul) 19 20 #define PAPR_SCM_DIMM_CMD_MASK \ 21 ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ 22 (1ul << ND_CMD_GET_CONFIG_DATA) | \ 23 - (1ul << ND_CMD_SET_CONFIG_DATA)) 24 25 struct papr_scm_priv { 26 struct platform_device *pdev; 27 struct device_node *dn; ··· 80 struct resource res; 81 struct nd_region *region; 82 struct nd_interleave_set nd_set; 83 }; 84 85 static int drc_pmem_bind(struct papr_scm_priv *p) ··· 194 return drc_pmem_bind(p); 195 } 196 197 198 static int papr_scm_meta_get(struct papr_scm_priv *p, 199 struct nd_cmd_get_config_data_hdr *hdr) ··· 351 return 0; 352 } 353 354 static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, 355 struct nvdimm *nvdimm, unsigned int cmd, void *buf, 356 unsigned int buf_len, int *cmd_rc) 357 { 358 struct nd_cmd_get_config_size *get_size_hdr; 359 struct papr_scm_priv *p; 360 361 - /* Only dimm-specific calls are supported atm */ 362 - if (!nvdimm) 363 - return -EINVAL; 364 365 p = nvdimm_provider_data(nvdimm); 366 ··· 616 *cmd_rc = papr_scm_meta_set(p, buf); 617 break; 618 619 default: 620 return -EINVAL; 621 } 622 ··· 630 631 return 0; 632 } 633 634 static int papr_scm_nvdimm_init(struct papr_scm_priv *p) 635 { ··· 715 dimm_flags = 0; 716 set_bit(NDD_LABELING, &dimm_flags); 717 718 - p->nvdimm = nvdimm_create(p->bus, p, NULL, dimm_flags, 719 - PAPR_SCM_DIMM_CMD_MASK, 0, NULL); 720 if (!p->nvdimm) { 721 dev_err(dev, "Error creating DIMM object for %pOF\n", p->dn); 722 goto err; ··· 801 p = kzalloc(sizeof(*p), GFP_KERNEL); 802 if (!p) 803 return -ENOMEM; 804 805 /* optional DT properties */ 806 of_property_read_u32(dn, "ibm,metadata-size", &metadata_size);
··· 12 #include <linux/libnvdimm.h> 13 #include <linux/platform_device.h> 14 #include <linux/delay.h> 15 + #include <linux/seq_buf.h> 16 17 #include <asm/plpar_wrappers.h> 18 + #include <asm/papr_pdsm.h> 19 20 #define BIND_ANY_ADDR (~0ul) 21 22 #define PAPR_SCM_DIMM_CMD_MASK \ 23 ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ 24 (1ul << ND_CMD_GET_CONFIG_DATA) | \ 25 + (1ul << ND_CMD_SET_CONFIG_DATA) | \ 26 + (1ul << ND_CMD_CALL)) 27 28 + /* DIMM health bitmap bitmap indicators */ 29 + /* SCM device is unable to persist memory contents */ 30 + #define PAPR_PMEM_UNARMED (1ULL << (63 - 0)) 31 + /* SCM device failed to persist memory contents */ 32 + #define PAPR_PMEM_SHUTDOWN_DIRTY (1ULL << (63 - 1)) 33 + /* SCM device contents are persisted from previous IPL */ 34 + #define PAPR_PMEM_SHUTDOWN_CLEAN (1ULL << (63 - 2)) 35 + /* SCM device contents are not persisted from previous IPL */ 36 + #define PAPR_PMEM_EMPTY (1ULL << (63 - 3)) 37 + /* SCM device memory life remaining is critically low */ 38 + #define PAPR_PMEM_HEALTH_CRITICAL (1ULL << (63 - 4)) 39 + /* SCM device will be garded off next IPL due to failure */ 40 + #define PAPR_PMEM_HEALTH_FATAL (1ULL << (63 - 5)) 41 + /* SCM contents cannot persist due to current platform health status */ 42 + #define PAPR_PMEM_HEALTH_UNHEALTHY (1ULL << (63 - 6)) 43 + /* SCM device is unable to persist memory contents in certain conditions */ 44 + #define PAPR_PMEM_HEALTH_NON_CRITICAL (1ULL << (63 - 7)) 45 + /* SCM device is encrypted */ 46 + #define PAPR_PMEM_ENCRYPTED (1ULL << (63 - 8)) 47 + /* SCM device has been scrubbed and locked */ 48 + #define PAPR_PMEM_SCRUBBED_AND_LOCKED (1ULL << (63 - 9)) 49 + 50 + /* Bits status indicators for health bitmap indicating unarmed dimm */ 51 + #define PAPR_PMEM_UNARMED_MASK (PAPR_PMEM_UNARMED | \ 52 + PAPR_PMEM_HEALTH_UNHEALTHY) 53 + 54 + /* Bits status indicators for health bitmap indicating unflushed dimm */ 55 + #define PAPR_PMEM_BAD_SHUTDOWN_MASK (PAPR_PMEM_SHUTDOWN_DIRTY) 56 + 57 + /* Bits status indicators for health bitmap indicating unrestored dimm */ 58 + #define PAPR_PMEM_BAD_RESTORE_MASK (PAPR_PMEM_EMPTY) 59 + 60 + /* Bit status indicators for smart event notification */ 61 + #define PAPR_PMEM_SMART_EVENT_MASK (PAPR_PMEM_HEALTH_CRITICAL | \ 62 + PAPR_PMEM_HEALTH_FATAL | \ 63 + PAPR_PMEM_HEALTH_UNHEALTHY) 64 + 65 + /* private struct associated with each region */ 66 struct papr_scm_priv { 67 struct platform_device *pdev; 68 struct device_node *dn; ··· 39 struct resource res; 40 struct nd_region *region; 41 struct nd_interleave_set nd_set; 42 + 43 + /* Protect dimm health data from concurrent read/writes */ 44 + struct mutex health_mutex; 45 + 46 + /* Last time the health information of the dimm was updated */ 47 + unsigned long lasthealth_jiffies; 48 + 49 + /* Health information for the dimm */ 50 + u64 health_bitmap; 51 }; 52 53 static int drc_pmem_bind(struct papr_scm_priv *p) ··· 144 return drc_pmem_bind(p); 145 } 146 147 + /* 148 + * Issue hcall to retrieve dimm health info and populate papr_scm_priv with the 149 + * health information. 150 + */ 151 + static int __drc_pmem_query_health(struct papr_scm_priv *p) 152 + { 153 + unsigned long ret[PLPAR_HCALL_BUFSIZE]; 154 + long rc; 155 + 156 + /* issue the hcall */ 157 + rc = plpar_hcall(H_SCM_HEALTH, ret, p->drc_index); 158 + if (rc != H_SUCCESS) { 159 + dev_err(&p->pdev->dev, 160 + "Failed to query health information, Err:%ld\n", rc); 161 + return -ENXIO; 162 + } 163 + 164 + p->lasthealth_jiffies = jiffies; 165 + p->health_bitmap = ret[0] & ret[1]; 166 + 167 + dev_dbg(&p->pdev->dev, 168 + "Queried dimm health info. Bitmap:0x%016lx Mask:0x%016lx\n", 169 + ret[0], ret[1]); 170 + 171 + return 0; 172 + } 173 + 174 + /* Min interval in seconds for assuming stable dimm health */ 175 + #define MIN_HEALTH_QUERY_INTERVAL 60 176 + 177 + /* Query cached health info and if needed call drc_pmem_query_health */ 178 + static int drc_pmem_query_health(struct papr_scm_priv *p) 179 + { 180 + unsigned long cache_timeout; 181 + int rc; 182 + 183 + /* Protect concurrent modifications to papr_scm_priv */ 184 + rc = mutex_lock_interruptible(&p->health_mutex); 185 + if (rc) 186 + return rc; 187 + 188 + /* Jiffies offset for which the health data is assumed to be same */ 189 + cache_timeout = p->lasthealth_jiffies + 190 + msecs_to_jiffies(MIN_HEALTH_QUERY_INTERVAL * 1000); 191 + 192 + /* Fetch new health info is its older than MIN_HEALTH_QUERY_INTERVAL */ 193 + if (time_after(jiffies, cache_timeout)) 194 + rc = __drc_pmem_query_health(p); 195 + else 196 + /* Assume cached health data is valid */ 197 + rc = 0; 198 + 199 + mutex_unlock(&p->health_mutex); 200 + return rc; 201 + } 202 203 static int papr_scm_meta_get(struct papr_scm_priv *p, 204 struct nd_cmd_get_config_data_hdr *hdr) ··· 246 return 0; 247 } 248 249 + /* 250 + * Do a sanity checks on the inputs args to dimm-control function and return 251 + * '0' if valid. Validation of PDSM payloads happens later in 252 + * papr_scm_service_pdsm. 253 + */ 254 + static int is_cmd_valid(struct nvdimm *nvdimm, unsigned int cmd, void *buf, 255 + unsigned int buf_len) 256 + { 257 + unsigned long cmd_mask = PAPR_SCM_DIMM_CMD_MASK; 258 + struct nd_cmd_pkg *nd_cmd; 259 + struct papr_scm_priv *p; 260 + enum papr_pdsm pdsm; 261 + 262 + /* Only dimm-specific calls are supported atm */ 263 + if (!nvdimm) 264 + return -EINVAL; 265 + 266 + /* get the provider data from struct nvdimm */ 267 + p = nvdimm_provider_data(nvdimm); 268 + 269 + if (!test_bit(cmd, &cmd_mask)) { 270 + dev_dbg(&p->pdev->dev, "Unsupported cmd=%u\n", cmd); 271 + return -EINVAL; 272 + } 273 + 274 + /* For CMD_CALL verify pdsm request */ 275 + if (cmd == ND_CMD_CALL) { 276 + /* Verify the envelope and envelop size */ 277 + if (!buf || 278 + buf_len < (sizeof(struct nd_cmd_pkg) + ND_PDSM_HDR_SIZE)) { 279 + dev_dbg(&p->pdev->dev, "Invalid pkg size=%u\n", 280 + buf_len); 281 + return -EINVAL; 282 + } 283 + 284 + /* Verify that the nd_cmd_pkg.nd_family is correct */ 285 + nd_cmd = (struct nd_cmd_pkg *)buf; 286 + 287 + if (nd_cmd->nd_family != NVDIMM_FAMILY_PAPR) { 288 + dev_dbg(&p->pdev->dev, "Invalid pkg family=0x%llx\n", 289 + nd_cmd->nd_family); 290 + return -EINVAL; 291 + } 292 + 293 + pdsm = (enum papr_pdsm)nd_cmd->nd_command; 294 + 295 + /* Verify if the pdsm command is valid */ 296 + if (pdsm <= PAPR_PDSM_MIN || pdsm >= PAPR_PDSM_MAX) { 297 + dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid PDSM\n", 298 + pdsm); 299 + return -EINVAL; 300 + } 301 + 302 + /* Have enough space to hold returned 'nd_pkg_pdsm' header */ 303 + if (nd_cmd->nd_size_out < ND_PDSM_HDR_SIZE) { 304 + dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid payload\n", 305 + pdsm); 306 + return -EINVAL; 307 + } 308 + } 309 + 310 + /* Let the command be further processed */ 311 + return 0; 312 + } 313 + 314 + /* Fetch the DIMM health info and populate it in provided package. */ 315 + static int papr_pdsm_health(struct papr_scm_priv *p, 316 + union nd_pdsm_payload *payload) 317 + { 318 + int rc; 319 + 320 + /* Ensure dimm health mutex is taken preventing concurrent access */ 321 + rc = mutex_lock_interruptible(&p->health_mutex); 322 + if (rc) 323 + goto out; 324 + 325 + /* Always fetch upto date dimm health data ignoring cached values */ 326 + rc = __drc_pmem_query_health(p); 327 + if (rc) { 328 + mutex_unlock(&p->health_mutex); 329 + goto out; 330 + } 331 + 332 + /* update health struct with various flags derived from health bitmap */ 333 + payload->health = (struct nd_papr_pdsm_health) { 334 + .extension_flags = 0, 335 + .dimm_unarmed = !!(p->health_bitmap & PAPR_PMEM_UNARMED_MASK), 336 + .dimm_bad_shutdown = !!(p->health_bitmap & PAPR_PMEM_BAD_SHUTDOWN_MASK), 337 + .dimm_bad_restore = !!(p->health_bitmap & PAPR_PMEM_BAD_RESTORE_MASK), 338 + .dimm_scrubbed = !!(p->health_bitmap & PAPR_PMEM_SCRUBBED_AND_LOCKED), 339 + .dimm_locked = !!(p->health_bitmap & PAPR_PMEM_SCRUBBED_AND_LOCKED), 340 + .dimm_encrypted = !!(p->health_bitmap & PAPR_PMEM_ENCRYPTED), 341 + .dimm_health = PAPR_PDSM_DIMM_HEALTHY, 342 + }; 343 + 344 + /* Update field dimm_health based on health_bitmap flags */ 345 + if (p->health_bitmap & PAPR_PMEM_HEALTH_FATAL) 346 + payload->health.dimm_health = PAPR_PDSM_DIMM_FATAL; 347 + else if (p->health_bitmap & PAPR_PMEM_HEALTH_CRITICAL) 348 + payload->health.dimm_health = PAPR_PDSM_DIMM_CRITICAL; 349 + else if (p->health_bitmap & PAPR_PMEM_HEALTH_UNHEALTHY) 350 + payload->health.dimm_health = PAPR_PDSM_DIMM_UNHEALTHY; 351 + 352 + /* struct populated hence can release the mutex now */ 353 + mutex_unlock(&p->health_mutex); 354 + rc = sizeof(struct nd_papr_pdsm_health); 355 + 356 + out: 357 + return rc; 358 + } 359 + 360 + /* 361 + * 'struct pdsm_cmd_desc' 362 + * Identifies supported PDSMs' expected length of in/out payloads 363 + * and pdsm service function. 364 + * 365 + * size_in : Size of input payload if any in the PDSM request. 366 + * size_out : Size of output payload if any in the PDSM request. 367 + * service : Service function for the PDSM request. Return semantics: 368 + * rc < 0 : Error servicing PDSM and rc indicates the error. 369 + * rc >=0 : Serviced successfully and 'rc' indicate number of 370 + * bytes written to payload. 371 + */ 372 + struct pdsm_cmd_desc { 373 + u32 size_in; 374 + u32 size_out; 375 + int (*service)(struct papr_scm_priv *dimm, 376 + union nd_pdsm_payload *payload); 377 + }; 378 + 379 + /* Holds all supported PDSMs' command descriptors */ 380 + static const struct pdsm_cmd_desc __pdsm_cmd_descriptors[] = { 381 + [PAPR_PDSM_MIN] = { 382 + .size_in = 0, 383 + .size_out = 0, 384 + .service = NULL, 385 + }, 386 + /* New PDSM command descriptors to be added below */ 387 + 388 + [PAPR_PDSM_HEALTH] = { 389 + .size_in = 0, 390 + .size_out = sizeof(struct nd_papr_pdsm_health), 391 + .service = papr_pdsm_health, 392 + }, 393 + /* Empty */ 394 + [PAPR_PDSM_MAX] = { 395 + .size_in = 0, 396 + .size_out = 0, 397 + .service = NULL, 398 + }, 399 + }; 400 + 401 + /* Given a valid pdsm cmd return its command descriptor else return NULL */ 402 + static inline const struct pdsm_cmd_desc *pdsm_cmd_desc(enum papr_pdsm cmd) 403 + { 404 + if (cmd >= 0 || cmd < ARRAY_SIZE(__pdsm_cmd_descriptors)) 405 + return &__pdsm_cmd_descriptors[cmd]; 406 + 407 + return NULL; 408 + } 409 + 410 + /* 411 + * For a given pdsm request call an appropriate service function. 412 + * Returns errors if any while handling the pdsm command package. 413 + */ 414 + static int papr_scm_service_pdsm(struct papr_scm_priv *p, 415 + struct nd_cmd_pkg *pkg) 416 + { 417 + /* Get the PDSM header and PDSM command */ 418 + struct nd_pkg_pdsm *pdsm_pkg = (struct nd_pkg_pdsm *)pkg->nd_payload; 419 + enum papr_pdsm pdsm = (enum papr_pdsm)pkg->nd_command; 420 + const struct pdsm_cmd_desc *pdsc; 421 + int rc; 422 + 423 + /* Fetch corresponding pdsm descriptor for validation and servicing */ 424 + pdsc = pdsm_cmd_desc(pdsm); 425 + 426 + /* Validate pdsm descriptor */ 427 + /* Ensure that reserved fields are 0 */ 428 + if (pdsm_pkg->reserved[0] || pdsm_pkg->reserved[1]) { 429 + dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid reserved field\n", 430 + pdsm); 431 + return -EINVAL; 432 + } 433 + 434 + /* If pdsm expects some input, then ensure that the size_in matches */ 435 + if (pdsc->size_in && 436 + pkg->nd_size_in != (pdsc->size_in + ND_PDSM_HDR_SIZE)) { 437 + dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_in=%d\n", 438 + pdsm, pkg->nd_size_in); 439 + return -EINVAL; 440 + } 441 + 442 + /* If pdsm wants to return data, then ensure that size_out matches */ 443 + if (pdsc->size_out && 444 + pkg->nd_size_out != (pdsc->size_out + ND_PDSM_HDR_SIZE)) { 445 + dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_out=%d\n", 446 + pdsm, pkg->nd_size_out); 447 + return -EINVAL; 448 + } 449 + 450 + /* Service the pdsm */ 451 + if (pdsc->service) { 452 + dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Servicing..\n", pdsm); 453 + 454 + rc = pdsc->service(p, &pdsm_pkg->payload); 455 + 456 + if (rc < 0) { 457 + /* error encountered while servicing pdsm */ 458 + pdsm_pkg->cmd_status = rc; 459 + pkg->nd_fw_size = ND_PDSM_HDR_SIZE; 460 + } else { 461 + /* pdsm serviced and 'rc' bytes written to payload */ 462 + pdsm_pkg->cmd_status = 0; 463 + pkg->nd_fw_size = ND_PDSM_HDR_SIZE + rc; 464 + } 465 + } else { 466 + dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Unsupported PDSM request\n", 467 + pdsm); 468 + pdsm_pkg->cmd_status = -ENOENT; 469 + pkg->nd_fw_size = ND_PDSM_HDR_SIZE; 470 + } 471 + 472 + return pdsm_pkg->cmd_status; 473 + } 474 + 475 static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, 476 struct nvdimm *nvdimm, unsigned int cmd, void *buf, 477 unsigned int buf_len, int *cmd_rc) 478 { 479 struct nd_cmd_get_config_size *get_size_hdr; 480 + struct nd_cmd_pkg *call_pkg = NULL; 481 struct papr_scm_priv *p; 482 + int rc; 483 484 + rc = is_cmd_valid(nvdimm, cmd, buf, buf_len); 485 + if (rc) { 486 + pr_debug("Invalid cmd=0x%x. Err=%d\n", cmd, rc); 487 + return rc; 488 + } 489 + 490 + /* Use a local variable in case cmd_rc pointer is NULL */ 491 + if (!cmd_rc) 492 + cmd_rc = &rc; 493 494 p = nvdimm_provider_data(nvdimm); 495 ··· 277 *cmd_rc = papr_scm_meta_set(p, buf); 278 break; 279 280 + case ND_CMD_CALL: 281 + call_pkg = (struct nd_cmd_pkg *)buf; 282 + *cmd_rc = papr_scm_service_pdsm(p, call_pkg); 283 + break; 284 + 285 default: 286 + dev_dbg(&p->pdev->dev, "Unknown command = %d\n", cmd); 287 return -EINVAL; 288 } 289 ··· 285 286 return 0; 287 } 288 + 289 + static ssize_t flags_show(struct device *dev, 290 + struct device_attribute *attr, char *buf) 291 + { 292 + struct nvdimm *dimm = to_nvdimm(dev); 293 + struct papr_scm_priv *p = nvdimm_provider_data(dimm); 294 + struct seq_buf s; 295 + u64 health; 296 + int rc; 297 + 298 + rc = drc_pmem_query_health(p); 299 + if (rc) 300 + return rc; 301 + 302 + /* Copy health_bitmap locally, check masks & update out buffer */ 303 + health = READ_ONCE(p->health_bitmap); 304 + 305 + seq_buf_init(&s, buf, PAGE_SIZE); 306 + if (health & PAPR_PMEM_UNARMED_MASK) 307 + seq_buf_printf(&s, "not_armed "); 308 + 309 + if (health & PAPR_PMEM_BAD_SHUTDOWN_MASK) 310 + seq_buf_printf(&s, "flush_fail "); 311 + 312 + if (health & PAPR_PMEM_BAD_RESTORE_MASK) 313 + seq_buf_printf(&s, "restore_fail "); 314 + 315 + if (health & PAPR_PMEM_ENCRYPTED) 316 + seq_buf_printf(&s, "encrypted "); 317 + 318 + if (health & PAPR_PMEM_SMART_EVENT_MASK) 319 + seq_buf_printf(&s, "smart_notify "); 320 + 321 + if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED) 322 + seq_buf_printf(&s, "scrubbed locked "); 323 + 324 + if (seq_buf_used(&s)) 325 + seq_buf_printf(&s, "\n"); 326 + 327 + return seq_buf_used(&s); 328 + } 329 + DEVICE_ATTR_RO(flags); 330 + 331 + /* papr_scm specific dimm attributes */ 332 + static struct attribute *papr_nd_attributes[] = { 333 + &dev_attr_flags.attr, 334 + NULL, 335 + }; 336 + 337 + static struct attribute_group papr_nd_attribute_group = { 338 + .name = "papr", 339 + .attrs = papr_nd_attributes, 340 + }; 341 + 342 + static const struct attribute_group *papr_nd_attr_groups[] = { 343 + &papr_nd_attribute_group, 344 + NULL, 345 + }; 346 347 static int papr_scm_nvdimm_init(struct papr_scm_priv *p) 348 { ··· 312 dimm_flags = 0; 313 set_bit(NDD_LABELING, &dimm_flags); 314 315 + p->nvdimm = nvdimm_create(p->bus, p, papr_nd_attr_groups, 316 + dimm_flags, PAPR_SCM_DIMM_CMD_MASK, 0, NULL); 317 if (!p->nvdimm) { 318 dev_err(dev, "Error creating DIMM object for %pOF\n", p->dn); 319 goto err; ··· 398 p = kzalloc(sizeof(*p), GFP_KERNEL); 399 if (!p) 400 return -ENOMEM; 401 + 402 + /* Initialize the dimm mutex */ 403 + mutex_init(&p->health_mutex); 404 405 /* optional DT properties */ 406 of_property_read_u32(dn, "ibm,metadata-size", &metadata_size);
+1
include/uapi/linux/ndctl.h
··· 244 #define NVDIMM_FAMILY_HPE2 2 245 #define NVDIMM_FAMILY_MSFT 3 246 #define NVDIMM_FAMILY_HYPERV 4 247 248 #define ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\ 249 struct nd_cmd_pkg)
··· 244 #define NVDIMM_FAMILY_HPE2 2 245 #define NVDIMM_FAMILY_MSFT 3 246 #define NVDIMM_FAMILY_HYPERV 4 247 + #define NVDIMM_FAMILY_PAPR 5 248 249 #define ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\ 250 struct nd_cmd_pkg)
+1
lib/seq_buf.c
··· 91 92 return ret; 93 } 94 95 #ifdef CONFIG_BINARY_PRINTF 96 /**
··· 91 92 return ret; 93 } 94 + EXPORT_SYMBOL_GPL(seq_buf_printf); 95 96 #ifdef CONFIG_BINARY_PRINTF 97 /**