hmem.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/platform_device.h>
  3. #include <linux/memregion.h>
  4. #include <linux/module.h>
  5. #include <linux/pfn_t.h>
  6. #include <linux/dax.h>
  7. #include "../bus.h"
  8. static bool region_idle;
  9. module_param_named(region_idle, region_idle, bool, 0644);
  10. static int dax_hmem_probe(struct platform_device *pdev)
  11. {
  12. unsigned long flags = IORESOURCE_DAX_KMEM;
  13. struct device *dev = &pdev->dev;
  14. struct dax_region *dax_region;
  15. struct memregion_info *mri;
  16. struct dev_dax_data data;
  17. /*
  18. * @region_idle == true indicates that an administrative agent
  19. * wants to manipulate the range partitioning before the devices
  20. * are created, so do not send them to the dax_kmem driver by
  21. * default.
  22. */
  23. if (region_idle)
  24. flags = 0;
  25. mri = dev->platform_data;
  26. dax_region = alloc_dax_region(dev, pdev->id, &mri->range,
  27. mri->target_node, PMD_SIZE, flags);
  28. if (!dax_region)
  29. return -ENOMEM;
  30. data = (struct dev_dax_data) {
  31. .dax_region = dax_region,
  32. .id = -1,
  33. .size = region_idle ? 0 : range_len(&mri->range),
  34. .memmap_on_memory = false,
  35. };
  36. return PTR_ERR_OR_ZERO(devm_create_dev_dax(&data));
  37. }
  38. static struct platform_driver dax_hmem_driver = {
  39. .probe = dax_hmem_probe,
  40. .driver = {
  41. .name = "hmem",
  42. },
  43. };
  44. static void release_memregion(void *data)
  45. {
  46. memregion_free((long) data);
  47. }
  48. static void release_hmem(void *pdev)
  49. {
  50. platform_device_unregister(pdev);
  51. }
  52. static int hmem_register_device(struct device *host, int target_nid,
  53. const struct resource *res)
  54. {
  55. struct platform_device *pdev;
  56. struct memregion_info info;
  57. long id;
  58. int rc;
  59. if (IS_ENABLED(CONFIG_CXL_REGION) &&
  60. region_intersects(res->start, resource_size(res), IORESOURCE_MEM,
  61. IORES_DESC_CXL) != REGION_DISJOINT) {
  62. dev_dbg(host, "deferring range to CXL: %pr\n", res);
  63. return 0;
  64. }
  65. rc = region_intersects(res->start, resource_size(res), IORESOURCE_MEM,
  66. IORES_DESC_SOFT_RESERVED);
  67. if (rc != REGION_INTERSECTS)
  68. return 0;
  69. id = memregion_alloc(GFP_KERNEL);
  70. if (id < 0) {
  71. dev_err(host, "memregion allocation failure for %pr\n", res);
  72. return -ENOMEM;
  73. }
  74. rc = devm_add_action_or_reset(host, release_memregion, (void *) id);
  75. if (rc)
  76. return rc;
  77. pdev = platform_device_alloc("hmem", id);
  78. if (!pdev) {
  79. dev_err(host, "device allocation failure for %pr\n", res);
  80. return -ENOMEM;
  81. }
  82. pdev->dev.numa_node = numa_map_to_online_node(target_nid);
  83. info = (struct memregion_info) {
  84. .target_node = target_nid,
  85. .range = {
  86. .start = res->start,
  87. .end = res->end,
  88. },
  89. };
  90. rc = platform_device_add_data(pdev, &info, sizeof(info));
  91. if (rc < 0) {
  92. dev_err(host, "memregion_info allocation failure for %pr\n",
  93. res);
  94. goto out_put;
  95. }
  96. rc = platform_device_add(pdev);
  97. if (rc < 0) {
  98. dev_err(host, "%s add failed for %pr\n", dev_name(&pdev->dev),
  99. res);
  100. goto out_put;
  101. }
  102. return devm_add_action_or_reset(host, release_hmem, pdev);
  103. out_put:
  104. platform_device_put(pdev);
  105. return rc;
  106. }
  107. static int dax_hmem_platform_probe(struct platform_device *pdev)
  108. {
  109. return walk_hmem_resources(&pdev->dev, hmem_register_device);
  110. }
  111. static struct platform_driver dax_hmem_platform_driver = {
  112. .probe = dax_hmem_platform_probe,
  113. .driver = {
  114. .name = "hmem_platform",
  115. },
  116. };
  117. static __init int dax_hmem_init(void)
  118. {
  119. int rc;
  120. rc = platform_driver_register(&dax_hmem_platform_driver);
  121. if (rc)
  122. return rc;
  123. rc = platform_driver_register(&dax_hmem_driver);
  124. if (rc)
  125. platform_driver_unregister(&dax_hmem_platform_driver);
  126. return rc;
  127. }
  128. static __exit void dax_hmem_exit(void)
  129. {
  130. platform_driver_unregister(&dax_hmem_driver);
  131. platform_driver_unregister(&dax_hmem_platform_driver);
  132. }
  133. module_init(dax_hmem_init);
  134. module_exit(dax_hmem_exit);
  135. /* Allow for CXL to define its own dax regions */
  136. #if IS_ENABLED(CONFIG_CXL_REGION)
  137. #if IS_MODULE(CONFIG_CXL_ACPI)
  138. MODULE_SOFTDEP("pre: cxl_acpi");
  139. #endif
  140. #endif
  141. MODULE_ALIAS("platform:hmem*");
  142. MODULE_ALIAS("platform:hmem_platform*");
  143. MODULE_DESCRIPTION("HMEM DAX: direct access to 'specific purpose' memory");
  144. MODULE_LICENSE("GPL v2");
  145. MODULE_AUTHOR("Intel Corporation");