@@ -30,17 +30,20 @@ static void unregister_nvdimm(void *nvdimm)
3030 struct cxl_nvdimm * cxl_nvd = nvdimm_provider_data (nvdimm );
3131 struct cxl_nvdimm_bridge * cxl_nvb = cxl_nvd -> bridge ;
3232 struct cxl_pmem_region * cxlr_pmem ;
33+ unsigned long index ;
3334
3435 device_lock (& cxl_nvb -> dev );
35- cxlr_pmem = cxl_nvd -> region ;
3636 dev_set_drvdata (& cxl_nvd -> dev , NULL );
37- cxl_nvd -> region = NULL ;
38- device_unlock (& cxl_nvb -> dev );
37+ xa_for_each (& cxl_nvd -> pmem_regions , index , cxlr_pmem ) {
38+ get_device (& cxlr_pmem -> dev );
39+ device_unlock (& cxl_nvb -> dev );
3940
40- if (cxlr_pmem ) {
4141 device_release_driver (& cxlr_pmem -> dev );
4242 put_device (& cxlr_pmem -> dev );
43+
44+ device_lock (& cxl_nvb -> dev );
4345 }
46+ device_unlock (& cxl_nvb -> dev );
4447
4548 nvdimm_delete (nvdimm );
4649 cxl_nvd -> bridge = NULL ;
@@ -366,25 +369,49 @@ static int match_cxl_nvdimm(struct device *dev, void *data)
366369
367370static void unregister_nvdimm_region (void * nd_region )
368371{
369- struct cxl_nvdimm_bridge * cxl_nvb ;
370- struct cxl_pmem_region * cxlr_pmem ;
372+ nvdimm_region_delete (nd_region );
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+ {
371403 int i ;
404+ struct cxl_pmem_region * cxlr_pmem = data ;
405+ struct cxl_nvdimm_bridge * cxl_nvb = cxlr_pmem -> bridge ;
372406
373- cxlr_pmem = nd_region_provider_data (nd_region );
374- cxl_nvb = cxlr_pmem -> bridge ;
375407 device_lock (& cxl_nvb -> dev );
376408 for (i = 0 ; i < cxlr_pmem -> nr_mappings ; i ++ ) {
377409 struct cxl_pmem_region_mapping * m = & cxlr_pmem -> mapping [i ];
378410 struct cxl_nvdimm * cxl_nvd = m -> cxl_nvd ;
379411
380- if (cxl_nvd -> region ) {
381- put_device (& cxlr_pmem -> dev );
382- cxl_nvd -> region = NULL ;
383- }
412+ cxl_nvdimm_del_region (cxl_nvd , cxlr_pmem );
384413 }
385414 device_unlock (& cxl_nvb -> dev );
386-
387- nvdimm_region_delete (nd_region );
388415}
389416
390417static void cxlr_pmem_remove_resource (void * res )
@@ -422,7 +449,7 @@ static int cxl_pmem_region_probe(struct device *dev)
422449 if (!cxl_nvb -> nvdimm_bus ) {
423450 dev_dbg (dev , "nvdimm bus not found\n" );
424451 rc = - ENXIO ;
425- goto err ;
452+ goto out_nvb ;
426453 }
427454
428455 memset (& mappings , 0 , sizeof (mappings ));
@@ -431,7 +458,7 @@ static int cxl_pmem_region_probe(struct device *dev)
431458 res = devm_kzalloc (dev , sizeof (* res ), GFP_KERNEL );
432459 if (!res ) {
433460 rc = - ENOMEM ;
434- goto err ;
461+ goto out_nvb ;
435462 }
436463
437464 res -> name = "Persistent Memory" ;
@@ -442,11 +469,11 @@ static int cxl_pmem_region_probe(struct device *dev)
442469
443470 rc = insert_resource (& iomem_resource , res );
444471 if (rc )
445- goto err ;
472+ goto out_nvb ;
446473
447474 rc = devm_add_action_or_reset (dev , cxlr_pmem_remove_resource , res );
448475 if (rc )
449- goto err ;
476+ goto out_nvb ;
450477
451478 ndr_desc .res = res ;
452479 ndr_desc .provider_data = cxlr_pmem ;
@@ -462,7 +489,7 @@ static int cxl_pmem_region_probe(struct device *dev)
462489 nd_set = devm_kzalloc (dev , sizeof (* nd_set ), GFP_KERNEL );
463490 if (!nd_set ) {
464491 rc = - ENOMEM ;
465- goto err ;
492+ goto out_nvb ;
466493 }
467494
468495 ndr_desc .memregion = cxlr -> id ;
@@ -472,9 +499,13 @@ static int cxl_pmem_region_probe(struct device *dev)
472499 info = kmalloc_array (cxlr_pmem -> nr_mappings , sizeof (* info ), GFP_KERNEL );
473500 if (!info ) {
474501 rc = - ENOMEM ;
475- goto err ;
502+ goto out_nvb ;
476503 }
477504
505+ rc = devm_add_action_or_reset (dev , release_mappings , cxlr_pmem );
506+ if (rc )
507+ goto out_nvd ;
508+
478509 for (i = 0 ; i < cxlr_pmem -> nr_mappings ; i ++ ) {
479510 struct cxl_pmem_region_mapping * m = & cxlr_pmem -> mapping [i ];
480511 struct cxl_memdev * cxlmd = m -> cxlmd ;
@@ -486,7 +517,7 @@ static int cxl_pmem_region_probe(struct device *dev)
486517 dev_dbg (dev , "[%d]: %s: no cxl_nvdimm found\n" , i ,
487518 dev_name (& cxlmd -> dev ));
488519 rc = - ENODEV ;
489- goto err ;
520+ goto out_nvd ;
490521 }
491522
492523 /* safe to drop ref now with bridge lock held */
@@ -498,10 +529,17 @@ static int cxl_pmem_region_probe(struct device *dev)
498529 dev_dbg (dev , "[%d]: %s: no nvdimm found\n" , i ,
499530 dev_name (& cxlmd -> dev ));
500531 rc = - ENODEV ;
501- goto err ;
532+ goto out_nvd ;
502533 }
503- cxl_nvd -> region = cxlr_pmem ;
504- get_device (& cxlr_pmem -> dev );
534+
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 ;
505543 m -> cxl_nvd = cxl_nvd ;
506544 mappings [i ] = (struct nd_mapping_desc ) {
507545 .nvdimm = nvdimm ,
@@ -527,27 +565,18 @@ static int cxl_pmem_region_probe(struct device *dev)
527565 nvdimm_pmem_region_create (cxl_nvb -> nvdimm_bus , & ndr_desc );
528566 if (!cxlr_pmem -> nd_region ) {
529567 rc = - ENOMEM ;
530- goto err ;
568+ goto out_nvd ;
531569 }
532570
533571 rc = devm_add_action_or_reset (dev , unregister_nvdimm_region ,
534572 cxlr_pmem -> nd_region );
535- out :
573+ out_nvd :
536574 kfree (info );
575+ out_nvb :
537576 device_unlock (& cxl_nvb -> dev );
538577 put_device (& cxl_nvb -> dev );
539578
540579 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 ;
551580}
552581
553582static struct cxl_driver cxl_pmem_region_driver = {
0 commit comments