| @@ -12,16 +12,57 @@ · 11 unmodified lines |
| 12 | #include <linux/libnvdimm.h> | 12 | #include <linux/libnvdimm.h> |
| 13 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
| 14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
| | 15 | #include <linux/seq_buf.h> |
| 15 | | 16 | |
| 16 | #include <asm/plpar_wrappers.h> | 17 | #include <asm/plpar_wrappers.h> |
| | 18 | #include <asm/papr_pdsm.h> |
| 17 | | 19 | |
| 18 | #define BIND_ANY_ADDR (~0ul) | 20 | #define BIND_ANY_ADDR (~0ul) |
| 19 | | 21 | |
| 20 | #define PAPR_SCM_DIMM_CMD_MASK \ | 22 | #define PAPR_SCM_DIMM_CMD_MASK \ |
| 21 | ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ | 23 | ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ |
| 22 | (1ul << ND_CMD_GET_CONFIG_DATA) | \ | 24 | (1ul << ND_CMD_GET_CONFIG_DATA) | \ |
| 23 | (1ul << ND_CMD_SET_CONFIG_DATA)) | 25 | (1ul << ND_CMD_SET_CONFIG_DATA) | \ |
| 24 | | 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 */ |
| 25 | struct papr_scm_priv { | 66 | struct papr_scm_priv { |
| 26 | struct platform_device *pdev; | 67 | struct platform_device *pdev; |
| 27 | struct device_node *dn; | 68 | struct device_node *dn; |
| @@ -39,6 +80,15 @@ struct papr_scm_priv { · 11 unmodified lines |
| 39 | struct resource res; | 80 | struct resource res; |
| 40 | struct nd_region *region; | 81 | struct nd_region *region; |
| 41 | struct nd_interleave_set nd_set; | 82 | struct nd_interleave_set nd_set; |
| | 83 | |
| | 84 | /* Protect dimm health data from concurrent read/writes */ |
| | 85 | struct mutex health_mutex; |
| | 86 | |
| | 87 | /* Last time the health information of the dimm was updated */ |
| | 88 | unsigned long lasthealth_jiffies; |
| | 89 | |
| | 90 | /* Health information for the dimm */ |
| | 91 | u64 health_bitmap; |
| 42 | }; | 92 | }; |
| 43 | | 93 | |
| 44 | static int drc_pmem_bind(struct papr_scm_priv *p) | 94 | static int drc_pmem_bind(struct papr_scm_priv *p) |
| @@ -144,6 +194,61 @@ err_out: · 99 unmodified lines |
| 144 | return drc_pmem_bind(p); | 194 | return drc_pmem_bind(p); |
| 145 | } | 195 | } |
| 146 | | 196 | |
| | 197 | /* |
| | 198 | * Issue hcall to retrieve dimm health info and populate papr_scm_priv with the |
| | 199 | * health information. |
| | 200 | */ |
| | 201 | static int __drc_pmem_query_health(struct papr_scm_priv *p) |
| | 202 | { |
| | 203 | unsigned long ret[PLPAR_HCALL_BUFSIZE]; |
| | 204 | long rc; |
| | 205 | |
| | 206 | /* issue the hcall */ |
| | 207 | rc = plpar_hcall(H_SCM_HEALTH, ret, p->drc_index); |
| | 208 | if (rc != H_SUCCESS) { |
| | 209 | dev_err(&p->pdev->dev, |
| | 210 | "Failed to query health information, Err:%ld\n", rc); |
| | 211 | return -ENXIO; |
| | 212 | } |
| | 213 | |
| | 214 | p->lasthealth_jiffies = jiffies; |
| | 215 | p->health_bitmap = ret[0] & ret[1]; |
| | 216 | |
| | 217 | dev_dbg(&p->pdev->dev, |
| | 218 | "Queried dimm health info. Bitmap:0x%016lx Mask:0x%016lx\n", |
| | 219 | ret[0], ret[1]); |
| | 220 | |
| | 221 | return 0; |
| | 222 | } |
| | 223 | |
| | 224 | /* Min interval in seconds for assuming stable dimm health */ |
| | 225 | #define MIN_HEALTH_QUERY_INTERVAL 60 |
| | 226 | |
| | 227 | /* Query cached health info and if needed call drc_pmem_query_health */ |
| | 228 | static int drc_pmem_query_health(struct papr_scm_priv *p) |
| | 229 | { |
| | 230 | unsigned long cache_timeout; |
| | 231 | int rc; |
| | 232 | |
| | 233 | /* Protect concurrent modifications to papr_scm_priv */ |
| | 234 | rc = mutex_lock_interruptible(&p->health_mutex); |
| | 235 | if (rc) |
| | 236 | return rc; |
| | 237 | |
| | 238 | /* Jiffies offset for which the health data is assumed to be same */ |
| | 239 | cache_timeout = p->lasthealth_jiffies + |
| | 240 | msecs_to_jiffies(MIN_HEALTH_QUERY_INTERVAL * 1000); |
| | 241 | |
| | 242 | /* Fetch new health info is its older than MIN_HEALTH_QUERY_INTERVAL */ |
| | 243 | if (time_after(jiffies, cache_timeout)) |
| | 244 | rc = __drc_pmem_query_health(p); |
| | 245 | else |
| | 246 | /* Assume cached health data is valid */ |
| | 247 | rc = 0; |
| | 248 | |
| | 249 | mutex_unlock(&p->health_mutex); |
| | 250 | return rc; |
| | 251 | } |
| 147 | | 252 | |
| 148 | static int papr_scm_meta_get(struct papr_scm_priv *p, | 253 | static int papr_scm_meta_get(struct papr_scm_priv *p, |
| 149 | struct nd_cmd_get_config_data_hdr *hdr) | 254 | struct nd_cmd_get_config_data_hdr *hdr) |
| @@ -246,16 +351,250 @@ static int papr_scm_meta_set(struct papr_scm_priv *p, · 96 unmodified lines |
| 246 | return 0; | 351 | return 0; |
| 247 | } | 352 | } |
| 248 | | 353 | |
| | 354 | /* |
| | 355 | * Do a sanity checks on the inputs args to dimm-control function and return |
| | 356 | * '0' if valid. Validation of PDSM payloads happens later in |
| | 357 | * papr_scm_service_pdsm. |
| | 358 | */ |
| | 359 | static int is_cmd_valid(struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
| | 360 | unsigned int buf_len) |
| | 361 | { |
| | 362 | unsigned long cmd_mask = PAPR_SCM_DIMM_CMD_MASK; |
| | 363 | struct nd_cmd_pkg *nd_cmd; |
| | 364 | struct papr_scm_priv *p; |
| | 365 | enum papr_pdsm pdsm; |
| | 366 | |
| | 367 | /* Only dimm-specific calls are supported atm */ |
| | 368 | if (!nvdimm) |
| | 369 | return -EINVAL; |
| | 370 | |
| | 371 | /* get the provider data from struct nvdimm */ |
| | 372 | p = nvdimm_provider_data(nvdimm); |
| | 373 | |
| | 374 | if (!test_bit(cmd, &cmd_mask)) { |
| | 375 | dev_dbg(&p->pdev->dev, "Unsupported cmd=%u\n", cmd); |
| | 376 | return -EINVAL; |
| | 377 | } |
| | 378 | |
| | 379 | /* For CMD_CALL verify pdsm request */ |
| | 380 | if (cmd == ND_CMD_CALL) { |
| | 381 | /* Verify the envelope and envelop size */ |
| | 382 | if (!buf || |
| | 383 | buf_len < (sizeof(struct nd_cmd_pkg) + ND_PDSM_HDR_SIZE)) { |
| | 384 | dev_dbg(&p->pdev->dev, "Invalid pkg size=%u\n", |
| | 385 | buf_len); |
| | 386 | return -EINVAL; |
| | 387 | } |
| | 388 | |
| | 389 | /* Verify that the nd_cmd_pkg.nd_family is correct */ |
| | 390 | nd_cmd = (struct nd_cmd_pkg *)buf; |
| | 391 | |
| | 392 | if (nd_cmd->nd_family != NVDIMM_FAMILY_PAPR) { |
| | 393 | dev_dbg(&p->pdev->dev, "Invalid pkg family=0x%llx\n", |
| | 394 | nd_cmd->nd_family); |
| | 395 | return -EINVAL; |
| | 396 | } |
| | 397 | |
| | 398 | pdsm = (enum papr_pdsm)nd_cmd->nd_command; |
| | 399 | |
| | 400 | /* Verify if the pdsm command is valid */ |
| | 401 | if (pdsm <= PAPR_PDSM_MIN || pdsm >= PAPR_PDSM_MAX) { |
| | 402 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid PDSM\n", |
| | 403 | pdsm); |
| | 404 | return -EINVAL; |
| | 405 | } |
| | 406 | |
| | 407 | /* Have enough space to hold returned 'nd_pkg_pdsm' header */ |
| | 408 | if (nd_cmd->nd_size_out < ND_PDSM_HDR_SIZE) { |
| | 409 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid payload\n", |
| | 410 | pdsm); |
| | 411 | return -EINVAL; |
| | 412 | } |
| | 413 | } |
| | 414 | |
| | 415 | /* Let the command be further processed */ |
| | 416 | return 0; |
| | 417 | } |
| | 418 | |
| | 419 | /* Fetch the DIMM health info and populate it in provided package. */ |
| | 420 | static int papr_pdsm_health(struct papr_scm_priv *p, |
| | 421 | union nd_pdsm_payload *payload) |
| | 422 | { |
| | 423 | int rc; |
| | 424 | |
| | 425 | /* Ensure dimm health mutex is taken preventing concurrent access */ |
| | 426 | rc = mutex_lock_interruptible(&p->health_mutex); |
| | 427 | if (rc) |
| | 428 | goto out; |
| | 429 | |
| | 430 | /* Always fetch upto date dimm health data ignoring cached values */ |
| | 431 | rc = __drc_pmem_query_health(p); |
| | 432 | if (rc) { |
| | 433 | mutex_unlock(&p->health_mutex); |
| | 434 | goto out; |
| | 435 | } |
| | 436 | |
| | 437 | /* update health struct with various flags derived from health bitmap */ |
| | 438 | payload->health = (struct nd_papr_pdsm_health) { |
| | 439 | .extension_flags = 0, |
| | 440 | .dimm_unarmed = !!(p->health_bitmap & PAPR_PMEM_UNARMED_MASK), |
| | 441 | .dimm_bad_shutdown = !!(p->health_bitmap & PAPR_PMEM_BAD_SHUTDOWN_MASK), |
| | 442 | .dimm_bad_restore = !!(p->health_bitmap & PAPR_PMEM_BAD_RESTORE_MASK), |
| | 443 | .dimm_scrubbed = !!(p->health_bitmap & PAPR_PMEM_SCRUBBED_AND_LOCKED), |
| | 444 | .dimm_locked = !!(p->health_bitmap & PAPR_PMEM_SCRUBBED_AND_LOCKED), |
| | 445 | .dimm_encrypted = !!(p->health_bitmap & PAPR_PMEM_ENCRYPTED), |
| | 446 | .dimm_health = PAPR_PDSM_DIMM_HEALTHY, |
| | 447 | }; |
| | 448 | |
| | 449 | /* Update field dimm_health based on health_bitmap flags */ |
| | 450 | if (p->health_bitmap & PAPR_PMEM_HEALTH_FATAL) |
| | 451 | payload->health.dimm_health = PAPR_PDSM_DIMM_FATAL; |
| | 452 | else if (p->health_bitmap & PAPR_PMEM_HEALTH_CRITICAL) |
| | 453 | payload->health.dimm_health = PAPR_PDSM_DIMM_CRITICAL; |
| | 454 | else if (p->health_bitmap & PAPR_PMEM_HEALTH_UNHEALTHY) |
| | 455 | payload->health.dimm_health = PAPR_PDSM_DIMM_UNHEALTHY; |
| | 456 | |
| | 457 | /* struct populated hence can release the mutex now */ |
| | 458 | mutex_unlock(&p->health_mutex); |
| | 459 | rc = sizeof(struct nd_papr_pdsm_health); |
| | 460 | |
| | 461 | out: |
| | 462 | return rc; |
| | 463 | } |
| | 464 | |
| | 465 | /* |
| | 466 | * 'struct pdsm_cmd_desc' |
| | 467 | * Identifies supported PDSMs' expected length of in/out payloads |
| | 468 | * and pdsm service function. |
| | 469 | * |
| | 470 | * size_in : Size of input payload if any in the PDSM request. |
| | 471 | * size_out : Size of output payload if any in the PDSM request. |
| | 472 | * service : Service function for the PDSM request. Return semantics: |
| | 473 | * rc < 0 : Error servicing PDSM and rc indicates the error. |
| | 474 | * rc >=0 : Serviced successfully and 'rc' indicate number of |
| | 475 | * bytes written to payload. |
| | 476 | */ |
| | 477 | struct pdsm_cmd_desc { |
| | 478 | u32 size_in; |
| | 479 | u32 size_out; |
| | 480 | int (*service)(struct papr_scm_priv *dimm, |
| | 481 | union nd_pdsm_payload *payload); |
| | 482 | }; |
| | 483 | |
| | 484 | /* Holds all supported PDSMs' command descriptors */ |
| | 485 | static const struct pdsm_cmd_desc __pdsm_cmd_descriptors[] = { |
| | 486 | [PAPR_PDSM_MIN] = { |
| | 487 | .size_in = 0, |
| | 488 | .size_out = 0, |
| | 489 | .service = NULL, |
| | 490 | }, |
| | 491 | /* New PDSM command descriptors to be added below */ |
| | 492 | |
| | 493 | [PAPR_PDSM_HEALTH] = { |
| | 494 | .size_in = 0, |
| | 495 | .size_out = sizeof(struct nd_papr_pdsm_health), |
| | 496 | .service = papr_pdsm_health, |
| | 497 | }, |
| | 498 | /* Empty */ |
| | 499 | [PAPR_PDSM_MAX] = { |
| | 500 | .size_in = 0, |
| | 501 | .size_out = 0, |
| | 502 | .service = NULL, |
| | 503 | }, |
| | 504 | }; |
| | 505 | |
| | 506 | /* Given a valid pdsm cmd return its command descriptor else return NULL */ |
| | 507 | static inline const struct pdsm_cmd_desc *pdsm_cmd_desc(enum papr_pdsm cmd) |
| | 508 | { |
| | 509 | if (cmd >= 0 || cmd < ARRAY_SIZE(__pdsm_cmd_descriptors)) |
| | 510 | return &__pdsm_cmd_descriptors[cmd]; |
| | 511 | |
| | 512 | return NULL; |
| | 513 | } |
| | 514 | |
| | 515 | /* |
| | 516 | * For a given pdsm request call an appropriate service function. |
| | 517 | * Returns errors if any while handling the pdsm command package. |
| | 518 | */ |
| | 519 | static int papr_scm_service_pdsm(struct papr_scm_priv *p, |
| | 520 | struct nd_cmd_pkg *pkg) |
| | 521 | { |
| | 522 | /* Get the PDSM header and PDSM command */ |
| | 523 | struct nd_pkg_pdsm *pdsm_pkg = (struct nd_pkg_pdsm *)pkg->nd_payload; |
| | 524 | enum papr_pdsm pdsm = (enum papr_pdsm)pkg->nd_command; |
| | 525 | const struct pdsm_cmd_desc *pdsc; |
| | 526 | int rc; |
| | 527 | |
| | 528 | /* Fetch corresponding pdsm descriptor for validation and servicing */ |
| | 529 | pdsc = pdsm_cmd_desc(pdsm); |
| | 530 | |
| | 531 | /* Validate pdsm descriptor */ |
| | 532 | /* Ensure that reserved fields are 0 */ |
| | 533 | if (pdsm_pkg->reserved[0] || pdsm_pkg->reserved[1]) { |
| | 534 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Invalid reserved field\n", |
| | 535 | pdsm); |
| | 536 | return -EINVAL; |
| | 537 | } |
| | 538 | |
| | 539 | /* If pdsm expects some input, then ensure that the size_in matches */ |
| | 540 | if (pdsc->size_in && |
| | 541 | pkg->nd_size_in != (pdsc->size_in + ND_PDSM_HDR_SIZE)) { |
| | 542 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_in=%d\n", |
| | 543 | pdsm, pkg->nd_size_in); |
| | 544 | return -EINVAL; |
| | 545 | } |
| | 546 | |
| | 547 | /* If pdsm wants to return data, then ensure that size_out matches */ |
| | 548 | if (pdsc->size_out && |
| | 549 | pkg->nd_size_out != (pdsc->size_out + ND_PDSM_HDR_SIZE)) { |
| | 550 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Mismatched size_out=%d\n", |
| | 551 | pdsm, pkg->nd_size_out); |
| | 552 | return -EINVAL; |
| | 553 | } |
| | 554 | |
| | 555 | /* Service the pdsm */ |
| | 556 | if (pdsc->service) { |
| | 557 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Servicing..\n", pdsm); |
| | 558 | |
| | 559 | rc = pdsc->service(p, &pdsm_pkg->payload); |
| | 560 | |
| | 561 | if (rc < 0) { |
| | 562 | /* error encountered while servicing pdsm */ |
| | 563 | pdsm_pkg->cmd_status = rc; |
| | 564 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE; |
| | 565 | } else { |
| | 566 | /* pdsm serviced and 'rc' bytes written to payload */ |
| | 567 | pdsm_pkg->cmd_status = 0; |
| | 568 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE + rc; |
| | 569 | } |
| | 570 | } else { |
| | 571 | dev_dbg(&p->pdev->dev, "PDSM[0x%x]: Unsupported PDSM request\n", |
| | 572 | pdsm); |
| | 573 | pdsm_pkg->cmd_status = -ENOENT; |
| | 574 | pkg->nd_fw_size = ND_PDSM_HDR_SIZE; |
| | 575 | } |
| | 576 | |
| | 577 | return pdsm_pkg->cmd_status; |
| | 578 | } |
| | 579 | |
| 249 | static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, | 580 | static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, |
| 250 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, | 581 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
| 251 | unsigned int buf_len, int *cmd_rc) | 582 | unsigned int buf_len, int *cmd_rc) |
| 252 | { | 583 | { |
| 253 | struct nd_cmd_get_config_size *get_size_hdr; | 584 | struct nd_cmd_get_config_size *get_size_hdr; |
| | 585 | struct nd_cmd_pkg *call_pkg = NULL; |
| 254 | struct papr_scm_priv *p; | 586 | struct papr_scm_priv *p; |
| | 587 | int rc; |
| 255 | | 588 | |
| 256 | /* Only dimm-specific calls are supported atm */ | 589 | rc = is_cmd_valid(nvdimm, cmd, buf, buf_len); |
| 257 | if (!nvdimm) | 590 | if (rc) { |
| 258 | return -EINVAL; | 591 | pr_debug("Invalid cmd=0x%x. Err=%d\n", cmd, rc); |
| | 592 | return rc; |
| | 593 | } |
| | 594 | |
| | 595 | /* Use a local variable in case cmd_rc pointer is NULL */ |
| | 596 | if (!cmd_rc) |
| | 597 | cmd_rc = &rc; |
| 259 | | 598 | |
| 260 | p = nvdimm_provider_data(nvdimm); | 599 | p = nvdimm_provider_data(nvdimm); |
| 261 | | 600 | |
| @@ -277,7 +616,13 @@ static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, · 15 unmodified lines |
| 277 | *cmd_rc = papr_scm_meta_set(p, buf); | 616 | *cmd_rc = papr_scm_meta_set(p, buf); |
| 278 | break; | 617 | break; |
| 279 | | 618 | |
| | 619 | case ND_CMD_CALL: |
| | 620 | call_pkg = (struct nd_cmd_pkg *)buf; |
| | 621 | *cmd_rc = papr_scm_service_pdsm(p, call_pkg); |
| | 622 | break; |
| | 623 | |
| 280 | default: | 624 | default: |
| | 625 | dev_dbg(&p->pdev->dev, "Unknown command = %d\n", cmd); |
| 281 | return -EINVAL; | 626 | return -EINVAL; |
| 282 | } | 627 | } |
| 283 | | 628 | |
| @@ -286,6 +631,64 @@ static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, · 2 unmodified lines |
| 286 | return 0; | 631 | return 0; |
| 287 | } | 632 | } |
| 288 | | 633 | |
| | 634 | static ssize_t flags_show(struct device *dev, |
| | 635 | struct device_attribute *attr, char *buf) |
| | 636 | { |
| | 637 | struct nvdimm *dimm = to_nvdimm(dev); |
| | 638 | struct papr_scm_priv *p = nvdimm_provider_data(dimm); |
| | 639 | struct seq_buf s; |
| | 640 | u64 health; |
| | 641 | int rc; |
| | 642 | |
| | 643 | rc = drc_pmem_query_health(p); |
| | 644 | if (rc) |
| | 645 | return rc; |
| | 646 | |
| | 647 | /* Copy health_bitmap locally, check masks & update out buffer */ |
| | 648 | health = READ_ONCE(p->health_bitmap); |
| | 649 | |
| | 650 | seq_buf_init(&s, buf, PAGE_SIZE); |
| | 651 | if (health & PAPR_PMEM_UNARMED_MASK) |
| | 652 | seq_buf_printf(&s, "not_armed "); |
| | 653 | |
| | 654 | if (health & PAPR_PMEM_BAD_SHUTDOWN_MASK) |
| | 655 | seq_buf_printf(&s, "flush_fail "); |
| | 656 | |
| | 657 | if (health & PAPR_PMEM_BAD_RESTORE_MASK) |
| | 658 | seq_buf_printf(&s, "restore_fail "); |
| | 659 | |
| | 660 | if (health & PAPR_PMEM_ENCRYPTED) |
| | 661 | seq_buf_printf(&s, "encrypted "); |
| | 662 | |
| | 663 | if (health & PAPR_PMEM_SMART_EVENT_MASK) |
| | 664 | seq_buf_printf(&s, "smart_notify "); |
| | 665 | |
| | 666 | if (health & PAPR_PMEM_SCRUBBED_AND_LOCKED) |
| | 667 | seq_buf_printf(&s, "scrubbed locked "); |
| | 668 | |
| | 669 | if (seq_buf_used(&s)) |
| | 670 | seq_buf_printf(&s, "\n"); |
| | 671 | |
| | 672 | return seq_buf_used(&s); |
| | 673 | } |
| | 674 | DEVICE_ATTR_RO(flags); |
| | 675 | |
| | 676 | /* papr_scm specific dimm attributes */ |
| | 677 | static struct attribute *papr_nd_attributes[] = { |
| | 678 | &dev_attr_flags.attr, |
| | 679 | NULL, |
| | 680 | }; |
| | 681 | |
| | 682 | static struct attribute_group papr_nd_attribute_group = { |
| | 683 | .name = "papr", |
| | 684 | .attrs = papr_nd_attributes, |
| | 685 | }; |
| | 686 | |
| | 687 | static const struct attribute_group *papr_nd_attr_groups[] = { |
| | 688 | &papr_nd_attribute_group, |
| | 689 | NULL, |
| | 690 | }; |
| | 691 | |
| 289 | static int papr_scm_nvdimm_init(struct papr_scm_priv *p) | 692 | static int papr_scm_nvdimm_init(struct papr_scm_priv *p) |
| 290 | { | 693 | { |
| 291 | struct device *dev = &p->pdev->dev; | 694 | struct device *dev = &p->pdev->dev; |
| @@ -312,8 +715,8 @@ static int papr_scm_nvdimm_init(struct papr_scm_priv *p) · 20 unmodified lines |
| 312 | dimm_flags = 0; | 715 | dimm_flags = 0; |
| 313 | set_bit(NDD_LABELING, &dimm_flags); | 716 | set_bit(NDD_LABELING, &dimm_flags); |
| 314 | | 717 | |
| 315 | p->nvdimm = nvdimm_create(p->bus, p, NULL, dimm_flags, | 718 | p->nvdimm = nvdimm_create(p->bus, p, papr_nd_attr_groups, |
| 316 | PAPR_SCM_DIMM_CMD_MASK, 0, NULL); | 719 | dimm_flags, PAPR_SCM_DIMM_CMD_MASK, 0, NULL); |
| 317 | if (!p->nvdimm) { | 720 | if (!p->nvdimm) { |
| 318 | dev_err(dev, "Error creating DIMM object for %pOF\n", p->dn); | 721 | dev_err(dev, "Error creating DIMM object for %pOF\n", p->dn); |
| 319 | goto err; | 722 | goto err; |
| @@ -399,6 +802,9 @@ static int papr_scm_probe(struct platform_device *pdev) · 79 unmodified lines |
| 399 | if (!p) | 802 | if (!p) |
| 400 | return -ENOMEM; | 803 | return -ENOMEM; |
| 401 | | 804 | |
| | 805 | /* Initialize the dimm mutex */ |
| | 806 | mutex_init(&p->health_mutex); |
| | 807 | |
| 402 | /* optional DT properties */ | 808 | /* optional DT properties */ |
| 403 | of_property_read_u32(dn, "ibm,metadata-size", &metadata_size); | 809 | of_property_read_u32(dn, "ibm,metadata-size", &metadata_size); |
| 404 | | 810 | |