From: Ira Weiny <[email protected]> Implement the release path that mirrors the add path: when the device asks for capacity back, the dax layer tears down the per-extent resources for the whole tag group atomically via dax_region_rm_resources().
If any extent in the group is still mapped by a dev_dax, the release is refused with -EBUSY and no state changes; the cxl side then leaves the tag group intact and the device retries. Based on an original patch by Navneet Singh. Signed-off-by: Ira Weiny <[email protected]> Signed-off-by: Anisa Su <[email protected]> Reviewed-by: Dave Jiang <[email protected]> --- drivers/cxl/core/extent.c | 12 +++++++++++ drivers/dax/bus.c | 43 +++++++++++++++++++++++++++++++++++++++ drivers/dax/cxl.c | 39 +++++++++++++++++++++++++---------- drivers/dax/dax-private.h | 8 ++++++-- 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/drivers/cxl/core/extent.c b/drivers/cxl/core/extent.c index 59db1878b5e2..7009ac6a51b4 100644 --- a/drivers/cxl/core/extent.c +++ b/drivers/cxl/core/extent.c @@ -627,6 +627,18 @@ int cxl_rm_extent(struct cxl_memdev_state *mds, struct cxl_extent *extent) if (rc) return rc; + rc = cxlr_notify_extent(cxlr, DCD_RELEASE_CAPACITY, group); + if (rc) { + /* + * dax layer refused (-EBUSY) or failed (-ENOMEM, etc.). Do + * not proceed to tear down the tag group — leave its + * dax_resources alive so we do not free them out from under + * live dev_dax ranges. The device will retry the release. + */ + return 0; + } + + /* Release the entire tag group */ rm_tag_group(group); return 0; } diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index 9b5c03616b83..95683dc8fcd0 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -282,6 +282,14 @@ static int __dax_region_rm_resource(struct dax_region *dax_region, return 0; } +int dax_region_rm_resource(struct dax_region *dax_region, + struct device *dev) +{ + guard(rwsem_write)(&dax_region_rwsem); + return __dax_region_rm_resource(dax_region, dev); +} +EXPORT_SYMBOL_GPL(dax_region_rm_resource); + /** * dax_region_add_resources - atomically add a set of dax_resources. * @@ -314,6 +322,41 @@ int dax_region_add_resources(struct dax_region *dax_region, } EXPORT_SYMBOL_GPL(dax_region_add_resources); +/** + * dax_region_rm_resources - atomically remove a set of dax_resources. + * + * Walk @devs twice under dax_region_rwsem. First pass refuses the + * operation if any member's use_cnt is non-zero; second pass releases + * each. This gives refuse-all-or-none semantics across the set, which + * a tag group's atomic release relies on. Devices with no + * dax_resource attached are silently skipped. + */ +int dax_region_rm_resources(struct dax_region *dax_region, + struct device * const *devs, unsigned int n) +{ + unsigned int i; + + guard(rwsem_write)(&dax_region_rwsem); + + for (i = 0; i < n; i++) { + struct dax_resource *r = dev_get_drvdata(devs[i]); + + if (r && r->use_cnt) + return -EBUSY; + } + + for (i = 0; i < n; i++) { + struct dax_resource *r = dev_get_drvdata(devs[i]); + + if (!r) + continue; + __dax_release_resource(r); + dev_set_drvdata(devs[i], NULL); + } + return 0; +} +EXPORT_SYMBOL_GPL(dax_region_rm_resources); + bool static_dev_dax(struct dev_dax *dev_dax) { return is_static(dev_dax->region); diff --git a/drivers/dax/cxl.c b/drivers/dax/cxl.c index 5d33be342d42..d885b6e698ef 100644 --- a/drivers/dax/cxl.c +++ b/drivers/dax/cxl.c @@ -40,13 +40,33 @@ static int cxl_dax_group_add(struct dax_region *dax_region, return rc; } -/* - * RELEASE is still a stub here — the atomic dax_region_rm_resources API - * and its wire-up land in the next commit. An incoming RELEASE returns - * success and the cxl side proceeds to rm_tag_group(), which device- - * unregisters each dc_extent; the devm action armed by - * dax_region_add_resource() then tears down each dax_resource. - */ +static int cxl_dax_group_rm(struct dax_region *dax_region, + struct cxl_dc_tag_group *group) +{ + struct dc_extent *dc_extent; + struct device **devs; + unsigned long index; + unsigned int n = 0; + int rc; + + if (!group->nr_extents) + return 0; + + devs = kmalloc_array(group->nr_extents, sizeof(*devs), GFP_KERNEL); + if (!devs) + return -ENOMEM; + + xa_for_each(&group->dc_extents, index, dc_extent) { + if (n == group->nr_extents) + break; + devs[n++] = &dc_extent->dev; + } + + rc = dax_region_rm_resources(dax_region, devs, n); + kfree(devs); + return rc; +} + static int cxl_dax_region_notify(struct device *dev, struct cxl_notify_data *notify_data) { @@ -58,10 +78,7 @@ static int cxl_dax_region_notify(struct device *dev, case DCD_ADD_CAPACITY: return cxl_dax_group_add(dax_region, group); case DCD_RELEASE_CAPACITY: - dev_dbg(&cxlr_dax->dev, - "DCD RELEASE notify (tag %pUb): no-op (stub)\n", - &group->uuid); - return 0; + return cxl_dax_group_rm(dax_region, group); case DCD_FORCED_CAPACITY_RELEASE: default: dev_err(&cxlr_dax->dev, "Unknown DC event %d\n", diff --git a/drivers/dax/dax-private.h b/drivers/dax/dax-private.h index 8d98fc9adb4b..59ba929e14fd 100644 --- a/drivers/dax/dax-private.h +++ b/drivers/dax/dax-private.h @@ -146,13 +146,17 @@ struct dax_resource { }; /* - * Similar to run_dax() dax_region_add_resource() is exported but is not - * intended to be a generic operation outside the dax subsystem. It is only + * Similar to run_dax() dax_region_{add,rm}_resource() are exported but are not + * intended to be generic operations outside the dax subsystem. They are only * generic between the dax layer and the dax drivers. */ int dax_region_add_resource(struct dax_region *dax_region, struct device *dev, resource_size_t start, resource_size_t length, const uuid_t *tag, u16 seq_num); +int dax_region_rm_resource(struct dax_region *dax_region, + struct device *dev); +int dax_region_rm_resources(struct dax_region *dax_region, + struct device * const *devs, unsigned int n); /* One resource to add as part of an atomic dax_region_add_resources() set. */ struct dax_resource_spec { -- 2.43.0

