Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

dax/hmem: Fix singleton confusion between dax_hmem_work and hmem devices

dax_hmem (ab)uses a platform device to allow for a module to autoload in
the presence of "Soft Reserved" resources. The dax_hmem driver had no
dependencies on the "hmem_platform" device being a singleton until the
recent "dax_hmem vs dax_cxl" takeover solution.

Replace the layering violation of dax_hmem_work assuming that there will
never be more than one "hmem_platform" device associated with a global work
item with a dax_hmem local workqueue that can theoretically support any
number of hmem_platform devices.

Fixup the reference counting to only pin the device while it is live in the
queue.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Link: https://patch.msgid.link/20260327052821.440749-7-dan.j.williams@intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>

authored by

Dan Williams and committed by
Dave Jiang
f8dc1bde 3cba30ee

+85 -66
+14 -1
drivers/dax/bus.h
··· 3 3 #ifndef __DAX_BUS_H__ 4 4 #define __DAX_BUS_H__ 5 5 #include <linux/device.h> 6 + #include <linux/platform_device.h> 6 7 #include <linux/range.h> 8 + #include <linux/workqueue.h> 7 9 8 10 struct dev_dax; 9 11 struct resource; ··· 51 49 void kill_dev_dax(struct dev_dax *dev_dax); 52 50 bool static_dev_dax(struct dev_dax *dev_dax); 53 51 52 + struct hmem_platform_device { 53 + struct platform_device pdev; 54 + struct work_struct work; 55 + bool did_probe; 56 + }; 57 + 58 + static inline struct hmem_platform_device * 59 + to_hmem_platform_device(struct platform_device *pdev) 60 + { 61 + return container_of(pdev, struct hmem_platform_device, pdev); 62 + } 63 + 54 64 #if IS_ENABLED(CONFIG_DEV_DAX_HMEM) 55 - extern bool dax_hmem_initial_probe; 56 65 void dax_hmem_flush_work(void); 57 66 #else 58 67 static inline void dax_hmem_flush_work(void) { }
+17 -11
drivers/dax/hmem/device.c
··· 4 4 #include <linux/module.h> 5 5 #include <linux/dax.h> 6 6 #include <linux/mm.h> 7 + #include "../bus.h" 7 8 8 9 static bool nohmem; 9 10 module_param_named(disable, nohmem, bool, 0444); 10 - 11 - bool dax_hmem_initial_probe; 12 - EXPORT_SYMBOL_FOR_MODULES(dax_hmem_initial_probe, "dax_hmem"); 13 11 14 12 static bool platform_initialized; 15 13 static DEFINE_MUTEX(hmem_resource_lock); ··· 34 36 } 35 37 EXPORT_SYMBOL_GPL(walk_hmem_resources); 36 38 39 + static void hmem_work(struct work_struct *work) 40 + { 41 + /* place holder until dax_hmem driver attaches */ 42 + } 43 + 44 + static struct hmem_platform_device hmem_platform = { 45 + .pdev = { 46 + .name = "hmem_platform", 47 + .id = 0, 48 + }, 49 + .work = __WORK_INITIALIZER(hmem_platform.work, hmem_work), 50 + }; 51 + 37 52 static void __hmem_register_resource(int target_nid, struct resource *res) 38 53 { 39 - struct platform_device *pdev; 40 54 struct resource *new; 41 55 int rc; 42 56 ··· 64 54 if (platform_initialized) 65 55 return; 66 56 67 - pdev = platform_device_alloc("hmem_platform", 0); 68 - if (!pdev) { 57 + rc = platform_device_register(&hmem_platform.pdev); 58 + if (rc) { 69 59 pr_err_once("failed to register device-dax hmem_platform device\n"); 70 60 return; 71 61 } 72 62 73 - rc = platform_device_add(pdev); 74 - if (rc) 75 - platform_device_put(pdev); 76 - else 77 - platform_initialized = true; 63 + platform_initialized = true; 78 64 } 79 65 80 66 void hmem_register_resource(int target_nid, struct resource *res)
+54 -54
drivers/dax/hmem/hmem.c
··· 59 59 platform_device_unregister(pdev); 60 60 } 61 61 62 - struct dax_defer_work { 63 - struct platform_device *pdev; 64 - struct work_struct work; 65 - }; 66 - 67 - static void process_defer_work(struct work_struct *w); 68 - 69 - static struct dax_defer_work dax_hmem_work = { 70 - .work = __WORK_INITIALIZER(dax_hmem_work.work, process_defer_work), 71 - }; 62 + static struct workqueue_struct *dax_hmem_wq; 72 63 73 64 void dax_hmem_flush_work(void) 74 65 { 75 - flush_work(&dax_hmem_work.work); 66 + flush_workqueue(dax_hmem_wq); 76 67 } 77 68 EXPORT_SYMBOL_FOR_MODULES(dax_hmem_flush_work, "dax_cxl"); 78 69 ··· 125 134 return rc; 126 135 } 127 136 128 - static int hmem_register_device(struct device *host, int target_nid, 129 - const struct resource *res) 130 - { 131 - if (IS_ENABLED(CONFIG_DEV_DAX_CXL) && 132 - region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 133 - IORES_DESC_CXL) != REGION_DISJOINT) { 134 - if (!dax_hmem_initial_probe) { 135 - dev_dbg(host, "await CXL initial probe: %pr\n", res); 136 - queue_work(system_long_wq, &dax_hmem_work.work); 137 - return 0; 138 - } 139 - dev_dbg(host, "deferring range to CXL: %pr\n", res); 140 - return 0; 141 - } 142 - 143 - return __hmem_register_device(host, target_nid, res); 144 - } 145 - 146 137 static int hmem_register_cxl_device(struct device *host, int target_nid, 147 138 const struct resource *res) 148 139 { ··· 143 170 144 171 static void process_defer_work(struct work_struct *w) 145 172 { 146 - struct dax_defer_work *work = container_of(w, typeof(*work), work); 147 - struct platform_device *pdev; 148 - 149 - if (!work->pdev) 150 - return; 151 - 152 - pdev = work->pdev; 173 + struct hmem_platform_device *hpdev = container_of(w, typeof(*hpdev), work); 174 + struct device *dev = &hpdev->pdev.dev; 153 175 154 176 /* Relies on cxl_acpi and cxl_pci having had a chance to load */ 155 177 wait_for_device_probe(); 156 178 157 - guard(device)(&pdev->dev); 158 - if (!pdev->dev.driver) 159 - return; 179 + guard(device)(dev); 180 + if (!dev->driver) 181 + goto out; 160 182 161 - if (!dax_hmem_initial_probe) { 162 - dax_hmem_initial_probe = true; 163 - walk_hmem_resources(&pdev->dev, hmem_register_cxl_device); 183 + if (!hpdev->did_probe) { 184 + hpdev->did_probe = true; 185 + walk_hmem_resources(dev, hmem_register_cxl_device); 164 186 } 187 + out: 188 + put_device(dev); 189 + } 190 + 191 + static int hmem_register_device(struct device *host, int target_nid, 192 + const struct resource *res) 193 + { 194 + struct platform_device *pdev = to_platform_device(host); 195 + struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev); 196 + 197 + if (IS_ENABLED(CONFIG_DEV_DAX_CXL) && 198 + region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 199 + IORES_DESC_CXL) != REGION_DISJOINT) { 200 + if (!hpdev->did_probe) { 201 + dev_dbg(host, "await CXL initial probe: %pr\n", res); 202 + hpdev->work.func = process_defer_work; 203 + get_device(host); 204 + if (!queue_work(dax_hmem_wq, &hpdev->work)) 205 + put_device(host); 206 + return 0; 207 + } 208 + dev_dbg(host, "deferring range to CXL: %pr\n", res); 209 + return 0; 210 + } 211 + 212 + return __hmem_register_device(host, target_nid, res); 165 213 } 166 214 167 215 static int dax_hmem_platform_probe(struct platform_device *pdev) 168 216 { 169 - if (work_pending(&dax_hmem_work.work)) 170 - return -EBUSY; 217 + struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev); 171 218 172 - if (!dax_hmem_work.pdev) 173 - dax_hmem_work.pdev = 174 - to_platform_device(get_device(&pdev->dev)); 219 + /* queue is only flushed on module unload, fail rebind with pending work */ 220 + if (work_pending(&hpdev->work)) 221 + return -EBUSY; 175 222 176 223 return walk_hmem_resources(&pdev->dev, hmem_register_device); 177 224 } ··· 217 224 request_module("cxl_pci"); 218 225 } 219 226 227 + dax_hmem_wq = alloc_ordered_workqueue("dax_hmem_wq", 0); 228 + if (!dax_hmem_wq) 229 + return -ENOMEM; 230 + 220 231 rc = platform_driver_register(&dax_hmem_platform_driver); 221 232 if (rc) 222 - return rc; 233 + goto err_platform_driver; 223 234 224 235 rc = platform_driver_register(&dax_hmem_driver); 225 236 if (rc) 226 - platform_driver_unregister(&dax_hmem_platform_driver); 237 + goto err_driver; 238 + 239 + return 0; 240 + 241 + err_driver: 242 + platform_driver_unregister(&dax_hmem_platform_driver); 243 + err_platform_driver: 244 + destroy_workqueue(dax_hmem_wq); 227 245 228 246 return rc; 229 247 } 230 248 231 249 static __exit void dax_hmem_exit(void) 232 250 { 233 - if (dax_hmem_work.pdev) { 234 - flush_work(&dax_hmem_work.work); 235 - put_device(&dax_hmem_work.pdev->dev); 236 - } 237 - 238 251 platform_driver_unregister(&dax_hmem_driver); 239 252 platform_driver_unregister(&dax_hmem_platform_driver); 253 + destroy_workqueue(dax_hmem_wq); 240 254 } 241 255 242 256 module_init(dax_hmem_init);