mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
cxl/region: Consolidate cxl_decoder_kill_region() and cxl_region_detach()
Both detach_target() and cxld_unregister() want to tear down a cxl_region when an endpoint decoder is either detached or destroyed. When a region is to be destroyed cxl_region_detach() releases cxl_region_rwsem unbinds the cxl_region driver and re-acquires the rwsem. This "reverse" locking pattern is difficult to reason about, not amenable to scope-based cleanup, and the minor differences in the calling context of detach_target() and cxld_unregister() currently results in the cxl_decoder_kill_region() wrapper. Introduce cxl_decoder_detach() to wrap a core __cxl_decoder_detach() that serves both cases. I.e. either detaching a known position in a region (interruptible), or detaching an endpoint decoder if it is found to be a member of a region (uninterruptible). Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Jonathan Cameron <jonathan.cameron@huawei.com> Cc: Dave Jiang <dave.jiang@intel.com> Cc: Alison Schofield <alison.schofield@intel.com> Cc: Vishal Verma <vishal.l.verma@intel.com> Cc: Ira Weiny <ira.weiny@intel.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Reviewed-by: Fabio M. De Francesco <fabio.m.de.francesco@linux.intel.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Reviewed-by: Davidlohr Bueso <dave@stgolabs.net> Link: https://patch.msgid.link/20250711234932.671292-8-dan.j.williams@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com>
This commit is contained in:
parent
695d9455af
commit
b3a8822551
3 changed files with 75 additions and 52 deletions
|
@ -12,6 +12,11 @@ extern const struct device_type cxl_pmu_type;
|
||||||
|
|
||||||
extern struct attribute_group cxl_base_attribute_group;
|
extern struct attribute_group cxl_base_attribute_group;
|
||||||
|
|
||||||
|
enum cxl_detach_mode {
|
||||||
|
DETACH_ONLY,
|
||||||
|
DETACH_INVALIDATE,
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_CXL_REGION
|
#ifdef CONFIG_CXL_REGION
|
||||||
extern struct device_attribute dev_attr_create_pmem_region;
|
extern struct device_attribute dev_attr_create_pmem_region;
|
||||||
extern struct device_attribute dev_attr_create_ram_region;
|
extern struct device_attribute dev_attr_create_ram_region;
|
||||||
|
@ -20,7 +25,11 @@ extern struct device_attribute dev_attr_region;
|
||||||
extern const struct device_type cxl_pmem_region_type;
|
extern const struct device_type cxl_pmem_region_type;
|
||||||
extern const struct device_type cxl_dax_region_type;
|
extern const struct device_type cxl_dax_region_type;
|
||||||
extern const struct device_type cxl_region_type;
|
extern const struct device_type cxl_region_type;
|
||||||
void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
|
|
||||||
|
int cxl_decoder_detach(struct cxl_region *cxlr,
|
||||||
|
struct cxl_endpoint_decoder *cxled, int pos,
|
||||||
|
enum cxl_detach_mode mode);
|
||||||
|
|
||||||
#define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
|
#define CXL_REGION_ATTR(x) (&dev_attr_##x.attr)
|
||||||
#define CXL_REGION_TYPE(x) (&cxl_region_type)
|
#define CXL_REGION_TYPE(x) (&cxl_region_type)
|
||||||
#define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
|
#define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr),
|
||||||
|
@ -48,7 +57,9 @@ static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
|
static inline int cxl_decoder_detach(struct cxl_region *cxlr,
|
||||||
|
struct cxl_endpoint_decoder *cxled,
|
||||||
|
int pos, enum cxl_detach_mode mode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
static inline int cxl_region_init(void)
|
static inline int cxl_region_init(void)
|
||||||
|
|
|
@ -2001,12 +2001,9 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, "CXL");
|
||||||
|
|
||||||
static void cxld_unregister(void *dev)
|
static void cxld_unregister(void *dev)
|
||||||
{
|
{
|
||||||
struct cxl_endpoint_decoder *cxled;
|
if (is_endpoint_decoder(dev))
|
||||||
|
cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1,
|
||||||
if (is_endpoint_decoder(dev)) {
|
DETACH_INVALIDATE);
|
||||||
cxled = to_cxl_endpoint_decoder(dev);
|
|
||||||
cxl_decoder_kill_region(cxled);
|
|
||||||
}
|
|
||||||
|
|
||||||
device_unregister(dev);
|
device_unregister(dev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2135,27 +2135,43 @@ static int cxl_region_attach(struct cxl_region *cxlr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
|
static struct cxl_region *
|
||||||
|
__cxl_decoder_detach(struct cxl_region *cxlr,
|
||||||
|
struct cxl_endpoint_decoder *cxled, int pos,
|
||||||
|
enum cxl_detach_mode mode)
|
||||||
{
|
{
|
||||||
struct cxl_port *iter, *ep_port = cxled_to_port(cxled);
|
|
||||||
struct cxl_region *cxlr = cxled->cxld.region;
|
|
||||||
struct cxl_region_params *p;
|
struct cxl_region_params *p;
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
lockdep_assert_held_write(&cxl_region_rwsem);
|
lockdep_assert_held_write(&cxl_region_rwsem);
|
||||||
|
|
||||||
if (!cxlr)
|
if (!cxled) {
|
||||||
return 0;
|
p = &cxlr->params;
|
||||||
|
|
||||||
p = &cxlr->params;
|
if (pos >= p->interleave_ways) {
|
||||||
get_device(&cxlr->dev);
|
dev_dbg(&cxlr->dev, "position %d out of range %d\n",
|
||||||
|
pos, p->interleave_ways);
|
||||||
|
return ERR_PTR(-ENXIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p->targets[pos])
|
||||||
|
return NULL;
|
||||||
|
cxled = p->targets[pos];
|
||||||
|
} else {
|
||||||
|
cxlr = cxled->cxld.region;
|
||||||
|
if (!cxlr)
|
||||||
|
return NULL;
|
||||||
|
p = &cxlr->params;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == DETACH_INVALIDATE)
|
||||||
|
cxled->part = -1;
|
||||||
|
|
||||||
if (p->state > CXL_CONFIG_ACTIVE) {
|
if (p->state > CXL_CONFIG_ACTIVE) {
|
||||||
cxl_region_decode_reset(cxlr, p->interleave_ways);
|
cxl_region_decode_reset(cxlr, p->interleave_ways);
|
||||||
p->state = CXL_CONFIG_ACTIVE;
|
p->state = CXL_CONFIG_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (iter = ep_port; !is_cxl_root(iter);
|
for (struct cxl_port *iter = cxled_to_port(cxled); !is_cxl_root(iter);
|
||||||
iter = to_cxl_port(iter->dev.parent))
|
iter = to_cxl_port(iter->dev.parent))
|
||||||
cxl_port_detach_region(iter, cxlr, cxled);
|
cxl_port_detach_region(iter, cxlr, cxled);
|
||||||
|
|
||||||
|
@ -2166,7 +2182,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
|
||||||
dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n",
|
dev_WARN_ONCE(&cxlr->dev, 1, "expected %s:%s at position %d\n",
|
||||||
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
|
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev),
|
||||||
cxled->pos);
|
cxled->pos);
|
||||||
goto out;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->state == CXL_CONFIG_ACTIVE) {
|
if (p->state == CXL_CONFIG_ACTIVE) {
|
||||||
|
@ -2180,21 +2196,42 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
|
||||||
.end = -1,
|
.end = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* notify the region driver that one of its targets has departed */
|
get_device(&cxlr->dev);
|
||||||
up_write(&cxl_region_rwsem);
|
return cxlr;
|
||||||
device_release_driver(&cxlr->dev);
|
|
||||||
down_write(&cxl_region_rwsem);
|
|
||||||
out:
|
|
||||||
put_device(&cxlr->dev);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
|
/*
|
||||||
|
* Cleanup a decoder's interest in a region. There are 2 cases to
|
||||||
|
* handle, removing an unknown @cxled from a known position in a region
|
||||||
|
* (detach_target()) or removing a known @cxled from an unknown @cxlr
|
||||||
|
* (cxld_unregister())
|
||||||
|
*
|
||||||
|
* When the detachment finds a region release the region driver.
|
||||||
|
*/
|
||||||
|
int cxl_decoder_detach(struct cxl_region *cxlr,
|
||||||
|
struct cxl_endpoint_decoder *cxled, int pos,
|
||||||
|
enum cxl_detach_mode mode)
|
||||||
{
|
{
|
||||||
down_write(&cxl_region_rwsem);
|
struct cxl_region *detach;
|
||||||
cxled->part = -1;
|
|
||||||
cxl_region_detach(cxled);
|
/* when the decoder is being destroyed lock unconditionally */
|
||||||
|
if (mode == DETACH_INVALIDATE)
|
||||||
|
down_write(&cxl_region_rwsem);
|
||||||
|
else {
|
||||||
|
int rc = down_write_killable(&cxl_region_rwsem);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
detach = __cxl_decoder_detach(cxlr, cxled, pos, mode);
|
||||||
up_write(&cxl_region_rwsem);
|
up_write(&cxl_region_rwsem);
|
||||||
|
|
||||||
|
if (detach) {
|
||||||
|
device_release_driver(&detach->dev);
|
||||||
|
put_device(&detach->dev);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int attach_target(struct cxl_region *cxlr,
|
static int attach_target(struct cxl_region *cxlr,
|
||||||
|
@ -2225,29 +2262,7 @@ static int attach_target(struct cxl_region *cxlr,
|
||||||
|
|
||||||
static int detach_target(struct cxl_region *cxlr, int pos)
|
static int detach_target(struct cxl_region *cxlr, int pos)
|
||||||
{
|
{
|
||||||
struct cxl_region_params *p = &cxlr->params;
|
return cxl_decoder_detach(cxlr, NULL, pos, DETACH_ONLY);
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = down_write_killable(&cxl_region_rwsem);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (pos >= p->interleave_ways) {
|
|
||||||
dev_dbg(&cxlr->dev, "position %d out of range %d\n", pos,
|
|
||||||
p->interleave_ways);
|
|
||||||
rc = -ENXIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p->targets[pos]) {
|
|
||||||
rc = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = cxl_region_detach(p->targets[pos]);
|
|
||||||
out:
|
|
||||||
up_write(&cxl_region_rwsem);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos,
|
static size_t store_targetN(struct cxl_region *cxlr, const char *buf, int pos,
|
||||||
|
|
Loading…
Add table
Reference in a new issue