| @@ -12,16 +12,57 @@ · 11 unmodified lines |
| 12 | 12 | #include <linux/libnvdimm.h> |
| 13 | 13 | #include <linux/platform_device.h> |
| 14 | 14 | #include <linux/delay.h> |
| 15 | +#include <linux/seq_buf.h> |
| 15 | 16 | |
| 16 | 17 | #include <asm/plpar_wrappers.h> |
| 18 | +#include <asm/papr_pdsm.h> |
| 17 | 19 | |
| 18 | 20 | #define BIND_ANY_ADDR (~0ul) |
| 19 | 21 | |
| 20 | 22 | #define PAPR_SCM_DIMM_CMD_MASK \ |
| 21 | 23 | ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ |
| 22 | 24 | (1ul << ND_CMD_GET_CONFIG_DATA) | \ |
| 23 | | − (1ul << ND_CMD_SET_CONFIG_DATA)) |
| 24 | | − |
| 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 */ |
| 25 | 66 | struct papr_scm_priv { |
| 26 | 67 | struct platform_device *pdev; |
| 27 | 68 | struct device_node *dn; |
| @@ -39,6 +80,15 @@ struct papr_scm_priv { · 11 unmodified lines |
| 39 | 80 | struct resource res; |
| 40 | 81 | struct nd_region *region; |
| 41 | 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 | 94 | static int drc_pmem_bind(struct papr_scm_priv *p) |
| @@ -144,6 +194,61 @@ err_out: · 99 unmodified lines |
| 144 | 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 | 253 | static int papr_scm_meta_get(struct papr_scm_priv *p, |
| 149 | 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 | 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 | 580 | static int papr_scm_ndctl(struct nvdimm_bus_descriptor *nd_desc, |
| 250 | 581 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
| 251 | 582 | unsigned int buf_len, int *cmd_rc) |
| 252 | 583 | { |
| 253 | 584 | struct nd_cmd_get_config_size *get_size_hdr; |
| 585 | + struct nd_cmd_pkg *call_pkg = NULL; |
| 254 | 586 | struct papr_scm_priv *p; |
| 587 | + int rc; |
| 255 | 588 | |
| 256 | | − /* Only dimm-specific calls are supported atm */ |
| 257 | | − if (!nvdimm) |
| 258 | | − return -EINVAL; |
| 589 | + rc = is_cmd_valid(nvdimm, cmd, buf, buf_len); |
| 590 | + if (rc) { |
| 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 | 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 | 616 | *cmd_rc = papr_scm_meta_set(p, buf); |
| 278 | 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 | 624 | default: |
| 625 | + dev_dbg(&p->pdev->dev, "Unknown command = %d\n", cmd); |
| 281 | 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 | 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 | 692 | static int papr_scm_nvdimm_init(struct papr_scm_priv *p) |
| 290 | 693 | { |
| 291 | 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 | 715 | dimm_flags = 0; |
| 313 | 716 | set_bit(NDD_LABELING, &dimm_flags); |
| 314 | 717 | |
| 315 | | − p->nvdimm = nvdimm_create(p->bus, p, NULL, dimm_flags, |
| 316 | | − PAPR_SCM_DIMM_CMD_MASK, 0, NULL); |
| 718 | + p->nvdimm = nvdimm_create(p->bus, p, papr_nd_attr_groups, |
| 719 | + dimm_flags, PAPR_SCM_DIMM_CMD_MASK, 0, NULL); |
| 317 | 720 | if (!p->nvdimm) { |
| 318 | 721 | dev_err(dev, "Error creating DIMM object for %pOF\n", p->dn); |
| 319 | 722 | goto err; |
| @@ -399,6 +802,9 @@ static int papr_scm_probe(struct platform_device *pdev) · 79 unmodified lines |
| 399 | 802 | if (!p) |
| 400 | 803 | return -ENOMEM; |
| 401 | 804 | |
| 805 | + /* Initialize the dimm mutex */ |
| 806 | + mutex_init(&p->health_mutex); |
| 807 | + |
| 402 | 808 | /* optional DT properties */ |
| 403 | 809 | of_property_read_u32(dn, "ibm,metadata-size", &metadata_size); |
| 404 | 810 | |