| @@ -30,17 +30,20 @@ static void unregister_nvdimm(void *nvdimm) · 29 unmodified lines |
| 30 | struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm); | 30 | struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm); |
| 31 | struct cxl_nvdimm_bridge *cxl_nvb = cxl_nvd->bridge; | 31 | struct cxl_nvdimm_bridge *cxl_nvb = cxl_nvd->bridge; |
| 32 | struct cxl_pmem_region *cxlr_pmem; | 32 | struct cxl_pmem_region *cxlr_pmem; |
| | 33 | unsigned long index; |
| 33 | | 34 | |
| 34 | device_lock(&cxl_nvb->dev); | 35 | device_lock(&cxl_nvb->dev); |
| 35 | cxlr_pmem = cxl_nvd->region; | | |
| 36 | dev_set_drvdata(&cxl_nvd->dev, NULL); | 36 | dev_set_drvdata(&cxl_nvd->dev, NULL); |
| 37 | cxl_nvd->region = NULL; | 37 | xa_for_each(&cxl_nvd->pmem_regions, index, cxlr_pmem) { |
| 38 | device_unlock(&cxl_nvb->dev); | 38 | get_device(&cxlr_pmem->dev); |
| | 39 | device_unlock(&cxl_nvb->dev); |
| 39 | | 40 | |
| 40 | if (cxlr_pmem) { | | |
| 41 | device_release_driver(&cxlr_pmem->dev); | 41 | device_release_driver(&cxlr_pmem->dev); |
| 42 | put_device(&cxlr_pmem->dev); | 42 | put_device(&cxlr_pmem->dev); |
| | 43 | |
| | 44 | device_lock(&cxl_nvb->dev); |
| 43 | } | 45 | } |
| | 46 | device_unlock(&cxl_nvb->dev); |
| 44 | | 47 | |
| 45 | nvdimm_delete(nvdimm); | 48 | nvdimm_delete(nvdimm); |
| 46 | cxl_nvd->bridge = NULL; | 49 | cxl_nvd->bridge = NULL; |
| @@ -366,25 +369,49 @@ static int match_cxl_nvdimm(struct device *dev, void *data) · 319 unmodified lines |
| 366 | | 369 | |
| 367 | static void unregister_nvdimm_region(void *nd_region) | 370 | static void unregister_nvdimm_region(void *nd_region) |
| 368 | { | 371 | { |
| 369 | struct cxl_nvdimm_bridge *cxl_nvb; | 372 | nvdimm_region_delete(nd_region); |
| 370 | struct cxl_pmem_region *cxlr_pmem; | 373 | } |
| | 374 | |
| | 375 | static int cxl_nvdimm_add_region(struct cxl_nvdimm *cxl_nvd, |
| | 376 | struct cxl_pmem_region *cxlr_pmem) |
| | 377 | { |
| | 378 | int rc; |
| | 379 | |
| | 380 | rc = xa_insert(&cxl_nvd->pmem_regions, (unsigned long)cxlr_pmem, |
| | 381 | cxlr_pmem, GFP_KERNEL); |
| | 382 | if (rc) |
| | 383 | return rc; |
| | 384 | |
| | 385 | get_device(&cxlr_pmem->dev); |
| | 386 | return 0; |
| | 387 | } |
| | 388 | |
| | 389 | static void cxl_nvdimm_del_region(struct cxl_nvdimm *cxl_nvd, |
| | 390 | struct cxl_pmem_region *cxlr_pmem) |
| | 391 | { |
| | 392 | /* |
| | 393 | * It is possible this is called without a corresponding |
| | 394 | * cxl_nvdimm_add_region for @cxlr_pmem |
| | 395 | */ |
| | 396 | cxlr_pmem = xa_erase(&cxl_nvd->pmem_regions, (unsigned long)cxlr_pmem); |
| | 397 | if (cxlr_pmem) |
| | 398 | put_device(&cxlr_pmem->dev); |
| | 399 | } |
| | 400 | |
| | 401 | static void release_mappings(void *data) |
| | 402 | { |
| 371 | int i; | 403 | int i; |
| | 404 | struct cxl_pmem_region *cxlr_pmem = data; |
| | 405 | struct cxl_nvdimm_bridge *cxl_nvb = cxlr_pmem->bridge; |
| 372 | | 406 | |
| 373 | cxlr_pmem = nd_region_provider_data(nd_region); | | |
| 374 | cxl_nvb = cxlr_pmem->bridge; | | |
| 375 | device_lock(&cxl_nvb->dev); | 407 | device_lock(&cxl_nvb->dev); |
| 376 | for (i = 0; i < cxlr_pmem->nr_mappings; i++) { | 408 | for (i = 0; i < cxlr_pmem->nr_mappings; i++) { |
| 377 | struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; | 409 | struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; |
| 378 | struct cxl_nvdimm *cxl_nvd = m->cxl_nvd; | 410 | struct cxl_nvdimm *cxl_nvd = m->cxl_nvd; |
| 379 | | 411 | |
| 380 | if (cxl_nvd->region) { | 412 | cxl_nvdimm_del_region(cxl_nvd, cxlr_pmem); |
| 381 | put_device(&cxlr_pmem->dev); | | |
| 382 | cxl_nvd->region = NULL; | | |
| 383 | } | | |
| 384 | } | 413 | } |
| 385 | device_unlock(&cxl_nvb->dev); | 414 | device_unlock(&cxl_nvb->dev); |
| 386 | | | |
| 387 | nvdimm_region_delete(nd_region); | | |
| 388 | } | 415 | } |
| 389 | | 416 | |
| 390 | static void cxlr_pmem_remove_resource(void *res) | 417 | static void cxlr_pmem_remove_resource(void *res) |
| @@ -422,7 +449,7 @@ static int cxl_pmem_region_probe(struct device *dev) · 31 unmodified lines |
| 422 | if (!cxl_nvb->nvdimm_bus) { | 449 | if (!cxl_nvb->nvdimm_bus) { |
| 423 | dev_dbg(dev, "nvdimm bus not found\n"); | 450 | dev_dbg(dev, "nvdimm bus not found\n"); |
| 424 | rc = -ENXIO; | 451 | rc = -ENXIO; |
| 425 | goto err; | 452 | goto out_nvb; |
| 426 | } | 453 | } |
| 427 | | 454 | |
| 428 | memset(&mappings, 0, sizeof(mappings)); | 455 | memset(&mappings, 0, sizeof(mappings)); |
| @@ -431,7 +458,7 @@ static int cxl_pmem_region_probe(struct device *dev) · 2 unmodified lines |
| 431 | res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); | 458 | res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); |
| 432 | if (!res) { | 459 | if (!res) { |
| 433 | rc = -ENOMEM; | 460 | rc = -ENOMEM; |
| 434 | goto err; | 461 | goto out_nvb; |
| 435 | } | 462 | } |
| 436 | | 463 | |
| 437 | res->name = "Persistent Memory"; | 464 | res->name = "Persistent Memory"; |
| @@ -442,11 +469,11 @@ static int cxl_pmem_region_probe(struct device *dev) · 4 unmodified lines |
| 442 | | 469 | |
| 443 | rc = insert_resource(&iomem_resource, res); | 470 | rc = insert_resource(&iomem_resource, res); |
| 444 | if (rc) | 471 | if (rc) |
| 445 | goto err; | 472 | goto out_nvb; |
| 446 | | 473 | |
| 447 | rc = devm_add_action_or_reset(dev, cxlr_pmem_remove_resource, res); | 474 | rc = devm_add_action_or_reset(dev, cxlr_pmem_remove_resource, res); |
| 448 | if (rc) | 475 | if (rc) |
| 449 | goto err; | 476 | goto out_nvb; |
| 450 | | 477 | |
| 451 | ndr_desc.res = res; | 478 | ndr_desc.res = res; |
| 452 | ndr_desc.provider_data = cxlr_pmem; | 479 | ndr_desc.provider_data = cxlr_pmem; |
| @@ -462,7 +489,7 @@ static int cxl_pmem_region_probe(struct device *dev) · 9 unmodified lines |
| 462 | nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL); | 489 | nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL); |
| 463 | if (!nd_set) { | 490 | if (!nd_set) { |
| 464 | rc = -ENOMEM; | 491 | rc = -ENOMEM; |
| 465 | goto err; | 492 | goto out_nvb; |
| 466 | } | 493 | } |
| 467 | | 494 | |
| 468 | ndr_desc.memregion = cxlr->id; | 495 | ndr_desc.memregion = cxlr->id; |
| @@ -472,9 +499,13 @@ static int cxl_pmem_region_probe(struct device *dev) · 3 unmodified lines |
| 472 | info = kmalloc_array(cxlr_pmem->nr_mappings, sizeof(*info), GFP_KERNEL); | 499 | info = kmalloc_array(cxlr_pmem->nr_mappings, sizeof(*info), GFP_KERNEL); |
| 473 | if (!info) { | 500 | if (!info) { |
| 474 | rc = -ENOMEM; | 501 | rc = -ENOMEM; |
| 475 | goto err; | 502 | goto out_nvb; |
| 476 | } | 503 | } |
| 477 | | 504 | |
| | 505 | rc = devm_add_action_or_reset(dev, release_mappings, cxlr_pmem); |
| | 506 | if (rc) |
| | 507 | goto out_nvd; |
| | 508 | |
| 478 | for (i = 0; i < cxlr_pmem->nr_mappings; i++) { | 509 | for (i = 0; i < cxlr_pmem->nr_mappings; i++) { |
| 479 | struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; | 510 | struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; |
| 480 | struct cxl_memdev *cxlmd = m->cxlmd; | 511 | struct cxl_memdev *cxlmd = m->cxlmd; |
| @@ -486,7 +517,7 @@ static int cxl_pmem_region_probe(struct device *dev) · 5 unmodified lines |
| 486 | dev_dbg(dev, "[%d]: %s: no cxl_nvdimm found\n", i, | 517 | dev_dbg(dev, "[%d]: %s: no cxl_nvdimm found\n", i, |
| 487 | dev_name(&cxlmd->dev)); | 518 | dev_name(&cxlmd->dev)); |
| 488 | rc = -ENODEV; | 519 | rc = -ENODEV; |
| 489 | goto err; | 520 | goto out_nvd; |
| 490 | } | 521 | } |
| 491 | | 522 | |
| 492 | /* safe to drop ref now with bridge lock held */ | 523 | /* safe to drop ref now with bridge lock held */ |
| @@ -498,10 +529,17 @@ static int cxl_pmem_region_probe(struct device *dev) · 5 unmodified lines |
| 498 | dev_dbg(dev, "[%d]: %s: no nvdimm found\n", i, | 529 | dev_dbg(dev, "[%d]: %s: no nvdimm found\n", i, |
| 499 | dev_name(&cxlmd->dev)); | 530 | dev_name(&cxlmd->dev)); |
| 500 | rc = -ENODEV; | 531 | rc = -ENODEV; |
| 501 | goto err; | 532 | goto out_nvd; |
| 502 | } | 533 | } |
| 503 | cxl_nvd->region = cxlr_pmem; | 534 | |
| 504 | get_device(&cxlr_pmem->dev); | 535 | /* |
| | 536 | * Pin the region per nvdimm device as those may be released |
| | 537 | * out-of-order with respect to the region, and a single nvdimm |
| | 538 | * maybe associated with multiple regions |
| | 539 | */ |
| | 540 | rc = cxl_nvdimm_add_region(cxl_nvd, cxlr_pmem); |
| | 541 | if (rc) |
| | 542 | goto out_nvd; |
| 505 | m->cxl_nvd = cxl_nvd; | 543 | m->cxl_nvd = cxl_nvd; |
| 506 | mappings[i] = (struct nd_mapping_desc) { | 544 | mappings[i] = (struct nd_mapping_desc) { |
| 507 | .nvdimm = nvdimm, | 545 | .nvdimm = nvdimm, |
| @@ -527,27 +565,18 @@ static int cxl_pmem_region_probe(struct device *dev) · 19 unmodified lines |
| 527 | nvdimm_pmem_region_create(cxl_nvb->nvdimm_bus, &ndr_desc); | 565 | nvdimm_pmem_region_create(cxl_nvb->nvdimm_bus, &ndr_desc); |
| 528 | if (!cxlr_pmem->nd_region) { | 566 | if (!cxlr_pmem->nd_region) { |
| 529 | rc = -ENOMEM; | 567 | rc = -ENOMEM; |
| 530 | goto err; | 568 | goto out_nvd; |
| 531 | } | 569 | } |
| 532 | | 570 | |
| 533 | rc = devm_add_action_or_reset(dev, unregister_nvdimm_region, | 571 | rc = devm_add_action_or_reset(dev, unregister_nvdimm_region, |
| 534 | cxlr_pmem->nd_region); | 572 | cxlr_pmem->nd_region); |
| 535 | out: | 573 | out_nvd: |
| 536 | kfree(info); | 574 | kfree(info); |
| | 575 | out_nvb: |
| 537 | device_unlock(&cxl_nvb->dev); | 576 | device_unlock(&cxl_nvb->dev); |
| 538 | put_device(&cxl_nvb->dev); | 577 | put_device(&cxl_nvb->dev); |
| 539 | | 578 | |
| 540 | return rc; | 579 | return rc; |
| 541 | | | |
| 542 | err: | | |
| 543 | dev_dbg(dev, "failed to create nvdimm region\n"); | | |
| 544 | for (i--; i >= 0; i--) { | | |
| 545 | nvdimm = mappings[i].nvdimm; | | |
| 546 | cxl_nvd = nvdimm_provider_data(nvdimm); | | |
| 547 | put_device(&cxl_nvd->region->dev); | | |
| 548 | cxl_nvd->region = NULL; | | |
| 549 | } | | |
| 550 | goto out; | | |
| 551 | } | 580 | } |
| 552 | | 581 | |
| 553 | static struct cxl_driver cxl_pmem_region_driver = { | 582 | static struct cxl_driver cxl_pmem_region_driver = { |