From nobody Sun Feb 8 18:08:44 2026 Received: from mail-qk1-f170.google.com (mail-qk1-f170.google.com [209.85.222.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0EF6A365A13 for ; Mon, 12 Jan 2026 16:35:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235759; cv=none; b=r1rVxi5QF+WTKpqMBHERCZ+6kgAXfzTLfA495Z2GWAGxExuTseR5iZFA9X5nzySW7OoiedvKITm2bifAU2GQvTVZKwATA7eur6JLp7gBMyLQY9u6ub2ueU5HoAyq3B/XESsslgTDKzc+7xBKwUUuqOfZYnBMx+k7b5Z2/HocGFg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235759; c=relaxed/simple; bh=8Lxk5q8Y8vH2rpMB8enFhlEA8toiRN4zlIRDGPRvUrI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Cci8NPknT9mwo2qQ+w6z6mkLuBeJ047e6zX7iY2pfK8gmP5cfyjj2QMK9OLzJK1ksZARF4dIumYg6rkEeKPnsqG/umfmfOoFTxfQO0YPlPlim1jubSXdNfCsQWdvjzDcDgzCidIiJ2un7Sme6DRodNX4CdkH4lnlQB79C7xihUc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=WH8Gde/s; arc=none smtp.client-ip=209.85.222.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="WH8Gde/s" Received: by mail-qk1-f170.google.com with SMTP id af79cd13be357-8b2d32b9777so997243885a.2 for ; Mon, 12 Jan 2026 08:35:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1768235757; x=1768840557; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kitsfgvAgIwjSktv7QGOLbd42NqWt1GntcXx/7ro77s=; b=WH8Gde/s0PH2QzWDWcn6o5rKkEfLrLlwvZpp8IMw+X3FFRiTPvDa4vJAasVBm9chSY tVisJZBKi56poVQVA0gVLoZqNonnMYzkA1hU4n7Sr9Pu9kN1pglAfREIL5Rc61hcKzsK rnUfppOlkZ5pez8FkOPgIuv9ELdllC8AX3YuJD/5SD8aiek53HXLw0T99n1NsK+RKOVn lN21EDo3VX4BbiKqLrjIPgzFJs6Rm8rw5y2N+xiShgrfPjANzaJaBICL0u/inkmkbs/R waTjAWU5WtI+2LT61J8Uzd9fy5JBUlrBzK0jXLdZGod+zc6dwOh2VXxIZMA9VkVHy7K3 htZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768235757; x=1768840557; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kitsfgvAgIwjSktv7QGOLbd42NqWt1GntcXx/7ro77s=; b=JlZTWzLoIzuzytpWLTzQVaFu8VqecFuKXjQYyXWcRmFmVg3mJhPSEK/SHLjAF80TWA 6UZZwjwLmBEJKPBx3E0QXEFVBDbDdvQLioHtfWOXFCxwHjAPY3+jA7b6BFUZAUPGrB0F vifv2+ynp7arVi3vEsEdK5W5YWkjmPRj2bSCFBuiDd1DqaSmDH/X9ywdATIJtshfltGS jr97VPnihxX8KCYZf/bMRmyxS/VbBZT/ZU6Y8nWILoRGZ32M7eTK2/hKfgXOt5FOoSqP NjRQA4NM8i7sLpnAPvDQlzgqpih4g6g8dO6P8rNp0kAJIfyUnAnBopqYkIuFOUaiNg12 b6OQ== X-Gm-Message-State: AOJu0YyDdni5pN8hGIFTguC/JZjV66jlb3kc9NNbJLsEXDO9365f0N3a qeOiYknVqPJSILGSJC0XiDEMM5UydPi2zapd7KhjaDzR6XKWV5AjFZPKM7tFfLM8MiA= X-Gm-Gg: AY/fxX6Kht1m+VXhz6xgwzOCnADq14NRpZKkc3V/lEftwLxTzUc4zB/rKEYVanTAUVE eAOesTMLzDVaI3lmWh6snvkDOpps7X3wJZVtwTm2f3agc+v51coNanWDzYn9eFdm2+EoiGsJfWp T969n+KbT2bK0J45Q7AjYiUiQjwjr3yOlKEnp3d270+ubS0B9teBmszIor5ZUS0D2dDFL+ighhI aElfEBvgsX/nAGond56o1s5MrwF3WCbKTiPDR0jUpfF3krKcHqRGBBfoEjXfMpjSndv/mfC6Ziy aiH2z9KXnfC8g1I8gmlw339s8cj0ZHPqvJWWsXuGHk4U25xIhfS8o7dN1QjuLBDmRO5zE0QTttL VoccEegUx0FH18Fd+GBI7Fu/zS5gjEaJpJrXv23/rEuKadwrBptxmgfgZaQVmpwh9LNGZqAb9Xw QNa3j+wtHO0vrcS2SXCkcLBahu1BX/yM67WyCfobvwyS8DobeMUTls3BcknmnYkT88VdRYa7Tbs 30= X-Google-Smtp-Source: AGHT+IEfsLXkvZ5Af7pEDx7TCXcw8AH3MCvjCGKobIA4iIxO4KEaAk5ecCJQJ+qJH2eNNZ5KFkYuBA== X-Received: by 2002:a05:620a:458c:b0:8ba:419f:c79e with SMTP id af79cd13be357-8c3893f5027mr2124194685a.55.1768235756801; Mon, 12 Jan 2026 08:35:56 -0800 (PST) Received: from gourry-fedora-PF4VCD3F.lan (pool-96-255-20-138.washdc.ftas.verizon.net. [96.255.20.138]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8c37f4a7962sm1489152685a.11.2026.01.12.08.35.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jan 2026 08:35:56 -0800 (PST) From: Gregory Price To: linux-cxl@vger.kernel.org Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, dave@stgolabs.net, jonathan.cameron@huawei.com, dave.jiang@intel.com, alison.schofield@intel.com, vishal.l.verma@intel.com, ira.weiny@intel.com, dan.j.williams@intel.com Subject: [PATCH 1/6] drivers/cxl: add cxl_memctrl_mode and region->memctrl Date: Mon, 12 Jan 2026 11:35:09 -0500 Message-ID: <20260112163514.2551809-2-gourry@gourry.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260112163514.2551809-1-gourry@gourry.net> References: <20260112163514.2551809-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The CXL driver presently hands policy management over to DAX subsystem for sysram regions, which makes building policy around the entire region clunky and at time difficult (e.g. multiple actions to offline and hot-unplug memory reliably). To support multiple backend controllers for memory regions (for example dax vs direct hotplug), implement a memctrl field in cxl_region allows switching uncomitted regions between different "memory controllers". CXL_CONTROL_NONE: No selected controller, probe will fail. CXL_CONTROL_AUTO: If memory is already online as SysRAM, no controller otherwise register a dax_region CXL_CONTROL_DAX : register a dax_region Auto regions will either be static sysram (BIOS-onlined) and has no region controller associated with it - or if the SP bit was set a DAX device will be created. Rather than default all regions to the auto-controller, only default auto-regions to the auto controller. Non-auto regions will be defaulted to CXL_CONTROL_NONE, which will cause a failure to probe unless a controller is selected. Signed-off-by: Gregory Price --- drivers/cxl/core/Makefile | 1 + drivers/cxl/core/core.h | 2 + drivers/cxl/core/memctrl/Makefile | 4 + drivers/cxl/core/memctrl/dax_region.c | 79 +++++++++++++++ drivers/cxl/core/memctrl/memctrl.c | 42 ++++++++ drivers/cxl/core/region.c | 136 ++++++++++---------------- drivers/cxl/cxl.h | 14 +++ 7 files changed, 192 insertions(+), 86 deletions(-) create mode 100644 drivers/cxl/core/memctrl/Makefile create mode 100644 drivers/cxl/core/memctrl/dax_region.c create mode 100644 drivers/cxl/core/memctrl/memctrl.c diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index 5ad8fef210b5..79de20e3f8aa 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -17,6 +17,7 @@ cxl_core-y +=3D cdat.o cxl_core-y +=3D ras.o cxl_core-$(CONFIG_TRACING) +=3D trace.o cxl_core-$(CONFIG_CXL_REGION) +=3D region.o +include $(src)/memctrl/Makefile cxl_core-$(CONFIG_CXL_MCE) +=3D mce.o cxl_core-$(CONFIG_CXL_FEATURES) +=3D features.o cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) +=3D edac.o diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 1fb66132b777..1156a4bd0080 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -42,6 +42,8 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port); struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 d= pa); u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, u64 dpa); +int cxl_enable_memctrl(struct cxl_region *cxlr); +int devm_cxl_add_dax_region(struct cxl_region *cxlr); =20 #else static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, diff --git a/drivers/cxl/core/memctrl/Makefile b/drivers/cxl/core/memctrl/M= akefile new file mode 100644 index 000000000000..8165aad5a52a --- /dev/null +++ b/drivers/cxl/core/memctrl/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/memctrl.o +cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/dax_region.o diff --git a/drivers/cxl/core/memctrl/dax_region.c b/drivers/cxl/core/memct= rl/dax_region.c new file mode 100644 index 000000000000..90d7fdb97013 --- /dev/null +++ b/drivers/cxl/core/memctrl/dax_region.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include +#include "../core.h" + +static struct lock_class_key cxl_dax_region_key; + +static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr) +{ + struct cxl_region_params *p =3D &cxlr->params; + struct cxl_dax_region *cxlr_dax; + struct device *dev; + + guard(rwsem_read)(&cxl_rwsem.region); + if (p->state !=3D CXL_CONFIG_COMMIT) + return ERR_PTR(-ENXIO); + + cxlr_dax =3D kzalloc(sizeof(*cxlr_dax), GFP_KERNEL); + if (!cxlr_dax) + return ERR_PTR(-ENOMEM); + + cxlr_dax->hpa_range.start =3D p->res->start; + cxlr_dax->hpa_range.end =3D p->res->end; + + dev =3D &cxlr_dax->dev; + cxlr_dax->cxlr =3D cxlr; + device_initialize(dev); + lockdep_set_class(&dev->mutex, &cxl_dax_region_key); + device_set_pm_not_required(dev); + dev->parent =3D &cxlr->dev; + dev->bus =3D &cxl_bus_type; + dev->type =3D &cxl_dax_region_type; + + return cxlr_dax; +} + +static void cxlr_dax_unregister(void *_cxlr_dax) +{ + struct cxl_dax_region *cxlr_dax =3D _cxlr_dax; + + device_unregister(&cxlr_dax->dev); +} + +/* + * The dax controller is the default controller and simply hands the + * control pattern over to the dax driver. It does with a dax_region + * built by dax/cxl.c + */ +int devm_cxl_add_dax_region(struct cxl_region *cxlr) +{ + struct cxl_dax_region *cxlr_dax; + struct device *dev; + int rc; + + cxlr_dax =3D cxl_dax_region_alloc(cxlr); + if (IS_ERR(cxlr_dax)) + return PTR_ERR(cxlr_dax); + + dev =3D &cxlr_dax->dev; + rc =3D dev_set_name(dev, "dax_region%d", cxlr->id); + if (rc) + goto err; + + rc =3D device_add(dev); + if (rc) + goto err; + + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), + dev_name(dev)); + + return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister, + cxlr_dax); +err: + put_device(dev); + return rc; +} diff --git a/drivers/cxl/core/memctrl/memctrl.c b/drivers/cxl/core/memctrl/= memctrl.c new file mode 100644 index 000000000000..24e0e14b39c7 --- /dev/null +++ b/drivers/cxl/core/memctrl/memctrl.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +/* Copyright(c) 2026 Meta Inc. All rights reserved. */ +#include +#include +#include +#include +#include "../core.h" + +static int is_system_ram(struct resource *res, void *arg) +{ + struct cxl_region *cxlr =3D arg; + struct cxl_region_params *p =3D &cxlr->params; + + dev_dbg(&cxlr->dev, "%pr has System RAM: %pr\n", p->res, res); + return 1; +} + +int cxl_enable_memctrl(struct cxl_region *cxlr) +{ + struct cxl_region_params *p =3D &cxlr->params; + + switch (cxlr->memctrl) { + case CXL_MEMCTRL_AUTO: + /* + * The region can not be manged by CXL if any portion of + * it is already online as 'System RAM' + */ + if (walk_iomem_res_desc(IORES_DESC_NONE, + IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY, + p->res->start, p->res->end, cxlr, + is_system_ram) > 0) + return 0; + return devm_cxl_add_dax_region(cxlr); + case CXL_MEMCTRL_DAX: + return devm_cxl_add_dax_region(cxlr); + default: + return -EINVAL; + } +} + + diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index ae899f68551f..02d7d9ae0252 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -626,6 +626,50 @@ static ssize_t mode_show(struct device *dev, struct de= vice_attribute *attr, } static DEVICE_ATTR_RO(mode); =20 +static ssize_t ctrl_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cxl_region *cxlr =3D to_cxl_region(dev); + const char *desc; + + switch (cxlr->memctrl) { + case CXL_MEMCTRL_AUTO: + desc =3D "auto"; + break; + case CXL_MEMCTRL_DAX: + desc =3D "dax"; + break; + default: + desc =3D ""; + break; + } + + return sysfs_emit(buf, "%s\n", desc); +} + +static ssize_t ctrl_store(struct device *dev, struct device_attribute *att= r, + const char *buf, size_t len) +{ + struct cxl_region *cxlr =3D to_cxl_region(dev); + struct cxl_region_params *p =3D &cxlr->params; + int rc; + + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); + if ((rc =3D ACQUIRE_ERR(rwsem_write_kill, &rwsem))) + return rc; + + if (p->state >=3D CXL_CONFIG_COMMIT) + return -EBUSY; + + if (sysfs_streq(buf, "dax")) + cxlr->memctrl =3D CXL_MEMCTRL_DAX; + else + return -EINVAL; + + return len; +} +static DEVICE_ATTR_RW(ctrl); + static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size) { struct cxl_root_decoder *cxlrd =3D to_cxl_root_decoder(cxlr->dev.parent); @@ -772,6 +816,7 @@ static struct attribute *cxl_region_attrs[] =3D { &dev_attr_size.attr, &dev_attr_mode.attr, &dev_attr_extended_linear_cache_size.attr, + &dev_attr_ctrl.attr, NULL, }; =20 @@ -2598,6 +2643,7 @@ static struct cxl_region *devm_cxl_add_region(struct = cxl_root_decoder *cxlrd, return cxlr; cxlr->mode =3D mode; cxlr->type =3D type; + cxlr->memctrl =3D CXL_MEMCTRL_NONE; =20 dev =3D &cxlr->dev; rc =3D dev_set_name(dev, "region%d", id); @@ -3307,37 +3353,6 @@ struct cxl_dax_region *to_cxl_dax_region(struct devi= ce *dev) } EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL"); =20 -static struct lock_class_key cxl_dax_region_key; - -static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr) -{ - struct cxl_region_params *p =3D &cxlr->params; - struct cxl_dax_region *cxlr_dax; - struct device *dev; - - guard(rwsem_read)(&cxl_rwsem.region); - if (p->state !=3D CXL_CONFIG_COMMIT) - return ERR_PTR(-ENXIO); - - cxlr_dax =3D kzalloc(sizeof(*cxlr_dax), GFP_KERNEL); - if (!cxlr_dax) - return ERR_PTR(-ENOMEM); - - cxlr_dax->hpa_range.start =3D p->res->start; - cxlr_dax->hpa_range.end =3D p->res->end; - - dev =3D &cxlr_dax->dev; - cxlr_dax->cxlr =3D cxlr; - device_initialize(dev); - lockdep_set_class(&dev->mutex, &cxl_dax_region_key); - device_set_pm_not_required(dev); - dev->parent =3D &cxlr->dev; - dev->bus =3D &cxl_bus_type; - dev->type =3D &cxl_dax_region_type; - - return cxlr_dax; -} - static void cxlr_pmem_unregister(void *_cxlr_pmem) { struct cxl_pmem_region *cxlr_pmem =3D _cxlr_pmem; @@ -3424,42 +3439,6 @@ static int devm_cxl_add_pmem_region(struct cxl_regio= n *cxlr) return rc; } =20 -static void cxlr_dax_unregister(void *_cxlr_dax) -{ - struct cxl_dax_region *cxlr_dax =3D _cxlr_dax; - - device_unregister(&cxlr_dax->dev); -} - -static int devm_cxl_add_dax_region(struct cxl_region *cxlr) -{ - struct cxl_dax_region *cxlr_dax; - struct device *dev; - int rc; - - cxlr_dax =3D cxl_dax_region_alloc(cxlr); - if (IS_ERR(cxlr_dax)) - return PTR_ERR(cxlr_dax); - - dev =3D &cxlr_dax->dev; - rc =3D dev_set_name(dev, "dax_region%d", cxlr->id); - if (rc) - goto err; - - rc =3D device_add(dev); - if (rc) - goto err; - - dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), - dev_name(dev)); - - return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister, - cxlr_dax); -err: - put_device(dev); - return rc; -} - static int match_decoder_by_range(struct device *dev, const void *data) { const struct range *r1, *r2 =3D data; @@ -3579,6 +3558,9 @@ static int __construct_region(struct cxl_region *cxlr, =20 set_bit(CXL_REGION_F_AUTO, &cxlr->flags); =20 + /* Auto-regions will either be static sysram (onlined by BIOS) or DAX */ + cxlr->memctrl =3D CXL_MEMCTRL_AUTO; + res =3D kmalloc(sizeof(*res), GFP_KERNEL); if (!res) return -ENOMEM; @@ -3755,15 +3737,6 @@ u64 cxl_port_get_spa_cache_alias(struct cxl_port *en= dpoint, u64 spa) } EXPORT_SYMBOL_NS_GPL(cxl_port_get_spa_cache_alias, "CXL"); =20 -static int is_system_ram(struct resource *res, void *arg) -{ - struct cxl_region *cxlr =3D arg; - struct cxl_region_params *p =3D &cxlr->params; - - dev_dbg(&cxlr->dev, "%pr has System RAM: %pr\n", p->res, res); - return 1; -} - static void shutdown_notifiers(void *_cxlr) { struct cxl_region *cxlr =3D _cxlr; @@ -3965,16 +3938,7 @@ static int cxl_region_probe(struct device *dev) dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=3D%d failed\n", cxlr->id); =20 - /* - * The region can not be manged by CXL if any portion of - * it is already online as 'System RAM' - */ - if (walk_iomem_res_desc(IORES_DESC_NONE, - IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY, - p->res->start, p->res->end, cxlr, - is_system_ram) > 0) - return 0; - return devm_cxl_add_dax_region(cxlr); + return cxl_enable_memctrl(cxlr); default: dev_dbg(&cxlr->dev, "unsupported region mode: %d\n", cxlr->mode); diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index ba17fa86d249..b8fabaa77262 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -502,6 +502,19 @@ enum cxl_partition_mode { CXL_PARTMODE_PMEM, }; =20 + +/* + * Memory Controller modes: + * None - No controller selected + * Auto - either BIOS-configured as SysRAM, or default to DAX + * DAX - creates a dax_region controller for the cxl_region + */ +enum cxl_memctrl_mode { + CXL_MEMCTRL_NONE, + CXL_MEMCTRL_AUTO, + CXL_MEMCTRL_DAX, +}; + /* * Indicate whether this region has been assembled by autodetection or * userspace assembly. Prevent endpoint decoders outside of automatic @@ -543,6 +556,7 @@ struct cxl_region { struct device dev; int id; enum cxl_partition_mode mode; + enum cxl_memctrl_mode memctrl; enum cxl_decoder_type type; struct cxl_nvdimm_bridge *cxl_nvb; struct cxl_pmem_region *cxlr_pmem; --=20 2.52.0 From nobody Sun Feb 8 18:08:45 2026 Received: from mail-qk1-f172.google.com (mail-qk1-f172.google.com [209.85.222.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 920043659F8 for ; Mon, 12 Jan 2026 16:36:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235762; cv=none; b=Mz0wmzSMGcEJJG1PMpissgL/WvrLdvHmrMOa8UV31kGsyVtOYQgcaqVy9xLUggtBq6jSgSyOHohvQ+iu48R546YVxve7kWIvtChxPjydt2xIXP5u4Y9crR/xjDId0q/BCGT6pqhmOCAnZkWXnlp02fDVZxdIn0dz9kANMLjXd7E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235762; c=relaxed/simple; bh=JrHNQt8zuafgYPfF3gFXWJn22SxhOqIEHjy19OUrPSw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=e+QYFDrp6xE8FE/M0zQ0WmgEjxT499MU2GbY6ZYF6xAsE5alwcZiHsPysjHHLZh7xWwHWvtsYM42B4KtDLyP4kWzr7VIjYGbfrt6sH9d1FpVdKfarqLtgtMPychRIC/Op/s22pnDnThuPZDDNEnsKa4UAaW87w1zxaLwOGw7Gv0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=SbA+GsFc; arc=none smtp.client-ip=209.85.222.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="SbA+GsFc" Received: by mail-qk1-f172.google.com with SMTP id af79cd13be357-8b220ddc189so897601085a.0 for ; Mon, 12 Jan 2026 08:36:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1768235759; x=1768840559; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=O4OBouSJxcSdHo3tQ8y/LhFr77uHm9IloTJDMHbIIbI=; b=SbA+GsFcOeLT+4goAQy+I+jwsZ/RkmVxVZYBlhS//UwjvV8p8LeYbaQmZtLciCzbgm qFqKSbC38eXIF1mO3jVZhFXMOsw5/zK3H9bXUGVRjq80zUjYdZp2QtOADxcnlq5BPQr7 o7Otufz2Gsc25KFJ6B7u5LmjM9CQRrBCA2OrP16s1UMN9Is2QXqlk4svjfloDIWSW91L is39XprLyD+sP8lenhq1l6mUu9loPR+WhSnt7+TgCp61xdHIPy6Ion+JdDO8EdNrjffe IwZ7jcK2kmzSnfcbVrK3ZvSm5IY7xIcrcvwM0ukWh47RwRqy+L+3ylaAcoo0m1sMCyKd mucQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768235759; x=1768840559; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=O4OBouSJxcSdHo3tQ8y/LhFr77uHm9IloTJDMHbIIbI=; b=XJUODlp1xUnH0tukWJhVhPMzVGiWS6BYXCYX0ORtYE/DsQwk9cQ1hS+KO3MYclILUl gg3jAfi79Nc2UUtK6Ry+5xXcC4PkZPOVyw8HBmzyskq+mgDet4Ja6ksRzgdClJsIfUQT nR5ryxTN1YHsCEYLEDCYDL/xuZUAEe75Khl/PchIJL2rEl7THB/bVPLFTFCQ5LmnTCHu mBgNxPqKzO5i7KD5PwDTLCEvY8f6v1fEr9X2OjC8bgeXqvojOtiw3Ep4EaP0/LpWTBAG 3YmycqRdyPhDAuuYUCUt2gXsE8vmUJ4FFac8nESBFSG4LeufQec5A/VR6IiybHsiUt+e X4Wg== X-Gm-Message-State: AOJu0Yx4LWiIpkyE43nBkNetJOpnrQ/kzQ8275Eqr6l21e1ITnvXUYuu gn11mQxz8SrU6tt6VCf8XUBWhlCpRehcIn3iqxV3rYk6SUMtYed5GqpfJ/MzpN9QPHU= X-Gm-Gg: AY/fxX7sf9iDbCJPQTVAX/IsNzNx+rY5jIW7OaNhogMhgDAqcFKMtXprFzcW6/oBxuo TFYtfek5h1du5NjF/XNDTNBe1N/+PfZvfWImQ+a/5Sbk4htGebF8HLFdffpIJE+Ay3jo+mXeAaC nb73CZePf43gQ7P7FSpZnPhQ0JeYSZfyU278UYa4TEA19iDYik+NgonxNbO0o+c/+oMG80bAXT5 r90LvjvfDV0i++59rZK8XDBVBufn+sof6jX8DHzwsYiAS+M77OID5bPN21Ar5YDDEkdgl+hRAaY JMmks/lcQHmfNQOt71fDZbcaY+fDLT6StRx7sTibKyXVIU1nZr6Ld+MrEl9zwaihhLcHjBKrs75 a16jGZ2EPwSbsoYIp/0Shmpsmxe4Y3lOOs0iyH3l7XfVcTTzOJwGxYkybJLYDuqyYAHE4e19W2K JAnv3PdxhN5vt+R+k0WQEQh/uzdGMnqLccWtNNY0gfRGtI0EFfDE2YqYFDAqRhOT7Qh+0f9CU1J 9A= X-Google-Smtp-Source: AGHT+IG2Jjm6s9R3smVtD6zbj1AGex6fzQAS3nqpLst7EeccmBodp/dBMnr2YHsUiN17+Yt/X6pa3Q== X-Received: by 2002:a05:620a:2886:b0:8b1:ed55:e4f0 with SMTP id af79cd13be357-8c3893a06d1mr2719163985a.39.1768235759421; Mon, 12 Jan 2026 08:35:59 -0800 (PST) Received: from gourry-fedora-PF4VCD3F.lan (pool-96-255-20-138.washdc.ftas.verizon.net. [96.255.20.138]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8c37f4a7962sm1489152685a.11.2026.01.12.08.35.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jan 2026 08:35:59 -0800 (PST) From: Gregory Price To: linux-cxl@vger.kernel.org Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, dave@stgolabs.net, jonathan.cameron@huawei.com, dave.jiang@intel.com, alison.schofield@intel.com, vishal.l.verma@intel.com, ira.weiny@intel.com, dan.j.williams@intel.com, David Hildenbrand Subject: [PATCH 2/6] cxl: add sysram_region memory controller Date: Mon, 12 Jan 2026 11:35:10 -0500 Message-ID: <20260112163514.2551809-3-gourry@gourry.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260112163514.2551809-1-gourry@gourry.net> References: <20260112163514.2551809-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a sysram memctrl that directly hotplugs memory without needing to route through DAX. This simplifies the sysram usecase considerably. The sysram memctl adds new sysfs controls when registered: region/memctrl/[hotplug, hotunplug, state] hotplug: controller attempts to hotplug the memory region hotunplug: controller attempts to offline and hotunplug the memory region state: [online,online_normal,offline] online : controller onlines blocks in ZONE_MOVABLE online_normal: controller onlines blocks in ZONE_NORMAL offline : controller attempts to offline the memory blocks Hotplug note - by default the controller will hotplug the blocks, but leave them offline (unless MHP auto-online in Kconfig is enabled). Setting state to "online_normal" may prevent future hot-unplug of sysram regions, and unbinding a memory region with memory online in ZONE_NORMAL may result in the device being removed but the memory remaining online. This can result in future management functions failing (such as adding a new region). This is why "online_normal" is explicit, and the default online zone is ZONE_MOVABLE. Cc: David Hildenbrand Signed-off-by: Gregory Price --- drivers/cxl/core/core.h | 2 + drivers/cxl/core/memctrl/Makefile | 1 + drivers/cxl/core/memctrl/memctrl.c | 2 + drivers/cxl/core/memctrl/sysram_region.c | 358 +++++++++++++++++++++++ drivers/cxl/core/region.c | 5 + drivers/cxl/cxl.h | 6 +- 6 files changed, 372 insertions(+), 2 deletions(-) create mode 100644 drivers/cxl/core/memctrl/sysram_region.c diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 1156a4bd0080..18cb84950500 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -31,6 +31,8 @@ int cxl_decoder_detach(struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled, int pos, enum cxl_detach_mode mode); =20 +int devm_cxl_add_sysram_region(struct cxl_region *cxlr); + #define CXL_REGION_ATTR(x) (&dev_attr_##x.attr) #define CXL_REGION_TYPE(x) (&cxl_region_type) #define SET_CXL_REGION_ATTR(x) (&dev_attr_##x.attr), diff --git a/drivers/cxl/core/memctrl/Makefile b/drivers/cxl/core/memctrl/M= akefile index 8165aad5a52a..1c52c7d75570 100644 --- a/drivers/cxl/core/memctrl/Makefile +++ b/drivers/cxl/core/memctrl/Makefile @@ -2,3 +2,4 @@ =20 cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/memctrl.o cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/dax_region.o +cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/sysram_region.o diff --git a/drivers/cxl/core/memctrl/memctrl.c b/drivers/cxl/core/memctrl/= memctrl.c index 24e0e14b39c7..40ffb59353bb 100644 --- a/drivers/cxl/core/memctrl/memctrl.c +++ b/drivers/cxl/core/memctrl/memctrl.c @@ -34,6 +34,8 @@ int cxl_enable_memctrl(struct cxl_region *cxlr) return devm_cxl_add_dax_region(cxlr); case CXL_MEMCTRL_DAX: return devm_cxl_add_dax_region(cxlr); + case CXL_MEMCTRL_SYSRAM: + return devm_cxl_add_sysram_region(cxlr); default: return -EINVAL; } diff --git a/drivers/cxl/core/memctrl/sysram_region.c b/drivers/cxl/core/me= mctrl/sysram_region.c new file mode 100644 index 000000000000..a7570c8a54e1 --- /dev/null +++ b/drivers/cxl/core/memctrl/sysram_region.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2026 Meta Inc. All rights reserved. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core.h" + +/* If HMAT was unavailable, assign a default distance. */ +#define MEMTIER_DEFAULT_CXL_ADISTANCE (MEMTIER_ADISTANCE_DRAM * 5) + +static const char *sysram_name =3D "System RAM (CXL)"; + +struct cxl_sysram_data { + const char *res_name; + int mgid; + struct resource *res; +}; + +static DEFINE_MUTEX(cxl_memory_type_lock); +static LIST_HEAD(cxl_memory_types); + +static struct cxl_region *to_cxl_region(struct device *dev) +{ + if (dev->type !=3D &cxl_region_type) + return NULL; + return container_of(dev, struct cxl_region, dev); +} + +static struct memory_dev_type *cxl_find_alloc_memory_type(int adist) +{ + guard(mutex)(&cxl_memory_type_lock); + return mt_find_alloc_memory_type(adist, &cxl_memory_types); +} + +static void __maybe_unused cxl_put_memory_types(void) +{ + guard(mutex)(&cxl_memory_type_lock); + mt_put_memory_types(&cxl_memory_types); +} + +static int cxl_sysram_range(struct cxl_region *cxlr, struct range *r) +{ + struct cxl_region_params *p =3D &cxlr->params; + + if (!p->res) + return -ENODEV; + + /* memory-block align the hotplug range */ + r->start =3D ALIGN(p->res->start, memory_block_size_bytes()); + r->end =3D ALIGN_DOWN(p->res->end + 1, memory_block_size_bytes()) - 1; + if (r->start >=3D r->end) { + r->start =3D p->res->start; + r->end =3D p->res->end; + return -ENOSPC; + } + return 0; +} + +static ssize_t hotunplug_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr =3D to_cxl_region(dev); + struct range range; + int rc; + + if (!cxlr) + return -ENODEV; + + rc =3D cxl_sysram_range(cxlr, &range); + if (rc) + return rc; + + rc =3D offline_and_remove_memory(range.start, range_len(&range)); + + if (rc) + return rc; + + return len; +} +static DEVICE_ATTR_WO(hotunplug); + +struct online_memory_cb_arg { + int online_type; + int rc; +}; + +static int online_memory_block_cb(struct memory_block *mem, void *arg) +{ + struct online_memory_cb_arg *cb_arg =3D arg; + + if (signal_pending(current)) + return -EINTR; + + cond_resched(); + + if (mem->state =3D=3D MEM_ONLINE) + return 0; + + mem->online_type =3D cb_arg->online_type; + cb_arg->rc =3D device_online(&mem->dev); + + return cb_arg->rc; +} + +static int offline_memory_block_cb(struct memory_block *mem, void *arg) +{ + int *rc =3D arg; + + if (signal_pending(current)) + return -EINTR; + + cond_resched(); + + if (mem->state =3D=3D MEM_OFFLINE) + return 0; + + *rc =3D device_offline(&mem->dev); + + return *rc; +} + +static ssize_t state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr =3D to_cxl_region(dev); + struct online_memory_cb_arg cb_arg; + struct range range; + int rc; + + if (!cxlr) + return -ENODEV; + + rc =3D cxl_sysram_range(cxlr, &range); + if (rc) + return rc; + + rc =3D lock_device_hotplug_sysfs(); + if (rc) + return rc; + + if (sysfs_streq(buf, "online")) { + cb_arg.online_type =3D MMOP_ONLINE_MOVABLE; + cb_arg.rc =3D 0; + rc =3D walk_memory_blocks(range.start, range_len(&range), + &cb_arg, online_memory_block_cb); + if (!rc) + rc =3D cb_arg.rc; + } else if (sysfs_streq(buf, "online_normal")) { + cb_arg.online_type =3D MMOP_ONLINE; + cb_arg.rc =3D 0; + rc =3D walk_memory_blocks(range.start, range_len(&range), + &cb_arg, online_memory_block_cb); + if (!rc) + rc =3D cb_arg.rc; + } else if (sysfs_streq(buf, "offline")) { + int offline_rc =3D 0; + + rc =3D walk_memory_blocks(range.start, range_len(&range), + &offline_rc, offline_memory_block_cb); + if (!rc) + rc =3D offline_rc; + } else { + rc =3D -EINVAL; + } + + unlock_device_hotplug(); + + if (rc) + return rc; + + return len; +} +static DEVICE_ATTR_WO(state); + +static ssize_t hotplug_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cxl_region *cxlr =3D to_cxl_region(dev); + struct cxl_sysram_data *data; + struct range range; + int rc; + + if (!cxlr) + return -ENODEV; + + data =3D dev_get_drvdata(dev); + if (!data) + return -ENODEV; + + rc =3D cxl_sysram_range(cxlr, &range); + if (rc) + return rc; + + rc =3D add_memory_driver_managed(data->mgid, range.start, + range_len(&range), sysram_name, + MHP_NID_IS_MGID); + if (rc) + return rc; + + return len; +} +static DEVICE_ATTR_WO(hotplug); + +static struct attribute *cxl_sysram_region_attrs[] =3D { + &dev_attr_hotunplug.attr, + &dev_attr_state.attr, + &dev_attr_hotplug.attr, + NULL, +}; + +static const struct attribute_group cxl_sysram_region_group =3D { + .name =3D "memctl", + .attrs =3D cxl_sysram_region_attrs, +}; + +static void cxl_sysram_unregister(void *_data) +{ + struct cxl_sysram_data *data =3D _data; + struct range range =3D { + .start =3D data->res->start, + .end =3D data->res->end + }; + + /* We have one shot for removal, otherwise it's stuck til reboot */ + if (!offline_and_remove_memory(range.start, range_len(&range))) { + remove_resource(data->res); + kfree(data->res); + memory_group_unregister(data->mgid); + kfree(data->res_name); + kfree(data); + return; + } + pr_err("CXL: %#llx-%#llx cannot be hotremoved until next reboot\n", + range.start, range.end); +} + +int devm_cxl_add_sysram_region(struct cxl_region *cxlr) +{ + struct cxl_region_params *p =3D &cxlr->params; + struct device *dev =3D &cxlr->dev; + struct cxl_sysram_data *data; + struct memory_dev_type *mtype; + unsigned long total_len =3D 0; + struct resource *res; + struct range range; + mhp_t mhp_flags; + int numa_node; + int adist =3D MEMTIER_DEFAULT_CXL_ADISTANCE; + int rc; + + numa_node =3D phys_to_target_node(p->res->start); + if (numa_node < 0) { + dev_warn(dev, "rejecting CXL region with invalid node: %d\n", + numa_node); + return -EINVAL; + } + + rc =3D cxl_sysram_range(cxlr, &range); + if (rc) { + dev_info(dev, "range %#llx-%#llx too small after alignment\n", + range.start, range.end); + return rc; + } + total_len =3D range_len(&range); + + if (!total_len) { + dev_warn(dev, "rejecting CXL region without any memory after alignment\n= "); + return -EINVAL; + } + + mt_calc_adistance(numa_node, &adist); + mtype =3D cxl_find_alloc_memory_type(adist); + if (IS_ERR(mtype)) + return PTR_ERR(mtype); + + init_node_memory_type(numa_node, mtype); + + data =3D kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + rc =3D -ENOMEM; + goto err_data; + } + + data->res_name =3D kstrdup(dev_name(dev), GFP_KERNEL); + if (!data->res_name) { + rc =3D -ENOMEM; + goto err_res_name; + } + + rc =3D memory_group_register_static(numa_node, PFN_UP(total_len)); + if (rc < 0) + goto err_reg_mgid; + data->mgid =3D rc; + + /* Region is permanently reserved if hotremove fails when unbinding. */ + res =3D request_mem_region(range.start, range_len(&range), + data->res_name); + if (!res) { + dev_warn(dev, "range %#llx-%#llx could not reserve region\n", + range.start, range.end); + rc =3D -EBUSY; + goto err_request_mem; + } + data->res =3D res; + + /* + * Setup flags for System RAM. Leave _BUSY clear so add_memory() can add + * a child resource. Do not inherit flags from parent since it may set + * flags unknown to us that will the break add_memory() below. + */ + res->flags =3D IORESOURCE_SYSTEM_RAM; + mhp_flags =3D MHP_NID_IS_MGID; + rc =3D add_memory_driver_managed(data->mgid, range.start, + range_len(&range), sysram_name, mhp_flags); + if (rc) { + dev_warn(dev, "range %#llx-%#llx memory add failed\n", + range.start, range.end); + goto err_add_memory; + } + dev_dbg(dev, "%s: added %llu bytes as System RAM\n", dev_name(dev), + (unsigned long long)total_len); + + dev_set_drvdata(dev, data); + rc =3D devm_device_add_group(dev, &cxl_sysram_region_group); + if (rc) + goto err_add_group; + + return devm_add_action_or_reset(dev, cxl_sysram_unregister, data); + +err_add_group: + dev_set_drvdata(dev, NULL); + /* if this fails, memory cannot be removed from the system until reboot */ + remove_memory(range.start, range_len(&range)); +err_add_memory: + remove_resource(res); + kfree(res); +err_request_mem: + memory_group_unregister(data->mgid); +err_reg_mgid: + kfree(data->res_name); +err_res_name: + kfree(data); +err_data: + clear_node_memory_type(numa_node, mtype); + return rc; +} diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 02d7d9ae0252..eeab091f043a 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -639,6 +639,9 @@ static ssize_t ctrl_show(struct device *dev, struct dev= ice_attribute *attr, case CXL_MEMCTRL_DAX: desc =3D "dax"; break; + case CXL_MEMCTRL_SYSRAM: + desc =3D "sysram"; + break; default: desc =3D ""; break; @@ -663,6 +666,8 @@ static ssize_t ctrl_store(struct device *dev, struct de= vice_attribute *attr, =20 if (sysfs_streq(buf, "dax")) cxlr->memctrl =3D CXL_MEMCTRL_DAX; + else if (sysfs_streq(buf, "sysram")) + cxlr->memctrl =3D CXL_MEMCTRL_SYSRAM; else return -EINVAL; =20 diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index b8fabaa77262..bb4f877b4e8f 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -506,13 +506,15 @@ enum cxl_partition_mode { /* * Memory Controller modes: * None - No controller selected - * Auto - either BIOS-configured as SysRAM, or default to DAX - * DAX - creates a dax_region controller for the cxl_region + * Auto - either BIOS-configured as SysRAM, or default to DAX + * DAX - creates a dax_region controller for the cxl_region + * SYSRAM - hotplugs the region directly as System RAM */ enum cxl_memctrl_mode { CXL_MEMCTRL_NONE, CXL_MEMCTRL_AUTO, CXL_MEMCTRL_DAX, + CXL_MEMCTRL_SYSRAM, }; =20 /* --=20 2.52.0 From nobody Sun Feb 8 18:08:45 2026 Received: from mail-qk1-f169.google.com (mail-qk1-f169.google.com [209.85.222.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B54C366DBD for ; Mon, 12 Jan 2026 16:36:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235766; cv=none; b=RkBHXRZP68cQsxEr2H5Znt+qXnr6tfrSsyyC0f0139cqT0/2cxxPwqlg+j7IKBcHCjxyMMDI+uSd+K2b8gSND5U0IzzdpPe84eSqKXwDY9415+grsiBP31x8Dom7E4mE4lnCmZ2AZL4nQRmM7U8SbzpqmmfSjRAKk4LQw5L+YEs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235766; c=relaxed/simple; bh=+vbo+ow3/N5FLNf+VKIFLg2lCo3Oq70TF5BpjObwuvY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BcYfE+yFPLwv3L8Q6XzPK8c9XEeBXUnf0yP8VhwnLSksGDhsEpNXEP0+EVWD30hzkm5IwJLG55vdXy1jxaZuA8bx7IGflOcGsKJ2Ye14pw/r5oWwSbfkQG4y5HdX1k3+sbqDE2++tL/nS4r5Gg+m67dO8bfGUQpPj7nbF195bm0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=r/5qGLht; arc=none smtp.client-ip=209.85.222.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="r/5qGLht" Received: by mail-qk1-f169.google.com with SMTP id af79cd13be357-8c0f15e8247so931657885a.3 for ; Mon, 12 Jan 2026 08:36:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1768235762; x=1768840562; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aMVuY/Qzumrs9qaMsiMla3bJLkpnnRK5ZTojwjJEFPc=; b=r/5qGLhtTIfbS6LcQ8/HYhjPVZLxjvtjqnHnezScyqWfhgg6XfWSbPLl1RaU/jIwi4 CLJoIdV7oOLThgRbq2bbFn7ZlTphh2nNMLQkPGYSopmp8Le5z9yDpdrtXrdK+HzN10ql 8rlAsNZVWskFmX4ASL4wjH5M9E3h/xFRoEjvrfCs6nisgQXzCngLRWIzj5DU15qw97x9 dFuqlGdhpqcE+CMKe9bPtLVtM/TFbuv8ku3qgcncen9afoLi0P8t8pxckjyW+A4+SN0G pWLUNCetvLFBPZtraiDEv0JN+OtsHw2c1J9QydxALylfXawf6qjPr7H0iYboj9nFMSCh CSlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768235762; x=1768840562; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=aMVuY/Qzumrs9qaMsiMla3bJLkpnnRK5ZTojwjJEFPc=; b=W+fZgg62lqPkp1LVXaZPW1MipYDBBhPURgFvf39i/sm76aNctsJNzOkL7bc67Wh18y tB6Fi9K0gwCj70NulkjK3lBmf/2Mv+/faOf1ms0655GsBBlMdWqykbAip2654xSFUDTp 5UniyzOqNvapfNaXWwf3J6nmI490NZtLtUphaQM6IcH59hWjQYT7Kf5bMvPlN9MTLqAA TJAdiEawfgPDImCyi/ZrOGsVFjXV//6K5zL8dskbeAHvulxTVGpem2iJo9De1Yd1bLnp H406R8NPuf82eM0upVR5rhqI12QDQb9PvK+yguRb41iPbkSHsS8/kY57SaVFaN6fUe/8 jrUQ== X-Gm-Message-State: AOJu0Yxjw4Suhpil5aksc/pSH35gOCBOS1OA+Sy6SpOUQ6+u61d23kRP 2YOtYpN8MWD7frnbh3d01QErHQ0ZX0aHkVNC5W1h228NUKhIO+ym4Y26Xwfc3VuEY0w= X-Gm-Gg: AY/fxX4v8fuaNs8F9SkN9Q6YiuhQ7/VAAFuudtnawTRcCeUa4Zljzqm9FQzkGkt296W OvCNdneiT9J0eye8QZcDzckOWuXqU4WOpq0c7/D8+xEAcG0bv6QetJKsRrt9PvopJOLX5pIISrZ GlT7/xWNsFmTgrB5DqbksvR0VHz/ueI3yncySk5wcAZSzYhGZuDw8C7o6LkgpVdzrAw+WjvV7lJ ikYzn+mcxC8x2xoLMpXFG8DNf/bSryug9K7BG35fbawDeLzW7PhuBAvIFsYe2VMeg/ZwKq4UwhQ 4G06d1GtpKaVzLfWAuYjQL0bJZ0tIxU/42zFWrZFtM+Cd4pHtOmYCJnc0J8apHHi/qAPd7w5Lkf bglc5w7xCXeUFW89uLTn9d9BJFyvIqR8lEpQ61dp7xWETa8XagNeANFJDuaXNNY7zHuog961IQX 1KXztZxk6jUqJ40INOsciZ4mdG57RXggfH66EDgCJVG1MIy40g2qCweHlGDI1NpeMVdJzANFXyg zc= X-Google-Smtp-Source: AGHT+IHQLRoHgdIof3w2MBkwi0guFaozrTsZkg3IhdJhjNNFlSv9rRMzr4LIL1PmoNVhqPsAyCN6MA== X-Received: by 2002:a05:620a:2592:b0:815:630d:2cbd with SMTP id af79cd13be357-8c38938b1ccmr2586207285a.34.1768235762141; Mon, 12 Jan 2026 08:36:02 -0800 (PST) Received: from gourry-fedora-PF4VCD3F.lan (pool-96-255-20-138.washdc.ftas.verizon.net. [96.255.20.138]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8c37f4a7962sm1489152685a.11.2026.01.12.08.36.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jan 2026 08:36:01 -0800 (PST) From: Gregory Price To: linux-cxl@vger.kernel.org Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, dave@stgolabs.net, jonathan.cameron@huawei.com, dave.jiang@intel.com, alison.schofield@intel.com, vishal.l.verma@intel.com, ira.weiny@intel.com, dan.j.williams@intel.com Subject: [PATCH 3/6] cxl/core/region: move pmem memctrl logic into memctrl/pmem_region Date: Mon, 12 Jan 2026 11:35:11 -0500 Message-ID: <20260112163514.2551809-4-gourry@gourry.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260112163514.2551809-1-gourry@gourry.net> References: <20260112163514.2551809-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the pmem_region logic from region.c into memctrl/pmem_region.c. Restrict the valid controllers for pmem to the pmem controller. Simplify the controller selection logic in region probe. Cc:=20 Signed-off-by: Gregory Price Reviewed-by: Ben Cheatham --- drivers/cxl/core/core.h | 1 + drivers/cxl/core/memctrl/Makefile | 1 + drivers/cxl/core/memctrl/memctrl.c | 2 + drivers/cxl/core/memctrl/pmem_region.c | 191 +++++++++++++++++++++ drivers/cxl/core/region.c | 221 +++---------------------- drivers/cxl/cxl.h | 2 + 6 files changed, 217 insertions(+), 201 deletions(-) create mode 100644 drivers/cxl/core/memctrl/pmem_region.c diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 18cb84950500..59175890a6ac 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -46,6 +46,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct = cxl_memdev *cxlmd, u64 dpa); int cxl_enable_memctrl(struct cxl_region *cxlr); int devm_cxl_add_dax_region(struct cxl_region *cxlr); +int devm_cxl_add_pmem_region(struct cxl_region *cxlr); =20 #else static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, diff --git a/drivers/cxl/core/memctrl/Makefile b/drivers/cxl/core/memctrl/M= akefile index 1c52c7d75570..efffc8ba2c0b 100644 --- a/drivers/cxl/core/memctrl/Makefile +++ b/drivers/cxl/core/memctrl/Makefile @@ -3,3 +3,4 @@ cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/memctrl.o cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/dax_region.o cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/sysram_region.o +cxl_core-$(CONFIG_CXL_REGION) +=3D memctrl/pmem_region.o diff --git a/drivers/cxl/core/memctrl/memctrl.c b/drivers/cxl/core/memctrl/= memctrl.c index 40ffb59353bb..1b661465bdeb 100644 --- a/drivers/cxl/core/memctrl/memctrl.c +++ b/drivers/cxl/core/memctrl/memctrl.c @@ -36,6 +36,8 @@ int cxl_enable_memctrl(struct cxl_region *cxlr) return devm_cxl_add_dax_region(cxlr); case CXL_MEMCTRL_SYSRAM: return devm_cxl_add_sysram_region(cxlr); + case CXL_MEMCTRL_PMEM: + return devm_cxl_add_pmem_region(cxlr); default: return -EINVAL; } diff --git a/drivers/cxl/core/memctrl/pmem_region.c b/drivers/cxl/core/memc= trl/pmem_region.c new file mode 100644 index 000000000000..57668dd82d71 --- /dev/null +++ b/drivers/cxl/core/memctrl/pmem_region.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include +#include "../core.h" + +static void cxl_pmem_region_release(struct device *dev) +{ + struct cxl_pmem_region *cxlr_pmem =3D to_cxl_pmem_region(dev); + int i; + + for (i =3D 0; i < cxlr_pmem->nr_mappings; i++) { + struct cxl_memdev *cxlmd =3D cxlr_pmem->mapping[i].cxlmd; + + put_device(&cxlmd->dev); + } + + kfree(cxlr_pmem); +} + +static const struct attribute_group *cxl_pmem_region_attribute_groups[] = =3D { + &cxl_base_attribute_group, + NULL, +}; + +const struct device_type cxl_pmem_region_type =3D { + .name =3D "cxl_pmem_region", + .release =3D cxl_pmem_region_release, + .groups =3D cxl_pmem_region_attribute_groups, +}; +bool is_cxl_pmem_region(struct device *dev) +{ + return dev->type =3D=3D &cxl_pmem_region_type; +} +EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL"); + +struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) +{ + if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), + "not a cxl_pmem_region device\n")) + return NULL; + return container_of(dev, struct cxl_pmem_region, dev); +} +EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL"); +static struct lock_class_key cxl_pmem_region_key; + +static int cxl_pmem_region_alloc(struct cxl_region *cxlr) +{ + struct cxl_region_params *p =3D &cxlr->params; + struct cxl_nvdimm_bridge *cxl_nvb; + struct device *dev; + int i; + + guard(rwsem_read)(&cxl_rwsem.region); + if (p->state !=3D CXL_CONFIG_COMMIT) + return -ENXIO; + + struct cxl_pmem_region *cxlr_pmem __free(kfree) =3D + kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets), GFP_KERNEL); + if (!cxlr_pmem) + return -ENOMEM; + + cxlr_pmem->hpa_range.start =3D p->res->start; + cxlr_pmem->hpa_range.end =3D p->res->end; + + /* Snapshot the region configuration underneath the cxl_rwsem.region */ + cxlr_pmem->nr_mappings =3D p->nr_targets; + for (i =3D 0; i < p->nr_targets; i++) { + struct cxl_endpoint_decoder *cxled =3D p->targets[i]; + struct cxl_memdev *cxlmd =3D cxled_to_memdev(cxled); + struct cxl_pmem_region_mapping *m =3D &cxlr_pmem->mapping[i]; + + /* + * Regions never span CXL root devices, so by definition the + * bridge for one device is the same for all. + */ + if (i =3D=3D 0) { + cxl_nvb =3D cxl_find_nvdimm_bridge(cxlmd->endpoint); + if (!cxl_nvb) + return -ENODEV; + cxlr->cxl_nvb =3D cxl_nvb; + } + m->cxlmd =3D cxlmd; + get_device(&cxlmd->dev); + m->start =3D cxled->dpa_res->start; + m->size =3D resource_size(cxled->dpa_res); + m->position =3D i; + } + + dev =3D &cxlr_pmem->dev; + device_initialize(dev); + lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); + device_set_pm_not_required(dev); + dev->parent =3D &cxlr->dev; + dev->bus =3D &cxl_bus_type; + dev->type =3D &cxl_pmem_region_type; + cxlr_pmem->cxlr =3D cxlr; + cxlr->cxlr_pmem =3D no_free_ptr(cxlr_pmem); + + return 0; +} + +static void cxlr_pmem_unregister(void *_cxlr_pmem) +{ + struct cxl_pmem_region *cxlr_pmem =3D _cxlr_pmem; + struct cxl_region *cxlr =3D cxlr_pmem->cxlr; + struct cxl_nvdimm_bridge *cxl_nvb =3D cxlr->cxl_nvb; + + /* + * Either the bridge is in ->remove() context under the device_lock(), + * or cxlr_release_nvdimm() is cancelling the bridge's release action + * for @cxlr_pmem and doing it itself (while manually holding the bridge + * lock). + */ + device_lock_assert(&cxl_nvb->dev); + cxlr->cxlr_pmem =3D NULL; + cxlr_pmem->cxlr =3D NULL; + device_unregister(&cxlr_pmem->dev); +} + +static void cxlr_release_nvdimm(void *_cxlr) +{ + struct cxl_region *cxlr =3D _cxlr; + struct cxl_nvdimm_bridge *cxl_nvb =3D cxlr->cxl_nvb; + + scoped_guard(device, &cxl_nvb->dev) { + if (cxlr->cxlr_pmem) + devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister, + cxlr->cxlr_pmem); + } + cxlr->cxl_nvb =3D NULL; + put_device(&cxl_nvb->dev); +} + +/** + * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge + * @cxlr: parent CXL region for this pmem region bridge device + * + * Return: 0 on success negative error code on failure. + */ +int devm_cxl_add_pmem_region(struct cxl_region *cxlr) +{ + struct cxl_pmem_region *cxlr_pmem; + struct cxl_nvdimm_bridge *cxl_nvb; + struct device *dev; + int rc; + + rc =3D cxl_pmem_region_alloc(cxlr); + if (rc) + return rc; + cxlr_pmem =3D cxlr->cxlr_pmem; + cxl_nvb =3D cxlr->cxl_nvb; + + dev =3D &cxlr_pmem->dev; + rc =3D dev_set_name(dev, "pmem_region%d", cxlr->id); + if (rc) + goto err; + + rc =3D device_add(dev); + if (rc) + goto err; + + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), + dev_name(dev)); + + scoped_guard(device, &cxl_nvb->dev) { + if (cxl_nvb->dev.driver) + rc =3D devm_add_action_or_reset(&cxl_nvb->dev, + cxlr_pmem_unregister, + cxlr_pmem); + else + rc =3D -ENXIO; + } + + if (rc) + goto err_bridge; + + /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */ + return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr); + +err: + put_device(dev); +err_bridge: + put_device(&cxl_nvb->dev); + cxlr->cxl_nvb =3D NULL; + return rc; +} + + diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index eeab091f043a..85c20a09246d 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -642,6 +642,9 @@ static ssize_t ctrl_show(struct device *dev, struct dev= ice_attribute *attr, case CXL_MEMCTRL_SYSRAM: desc =3D "sysram"; break; + case CXL_MEMCTRL_PMEM: + desc =3D "pmem"; + break; default: desc =3D ""; break; @@ -661,6 +664,10 @@ static ssize_t ctrl_store(struct device *dev, struct d= evice_attribute *attr, if ((rc =3D ACQUIRE_ERR(rwsem_write_kill, &rwsem))) return rc; =20 + /* PMEM only has one controller - the pmem controller */ + if (cxlr->mode =3D=3D CXL_PARTMODE_PMEM) + return -EBUSY; + if (p->state >=3D CXL_CONFIG_COMMIT) return -EBUSY; =20 @@ -2648,7 +2655,11 @@ static struct cxl_region *devm_cxl_add_region(struct= cxl_root_decoder *cxlrd, return cxlr; cxlr->mode =3D mode; cxlr->type =3D type; - cxlr->memctrl =3D CXL_MEMCTRL_NONE; + + if (mode =3D=3D CXL_PARTMODE_PMEM) + cxlr->memctrl =3D CXL_MEMCTRL_PMEM; + else + cxlr->memctrl =3D CXL_MEMCTRL_NONE; =20 dev =3D &cxlr->dev; rc =3D dev_set_name(dev, "region%d", id); @@ -2797,46 +2808,6 @@ static ssize_t delete_region_store(struct device *de= v, } DEVICE_ATTR_WO(delete_region); =20 -static void cxl_pmem_region_release(struct device *dev) -{ - struct cxl_pmem_region *cxlr_pmem =3D to_cxl_pmem_region(dev); - int i; - - for (i =3D 0; i < cxlr_pmem->nr_mappings; i++) { - struct cxl_memdev *cxlmd =3D cxlr_pmem->mapping[i].cxlmd; - - put_device(&cxlmd->dev); - } - - kfree(cxlr_pmem); -} - -static const struct attribute_group *cxl_pmem_region_attribute_groups[] = =3D { - &cxl_base_attribute_group, - NULL, -}; - -const struct device_type cxl_pmem_region_type =3D { - .name =3D "cxl_pmem_region", - .release =3D cxl_pmem_region_release, - .groups =3D cxl_pmem_region_attribute_groups, -}; - -bool is_cxl_pmem_region(struct device *dev) -{ - return dev->type =3D=3D &cxl_pmem_region_type; -} -EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL"); - -struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) -{ - if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), - "not a cxl_pmem_region device\n")) - return NULL; - return container_of(dev, struct cxl_pmem_region, dev); -} -EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL"); - struct cxl_poison_context { struct cxl_port *port; int part; @@ -3268,64 +3239,6 @@ static int region_offset_to_dpa_result(struct cxl_re= gion *cxlr, u64 offset, return -ENXIO; } =20 -static struct lock_class_key cxl_pmem_region_key; - -static int cxl_pmem_region_alloc(struct cxl_region *cxlr) -{ - struct cxl_region_params *p =3D &cxlr->params; - struct cxl_nvdimm_bridge *cxl_nvb; - struct device *dev; - int i; - - guard(rwsem_read)(&cxl_rwsem.region); - if (p->state !=3D CXL_CONFIG_COMMIT) - return -ENXIO; - - struct cxl_pmem_region *cxlr_pmem __free(kfree) =3D - kzalloc(struct_size(cxlr_pmem, mapping, p->nr_targets), GFP_KERNEL); - if (!cxlr_pmem) - return -ENOMEM; - - cxlr_pmem->hpa_range.start =3D p->res->start; - cxlr_pmem->hpa_range.end =3D p->res->end; - - /* Snapshot the region configuration underneath the cxl_rwsem.region */ - cxlr_pmem->nr_mappings =3D p->nr_targets; - for (i =3D 0; i < p->nr_targets; i++) { - struct cxl_endpoint_decoder *cxled =3D p->targets[i]; - struct cxl_memdev *cxlmd =3D cxled_to_memdev(cxled); - struct cxl_pmem_region_mapping *m =3D &cxlr_pmem->mapping[i]; - - /* - * Regions never span CXL root devices, so by definition the - * bridge for one device is the same for all. - */ - if (i =3D=3D 0) { - cxl_nvb =3D cxl_find_nvdimm_bridge(cxlmd->endpoint); - if (!cxl_nvb) - return -ENODEV; - cxlr->cxl_nvb =3D cxl_nvb; - } - m->cxlmd =3D cxlmd; - get_device(&cxlmd->dev); - m->start =3D cxled->dpa_res->start; - m->size =3D resource_size(cxled->dpa_res); - m->position =3D i; - } - - dev =3D &cxlr_pmem->dev; - device_initialize(dev); - lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); - device_set_pm_not_required(dev); - dev->parent =3D &cxlr->dev; - dev->bus =3D &cxl_bus_type; - dev->type =3D &cxl_pmem_region_type; - cxlr_pmem->cxlr =3D cxlr; - cxlr->cxlr_pmem =3D no_free_ptr(cxlr_pmem); - - return 0; -} - static void cxl_dax_region_release(struct device *dev) { struct cxl_dax_region *cxlr_dax =3D to_cxl_dax_region(dev); @@ -3358,92 +3271,6 @@ struct cxl_dax_region *to_cxl_dax_region(struct devi= ce *dev) } EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL"); =20 -static void cxlr_pmem_unregister(void *_cxlr_pmem) -{ - struct cxl_pmem_region *cxlr_pmem =3D _cxlr_pmem; - struct cxl_region *cxlr =3D cxlr_pmem->cxlr; - struct cxl_nvdimm_bridge *cxl_nvb =3D cxlr->cxl_nvb; - - /* - * Either the bridge is in ->remove() context under the device_lock(), - * or cxlr_release_nvdimm() is cancelling the bridge's release action - * for @cxlr_pmem and doing it itself (while manually holding the bridge - * lock). - */ - device_lock_assert(&cxl_nvb->dev); - cxlr->cxlr_pmem =3D NULL; - cxlr_pmem->cxlr =3D NULL; - device_unregister(&cxlr_pmem->dev); -} - -static void cxlr_release_nvdimm(void *_cxlr) -{ - struct cxl_region *cxlr =3D _cxlr; - struct cxl_nvdimm_bridge *cxl_nvb =3D cxlr->cxl_nvb; - - scoped_guard(device, &cxl_nvb->dev) { - if (cxlr->cxlr_pmem) - devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister, - cxlr->cxlr_pmem); - } - cxlr->cxl_nvb =3D NULL; - put_device(&cxl_nvb->dev); -} - -/** - * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge - * @cxlr: parent CXL region for this pmem region bridge device - * - * Return: 0 on success negative error code on failure. - */ -static int devm_cxl_add_pmem_region(struct cxl_region *cxlr) -{ - struct cxl_pmem_region *cxlr_pmem; - struct cxl_nvdimm_bridge *cxl_nvb; - struct device *dev; - int rc; - - rc =3D cxl_pmem_region_alloc(cxlr); - if (rc) - return rc; - cxlr_pmem =3D cxlr->cxlr_pmem; - cxl_nvb =3D cxlr->cxl_nvb; - - dev =3D &cxlr_pmem->dev; - rc =3D dev_set_name(dev, "pmem_region%d", cxlr->id); - if (rc) - goto err; - - rc =3D device_add(dev); - if (rc) - goto err; - - dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), - dev_name(dev)); - - scoped_guard(device, &cxl_nvb->dev) { - if (cxl_nvb->dev.driver) - rc =3D devm_add_action_or_reset(&cxl_nvb->dev, - cxlr_pmem_unregister, - cxlr_pmem); - else - rc =3D -ENXIO; - } - - if (rc) - goto err_bridge; - - /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */ - return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr); - -err: - put_device(dev); -err_bridge: - put_device(&cxl_nvb->dev); - cxlr->cxl_nvb =3D NULL; - return rc; -} - static int match_decoder_by_range(struct device *dev, const void *data) { const struct range *r1, *r2 =3D data; @@ -3929,26 +3756,18 @@ static int cxl_region_probe(struct device *dev) return rc; } =20 - switch (cxlr->mode) { - case CXL_PARTMODE_PMEM: - rc =3D devm_cxl_region_edac_register(cxlr); - if (rc) - dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=3D%d failed\n", - cxlr->id); - - return devm_cxl_add_pmem_region(cxlr); - case CXL_PARTMODE_RAM: - rc =3D devm_cxl_region_edac_register(cxlr); - if (rc) - dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=3D%d failed\n", - cxlr->id); - - return cxl_enable_memctrl(cxlr); - default: + if (cxlr->mode > CXL_PARTMODE_PMEM) { dev_dbg(&cxlr->dev, "unsupported region mode: %d\n", cxlr->mode); return -ENXIO; } + + rc =3D devm_cxl_region_edac_register(cxlr); + if (rc) + dev_dbg(&cxlr->dev, "CXL EDAC registration for region_id=3D%d failed\n", + cxlr->id); + + return cxl_enable_memctrl(cxlr); } =20 static struct cxl_driver cxl_region_driver =3D { diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index bb4f877b4e8f..c69d27a2e97d 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -509,12 +509,14 @@ enum cxl_partition_mode { * Auto - either BIOS-configured as SysRAM, or default to DAX * DAX - creates a dax_region controller for the cxl_region * SYSRAM - hotplugs the region directly as System RAM + * PMEM - persistent memory controller (nvdimm) */ enum cxl_memctrl_mode { CXL_MEMCTRL_NONE, CXL_MEMCTRL_AUTO, CXL_MEMCTRL_DAX, CXL_MEMCTRL_SYSRAM, + CXL_MEMCTRL_PMEM, }; =20 /* --=20 2.52.0 From nobody Sun Feb 8 18:08:45 2026 Received: from mail-qk1-f182.google.com (mail-qk1-f182.google.com [209.85.222.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 65F4F365A13 for ; Mon, 12 Jan 2026 16:36:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235773; cv=none; b=SZlmo9oj130UFBkhIUEhhvbwL6M8y+F6BvOUsxm+n+dICypJugMtLBP0atceqZGCe2f9m+7mORsiym0ubYdzJRgEErvh+fOe+2h9PwqfbSPBjVjPcMI1Xr/8n5AeNKDLD/LhVIa8rjYjFPynioG9VK1FcBU8IHRqV8kaPU5ogFs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235773; c=relaxed/simple; bh=Hk+57CNZUTJGonJIYyjaO83T2sgxKKuNO9rva+9k0kU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=caI0fKgNVJXFTOxZG+oKOdIncxmohOXdxRjTkIddpo5evbmmQVj1DYoSOdrkNaPzJ2Fyl/5jgHC9+DF9S1nZMb9aEXnGqge1FQciiUhz8+GuScbg6A3LjrRCoxEcpKcYntu+F+38vBIUDIob5OCx9K677VaeNdsmRUg9BYkIPZs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=lTM1WMAy; arc=none smtp.client-ip=209.85.222.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="lTM1WMAy" Received: by mail-qk1-f182.google.com with SMTP id af79cd13be357-8b21fc25ae1so722360385a.1 for ; Mon, 12 Jan 2026 08:36:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1768235765; x=1768840565; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YcpN/uYsH1Dv10pXdsdEXhAfmqLRknf8ikqAWK0teK0=; b=lTM1WMAymLLsAU1uM/ecjXdIyAmoi1vU04gg1HV5qNma0m5/kvL/ObtYUxfeDzSZPK d9lARv4XFx2bm52IftjBeXd4QhOPfFvd3OsDPhtoniX0T9hLxR2uGjJhUkwX6FPpb1ty ny1ElI8GgRJ+nbmXwj11ECV/KAOPcYR7cffrNVwKA3hwrOSOjp1qmvh7/bF4N442OWgw mwLt9Q73jNMIWQ4AmKC4GeoHeqGo8VIga07hZmdpu4eDCZkfB1ROSojarLglQENBxEW7 NXordVxCVnQO7tqRWc5VLBhDuhK4GRBhwN0kyBFDzF4SSc5CnGJIzx8MGPThN+fYoQ1O p/aA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768235765; x=1768840565; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=YcpN/uYsH1Dv10pXdsdEXhAfmqLRknf8ikqAWK0teK0=; b=C+I1Phe53W/CjR1VQhGtM9v/fbu4sYLRh2ZB4bO/RyPbCFOH9M4HdeWbsj3UZWtuYG hl6UxuMebb5aljQyopN/V/Q8rANFVOppTVe6rr0+ON+Ozbz+4EloVZ5u4VATRSkCBDYz Gg21BoS9CubIQDjANYan4CGtwq9eInSr3vv3COh8J80bLAiYZcka4UY169iJIkK6Ia/D eLVqKY0PaWlWyc/fktsVz/yy/mRAlssoCQOi0DVgIGOk1CA9oKi59JUCoyuAXBS7Hxt2 ndUvqcTYTvLZ6jW4cViqmMz5VTabxzMcU5wKFS0K0A8DTAM52OtGAaWKffV5blbOxq7J GTRg== X-Gm-Message-State: AOJu0Yz9s5WezR6uhOou8Clw5qKdjSxsZMFx4qx7XjWMO4vhWfZ0KY8C KUzrnsy0uEqblv83pJYOYuKliaTRnTzTEUZrNfpsG0kNRTD7iOLmshDg+kcs1h12HNSP818rgZ0 QUv/V X-Gm-Gg: AY/fxX45VW/4NU7uEt6KuoaOCbXWLhQsfWjJtnSvqkBJkShJxrQFXiEYljHvJ4u9zw0 b3i3fBLFO59LQeBOTG1B5tjbkFhs3Qp0iEQvuFoFg8HxGN/N+zXkO/kqR2DVCupZiRKfzPq40F0 bd+P5S0RsZIgCdz6QIdIgIn/LBs2Myr4I+O14zb+ZKopsDxZAl114yvALrYqvOTbideMd921voy jHlPCGzpOPYZ3NJ8oY9tgyVRPOYxrjNYfYh+8fNT8i4shqedVkZ9mG0TtWOqFa+nmT/gtlU59ge 3AUE6UvwanUJZquXEkGrluWtG4TYIHlKh5mz8Fs6dsq2rAYnh352FdgeqWyRj/tcTrS+gfYiTF6 A3M2tSpEOi5TBNnC57L0MOL7XdNjTeCg4JEBNFFFj27BTd7XZsKY3Yy8VvsK2C6iV+U2FBQz9Ce wsTk4d5TeDhiCvpZ+zVlJuS43BPlCm1fNszK/rNY/NEcrAwCVafXVB4CsrddZra8TUz/fiyVdW5 gE= X-Google-Smtp-Source: AGHT+IFRPZbBbTCf/WOGz6KEi6yYu6VUkTSJSbTA6OixgptEep5NhZocpcXBxWuFQvIu25RSebcYsw== X-Received: by 2002:a05:620a:c53:b0:8b2:6a35:9635 with SMTP id af79cd13be357-8c38936898fmr2409334785a.6.1768235764551; Mon, 12 Jan 2026 08:36:04 -0800 (PST) Received: from gourry-fedora-PF4VCD3F.lan (pool-96-255-20-138.washdc.ftas.verizon.net. [96.255.20.138]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8c37f4a7962sm1489152685a.11.2026.01.12.08.36.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jan 2026 08:36:04 -0800 (PST) From: Gregory Price To: linux-cxl@vger.kernel.org Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, dave@stgolabs.net, jonathan.cameron@huawei.com, dave.jiang@intel.com, alison.schofield@intel.com, vishal.l.verma@intel.com, ira.weiny@intel.com, dan.j.williams@intel.com Subject: [PATCH 4/6] cxl: add CONFIG_CXL_REGION_CTRL_AUTO_* build config options Date: Mon, 12 Jan 2026 11:35:12 -0500 Message-ID: <20260112163514.2551809-5-gourry@gourry.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260112163514.2551809-1-gourry@gourry.net> References: <20260112163514.2551809-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" To give users the option to have the auto-behavior of memory to default to SYSRAM, provide a switch. The default is still recommended to be DAX in case of multiple devices being added to the system, but this provides simpler systems a path to use the sysram controller for systems already configured with auto-regions. Signed-off-by: Gregory Price --- drivers/cxl/Kconfig | 32 ++++++++++++++++++++++++++++++ drivers/cxl/core/memctrl/memctrl.c | 2 ++ drivers/cxl/cxl.h | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index 48b7314afdb8..5aed1524f8f1 100644 --- a/drivers/cxl/Kconfig +++ b/drivers/cxl/Kconfig @@ -211,6 +211,38 @@ config CXL_REGION =20 If unsure say 'y' =20 +choice + prompt "CXL Region Auto Control Mode" + depends on CXL_REGION + default CXL_REGION_CTRL_AUTO_DAX + help + Select the default controller for CXL regions when ctrl mode is + set to 'auto'. This determines how CXL memory regions are exposed + to the system when no explicit control mode is specified. + +config CXL_REGION_CTRL_AUTO_DAX + bool "DAX" + help + When a CXL region's control mode is 'auto', create a DAX region + controller. This allows fine-grained control over the memory region + through the DAX subsystem, and the region can later be converted to + System RAM via daxctl. + + This is the default and recommended option for most use cases. + +config CXL_REGION_CTRL_AUTO_SYSRAM + bool "System RAM" + help + When a CXL region's control mode is 'auto', hotplug the region + directly as System RAM. This makes the CXL memory immediately + available to the kernel's memory allocator without requiring + additional userspace configuration. + + Select this if you want CXL memory to be automatically available + as regular system memory. + +endchoice + config CXL_REGION_INVALIDATION_TEST bool "CXL: Region Cache Management Bypass (TEST)" depends on CXL_REGION diff --git a/drivers/cxl/core/memctrl/memctrl.c b/drivers/cxl/core/memctrl/= memctrl.c index 1b661465bdeb..cb6c37f4c0ee 100644 --- a/drivers/cxl/core/memctrl/memctrl.c +++ b/drivers/cxl/core/memctrl/memctrl.c @@ -31,6 +31,8 @@ int cxl_enable_memctrl(struct cxl_region *cxlr) p->res->start, p->res->end, cxlr, is_system_ram) > 0) return 0; + if (IS_ENABLED(CONFIG_CXL_REGION_CTRL_AUTO_SYSRAM)) + return devm_cxl_add_sysram_region(cxlr); return devm_cxl_add_dax_region(cxlr); case CXL_MEMCTRL_DAX: return devm_cxl_add_dax_region(cxlr); diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index c69d27a2e97d..1dae6fe4f70c 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -506,7 +506,7 @@ enum cxl_partition_mode { /* * Memory Controller modes: * None - No controller selected - * Auto - either BIOS-configured as SysRAM, or default to DAX + * Auto - Auto-select based on BIOS, boot, and build configs. * DAX - creates a dax_region controller for the cxl_region * SYSRAM - hotplugs the region directly as System RAM * PMEM - persistent memory controller (nvdimm) --=20 2.52.0 From nobody Sun Feb 8 18:08:45 2026 Received: from mail-qk1-f181.google.com (mail-qk1-f181.google.com [209.85.222.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7B035366DBB for ; Mon, 12 Jan 2026 16:36:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235770; cv=none; b=MclzoFUjfT4x7rYU3Yn3KUjzciBpqEO/eaU2S26u1lCaYRtU42YjmlKhTL5mx6Vw6f8UhMLIB+nQKtKh5PIrLAfzM5W9YKLbIu7zOjlNjpxuWkIzV1jp7fP/GcoGuiokhgRNPwLDbdD2Qoia0215hXkjc7R4lr0a4heNo3BmW7I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235770; c=relaxed/simple; bh=J5fWXWgfUgNARS1URmEQO+2W2oErSS+uXHLDhl4DgLs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fxBixFv+01cFXxpgukXLzqGB247gPMAHX3m54sRe8cZKOymYoskVAFMqAchfpDXARRL0NYBcGzTZsIzdqUgZpGVE2JtnAYbfeGV4KAxg2vz9mdAufdb4tFTb+njzD8r6zhyUOluEpq8sGaDRKyN5nWAgbKzgU45LyJXx7UPWS1g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=btXf4B5N; arc=none smtp.client-ip=209.85.222.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="btXf4B5N" Received: by mail-qk1-f181.google.com with SMTP id af79cd13be357-8b2d32b9777so997276385a.2 for ; Mon, 12 Jan 2026 08:36:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1768235767; x=1768840567; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kYljDHrhzse122IhNVBkcFsqZorvBX2rkO3ziE1OCKg=; b=btXf4B5N0xLmVJYyXRgkMD+46be0ZYK6RnGfyYOP58ZXDhTe30Yp8sVQvM/BU89RQ6 YiZgoONf+xdfBOCPGJNsqI4MUW0pG8fkvRdR3kxuyT+lNkS7cX1yacKvYMggKrwc3WeF oXHqQxqlsoZYpl3Ic7wW9NtbkN+7VVyGxYmxcmg9wBWASPYZNCLd6A1S3/HrGN16Yfu4 4chYsNLwXuks7Y68h0Tj+4Uvu0i2TTfx7FCJvPfNM67N2CMcF144B1LW92L9qev1BZ+O nvu5yjsyM9XDEMZhanoclekRIR8Jah19Xm58nYJIlP0xXZbmYmxZtCQOcQgk3V6XPk4M Q/zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768235767; x=1768840567; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kYljDHrhzse122IhNVBkcFsqZorvBX2rkO3ziE1OCKg=; b=STBfWOUXO1uP5gpeEgrZQ669f1jGaKQd/1tTvQhIQaVDLgja3Sgh3GWknPNnKry7Az 6CTAsCiVgK1NpFg9athXhvaFv8eSBoKLPq4TgkKRrXLdUvYPqiDFGiu2aRJ8qnsPlobb XVPEKJ9wK4UQd4RPt6t16vSy8ni+PvRMBL1n4EbgwvNBLX6y1zYQS9bl2KQabMiUfkix CX6USY8D9iJCcDSEXaOpXBLjrXxkDE1cW0Bwz6JbOoa0h63Gt8GOJo6oFA0o9nYssMv/ udNmzoNkXxssrALdAmMNT93LRBvaOGetGo3kS77njfS7eETYYPlZxX+QAlnzc7nuvxlx y3Sg== X-Gm-Message-State: AOJu0Yzop+V8/ADXf/XgpZBOFXovTMTykojn4HS1Ar1+ACL21LsAIvM/ fciIoKt6UmDtR4x2+LCbnA7og+BMcLNU2vCjkPn6O00E9J4SDGmJnIDBzjGo0oVf2Mc= X-Gm-Gg: AY/fxX6rYUr6dfGVTBjTqE1naiTCMhFQtcov5pWhH1a2LcsvibWXgyvo8Esttj/smQF 0tWdH9w4SuACQsV3OGbo+HHsmtNDbmsrrV2F2WJEpFJN4UIZLe40zWi7PehUgLRvis1JNennEQn 5m/ZBuQuuqpnM1kX064mEYVjaq2IiO+WOAjSdhX7nhpbE/305E3rVlgBVrqwMAA0r2WTwFsgeLH X+Q58Jd4dp3BeMjSk9nbowYqLjpy1LDzbMkPo9GOxufyH2a9ix+3QKgx/BCzNZ60JPudc7nliGa HNF6X+ovYkNoJL7sOB83RRQlna5YJ+pyjBrW/sxj+ncKrrP56E3zDfUo7X9K/FgLiYgXSrJseMB h1e0eGbRQ6w4WRuAXsuQG13fGdRZwy07VKuAyPKraSrjMDjtWgPLDGuQWqGZhK8jaSaQcDjIU7i PDBSqf4WlxtR9gbEdWXlMH6ut4S+EMG/YMVYleFSuLYc86BHCFX8Y7SKTQQ37SeqSvUO7evoPYr jLFWliHNSt5QQ== X-Google-Smtp-Source: AGHT+IEk7U/eiL6d2BgL0qXdS/KjLZjfHviCFNTvOTZ2x8tQOJI7E67CBhbzaabCC7zoXHNpM66EZw== X-Received: by 2002:a05:620a:298c:b0:89e:c659:3f99 with SMTP id af79cd13be357-8c38937959amr2642286585a.6.1768235767303; Mon, 12 Jan 2026 08:36:07 -0800 (PST) Received: from gourry-fedora-PF4VCD3F.lan (pool-96-255-20-138.washdc.ftas.verizon.net. [96.255.20.138]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8c37f4a7962sm1489152685a.11.2026.01.12.08.36.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jan 2026 08:36:06 -0800 (PST) From: Gregory Price To: linux-cxl@vger.kernel.org Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, dave@stgolabs.net, jonathan.cameron@huawei.com, dave.jiang@intel.com, alison.schofield@intel.com, vishal.l.verma@intel.com, ira.weiny@intel.com, dan.j.williams@intel.com Subject: [PATCH 5/6] cxl: add CXL_REGION_SYSRAM_DEFAULT_* build options Date: Mon, 12 Jan 2026 11:35:13 -0500 Message-ID: <20260112163514.2551809-6-gourry@gourry.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260112163514.2551809-1-gourry@gourry.net> References: <20260112163514.2551809-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" DEFAULT_OFFLINE: Blocks will be offline after being created. DEFAULT_ONLINE: Blocks will be onlined in ZONE_MOVABLE DEFAULT_ONLINE_NORMAL: Blocks will be onliend in ZONE_NORMAL. This prevents users from having to use the MHP auto-online build config, which may cause misbehaviors with other devices hotplugging memory. Signed-off-by: Gregory Price --- drivers/cxl/Kconfig | 40 ++++++++++ drivers/cxl/core/memctrl/sysram_region.c | 94 ++++++++++++++++++------ 2 files changed, 110 insertions(+), 24 deletions(-) diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index 5aed1524f8f1..3e087c9d5ea7 100644 --- a/drivers/cxl/Kconfig +++ b/drivers/cxl/Kconfig @@ -243,6 +243,46 @@ config CXL_REGION_CTRL_AUTO_SYSRAM =20 endchoice =20 +choice + prompt "CXL SYSRAM Auto Online Mode" + depends on CXL_REGION + default CXL_REGION_SYSRAM_DEFAULT_OFFLINE + help + Select whether CXL memory hotplugged as System RAM should be + automatically onlined and in which zone. This applies when the + region controller is set to SYSRAM (either explicitly or via + the auto control mode). + +config CXL_REGION_SYSRAM_DEFAULT_OFFLINE + bool "Offline" + help + Leave the memory offline after hotplug. The memory must be + manually onlined via sysfs or other mechanisms before it can + be used by the system. + + This is the default and most conservative option. + +config CXL_REGION_SYSRAM_DEFAULT_ONLINE + bool "Online (Movable)" + help + Automatically online the memory as ZONE_MOVABLE after hotplug. + ZONE_MOVABLE memory can be used for user pages and is eligible + for memory hotremove, but cannot be used for kernel allocations. + + Select this for memory that may need to be hotremoved later. + +config CXL_REGION_SYSRAM_DEFAULT_ONLINE_NORMAL + bool "Online (Normal)" + help + Automatically online the memory as ZONE_NORMAL after hotplug. + ZONE_NORMAL memory can be used for all allocations including + kernel allocations, but may not be hotremovable. + + Select this for maximum memory utilization when hotremove is + not required. + +endchoice + config CXL_REGION_INVALIDATION_TEST bool "CXL: Region Cache Management Bypass (TEST)" depends on CXL_REGION diff --git a/drivers/cxl/core/memctrl/sysram_region.c b/drivers/cxl/core/me= mctrl/sysram_region.c index a7570c8a54e1..2e2d9b59a725 100644 --- a/drivers/cxl/core/memctrl/sysram_region.c +++ b/drivers/cxl/core/memctrl/sysram_region.c @@ -129,12 +129,69 @@ static int offline_memory_block_cb(struct memory_bloc= k *mem, void *arg) return *rc; } =20 +static int cxl_sysram_online_memory(struct range *range, int online_type) +{ + struct online_memory_cb_arg cb_arg =3D { + .online_type =3D online_type, + .rc =3D 0, + }; + int rc; + + rc =3D walk_memory_blocks(range->start, range_len(range), + &cb_arg, online_memory_block_cb); + if (!rc) + rc =3D cb_arg.rc; + + return rc; +} + +static int cxl_sysram_offline_memory(struct range *range) +{ + int offline_rc =3D 0; + int rc; + + rc =3D walk_memory_blocks(range->start, range_len(range), + &offline_rc, offline_memory_block_cb); + if (!rc) + rc =3D offline_rc; + + return rc; +} + +static int cxl_sysram_auto_online(struct device *dev, struct range *range) +{ + int online_type; + int rc; + + if (IS_ENABLED(CONFIG_CXL_REGION_SYSRAM_DEFAULT_OFFLINE)) + return 0; + + if (IS_ENABLED(CONFIG_CXL_REGION_SYSRAM_DEFAULT_ONLINE)) + online_type =3D MMOP_ONLINE_MOVABLE; + else if (IS_ENABLED(CONFIG_CXL_REGION_SYSRAM_DEFAULT_ONLINE_NORMAL)) + online_type =3D MMOP_ONLINE_KERNEL; + else + online_type =3D MMOP_ONLINE_MOVABLE; + + rc =3D lock_device_hotplug_sysfs(); + if (rc) + return rc; + + rc =3D cxl_sysram_online_memory(range, online_type); + + unlock_device_hotplug(); + + if (rc) + dev_warn(dev, "auto-online failed: %d\n", rc); + + return rc; +} + static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct cxl_region *cxlr =3D to_cxl_region(dev); - struct online_memory_cb_arg cb_arg; struct range range; int rc; =20 @@ -149,30 +206,14 @@ static ssize_t state_store(struct device *dev, if (rc) return rc; =20 - if (sysfs_streq(buf, "online")) { - cb_arg.online_type =3D MMOP_ONLINE_MOVABLE; - cb_arg.rc =3D 0; - rc =3D walk_memory_blocks(range.start, range_len(&range), - &cb_arg, online_memory_block_cb); - if (!rc) - rc =3D cb_arg.rc; - } else if (sysfs_streq(buf, "online_normal")) { - cb_arg.online_type =3D MMOP_ONLINE; - cb_arg.rc =3D 0; - rc =3D walk_memory_blocks(range.start, range_len(&range), - &cb_arg, online_memory_block_cb); - if (!rc) - rc =3D cb_arg.rc; - } else if (sysfs_streq(buf, "offline")) { - int offline_rc =3D 0; - - rc =3D walk_memory_blocks(range.start, range_len(&range), - &offline_rc, offline_memory_block_cb); - if (!rc) - rc =3D offline_rc; - } else { + if (sysfs_streq(buf, "online")) + rc =3D cxl_sysram_online_memory(&range, MMOP_ONLINE_MOVABLE); + else if (sysfs_streq(buf, "online_normal")) + rc =3D cxl_sysram_online_memory(&range, MMOP_ONLINE); + else if (sysfs_streq(buf, "offline")) + rc =3D cxl_sysram_offline_memory(&range); + else rc =3D -EINVAL; - } =20 unlock_device_hotplug(); =20 @@ -332,6 +373,10 @@ int devm_cxl_add_sysram_region(struct cxl_region *cxlr) dev_dbg(dev, "%s: added %llu bytes as System RAM\n", dev_name(dev), (unsigned long long)total_len); =20 + rc =3D cxl_sysram_auto_online(dev, &range); + if (rc) + goto err_auto_online; + dev_set_drvdata(dev, data); rc =3D devm_device_add_group(dev, &cxl_sysram_region_group); if (rc) @@ -341,6 +386,7 @@ int devm_cxl_add_sysram_region(struct cxl_region *cxlr) =20 err_add_group: dev_set_drvdata(dev, NULL); +err_auto_online: /* if this fails, memory cannot be removed from the system until reboot */ remove_memory(range.start, range_len(&range)); err_add_memory: --=20 2.52.0 From nobody Sun Feb 8 18:08:45 2026 Received: from mail-qk1-f194.google.com (mail-qk1-f194.google.com [209.85.222.194]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 48BB0368283 for ; Mon, 12 Jan 2026 16:36:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.194 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235792; cv=none; b=pGyRmT9AO6CWK8fKPp6X7Gk5MHj8UtZYLBdEhb4uW4k1x04K/mOMAHl7F2sgywgClvnZONfx76FkHV4Jxbmxr1vx7DhUuUFc3JfMffkYLsceTtt9/WeBgt/s7NHBWX0X6lsJm+eUkWd6OsFS7WkD84zPJs1V+Azn+/oT0Nasngg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768235792; c=relaxed/simple; bh=a+1Vckg0Y0ApTL8FqKoFrVt3+WTxV5v3qbvQIZ1qTig=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=J4l4Z4H+szFY6iE8xLejwuP3X0HhaANRzcYV5TGY7AzSKhzk+heAcHZUZEriZTlQ4EB/yvYFHZCQNGufMiMblhjJRydsLHyHIC19znkMSfCPXEfYwZDR0r1ijhK+Hxbgs8R6kGAdVrWV+fVjiazP0LN1Q69iOHQyfycuXppAvSs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=tokGa3cq; arc=none smtp.client-ip=209.85.222.194 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="tokGa3cq" Received: by mail-qk1-f194.google.com with SMTP id af79cd13be357-8c0f15e8247so931674085a.3 for ; Mon, 12 Jan 2026 08:36:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1768235770; x=1768840570; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Z3I/G0EhDE3+slCtVTNXpav+ECSkZZ3ZPq9IZrnfZqI=; b=tokGa3cqkKATkXwkOfcCW+gELPPylqgG5Wk/Dbnh3lOG1wEHdCnUvyqTRAhLpM10Ik YcWdebp+Gr8wlP6L0VNPb/lPjjKTcMUZFn6UpwbUKU2yDeSG6kFPBiyfH+a6h/STp4bo hDGtyS/W+SXkLpq2awWnCb76lUBfHq4tHlJilok4DNl+M3/ejYIWUIDMy9knTb7aglxa h7X/AGTSZubQ8MaxPWrefF+pCBIIgrivGDrQ/sHSOleV98vDzw5bFltLPiZPgniYIoXR wi490uSe/Pgi1bbLHegGHwLnZIATXlcVPiaLx09J8sFRmFxVRc1iua/yWRl1iS4Zboh+ J70g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768235770; x=1768840570; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Z3I/G0EhDE3+slCtVTNXpav+ECSkZZ3ZPq9IZrnfZqI=; b=PdpMY1yWY3m0n/KhbEUVxj/TBQX7f2T3VOc5D53AoCgRsEI8rmIKf/hu8A9axbiFop R0wVPZeCkju0jnE06wYFEth2bTdUpnVDVhmZ9tfD3nYeCmH+5IDE2O/VE+HM9f/QJnOY y9jMTSeNnILoF+xW8U2cX16OPqvx4hvaR7xYXYsmBK3+pPNLYXAZAutso6dDjVDYFMRE U2HMB87WGeeM1OCqK6aNlLNEan36o9uqEZrKzFY0T30QVcTgYBjy2kaloy2P+cAbOuyo tsFBYw4WiraxEHu8IGBcw5pxpqBH6IVj/PltejzSymToU0HfhaHMrYAu6Wkmo1StcWEh zOnQ== X-Gm-Message-State: AOJu0YwMTdJjQNj/hZROUnSn+xfwcB7dMttXUbIHpByuOdUrhCTE8uuG WXQUs4UzxzHaRPrmQj85WmBiaV8vjmhpgwCrVtMeupALX06+cub9UmUROMGyXK0PvHQ= X-Gm-Gg: AY/fxX5gEy3G3QqJbC6sPcT/uYOzqwYzqOz7pwm6sSl+DbbLSQZ4swQrXGHFmmkcz4J QHlxul7/UH73nndLt1RnuPxZdbTcWnPrcuBDThZbAq9iabxXvDGZodAhcngbf3+6DI54mTePaTm d7VcX5I5j30nGx6S9LwBgTr4lO5P7/9Zc7+/VOvYZQwLqgVF3gebbuioF0kmIrfSFOlnxLi5Snr 5OPepkuQnxCQPbhU9jxggnBcC3wDukEnPsCyv3dq1dg7jE0blsFAFj/CrMWfePUL+crcW4XbLCn r+fy00RlPEaTflrTYjhvFAb4OydzpgdOPcBfy5ahElbpcWGGuTqRF8umQd4z7QVzpfVDTJtkUO1 JrV2gti8HaoUEBR4awiILEGYAHJCyV1ua/eC1DJSESLtBvFzxTVNTcmVT00Zky2l0D+ksdLHQyg XTdH+nodg9BLoMtzPikWfTL5+FryuO5soG0Z+xvonUIC/zqcJl76tVe+4BqjI7dOd1O+w6TX81f YsHOgAQ6MRdbw== X-Google-Smtp-Source: AGHT+IGBEMDopMAuDTiFSKsXIeUiEcVCCbmKNNb05D/QQ5LDaD26f9NZnIUwZpZW1w2CXBV00G3MkA== X-Received: by 2002:a05:620a:440d:b0:8ba:41b2:da01 with SMTP id af79cd13be357-8c389411960mr2326124585a.71.1768235770092; Mon, 12 Jan 2026 08:36:10 -0800 (PST) Received: from gourry-fedora-PF4VCD3F.lan (pool-96-255-20-138.washdc.ftas.verizon.net. [96.255.20.138]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8c37f4a7962sm1489152685a.11.2026.01.12.08.36.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jan 2026 08:36:09 -0800 (PST) From: Gregory Price To: linux-cxl@vger.kernel.org Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, dave@stgolabs.net, jonathan.cameron@huawei.com, dave.jiang@intel.com, alison.schofield@intel.com, vishal.l.verma@intel.com, ira.weiny@intel.com, dan.j.williams@intel.com, David Hildenbrand , Hannes Reinecke Subject: [PATCH 6/6] cxl/sysram: disallow onlining in ZONE_NORMAL if state is movable only Date: Mon, 12 Jan 2026 11:35:14 -0500 Message-ID: <20260112163514.2551809-7-gourry@gourry.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260112163514.2551809-1-gourry@gourry.net> References: <20260112163514.2551809-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" If state is set to online (default to ZONE_MOVABLE), the user intends for this memory to either refuse non-movable allocations, and/or intends to preserve the hot-unpluggability of this memory. However, any admin can write `offline` and `online` to the memory block controller and bring that memory online in ZONE_NORMAL. Register a memory_notify callback that disallows onlining the block into ZONE_NORMAL if the default state of the controller is ZONE_MOVABLE. If an actor attempts to online the block into ZONE_NORMAL, it will fail, but if it attempts to online into either NORMAL or MOVABLE, only MOVABLE will be allowed and it will succeed. Suggested-by: David Hildenbrand Suggested-by: Hannes Reinecke Link: https://lore.kernel.org/linux-mm/39533aa8-ca78-41a8-b005-9202ce53e3ae= @kernel.org/ Signed-off-by: Gregory Price --- drivers/cxl/core/memctrl/sysram_region.c | 138 +++++++++++++++++++++-- 1 file changed, 127 insertions(+), 11 deletions(-) diff --git a/drivers/cxl/core/memctrl/sysram_region.c b/drivers/cxl/core/me= mctrl/sysram_region.c index 2e2d9b59a725..71e39d725dc5 100644 --- a/drivers/cxl/core/memctrl/sysram_region.c +++ b/drivers/cxl/core/memctrl/sysram_region.c @@ -2,6 +2,7 @@ /* Copyright(c) 2026 Meta Inc. All rights reserved. */ #include #include +#include #include #include #include @@ -23,6 +24,14 @@ struct cxl_sysram_data { const char *res_name; int mgid; struct resource *res; + struct range range; + struct notifier_block memory_notifier; + /* + * Last online type requested by user via state sysfs or auto-online. + * Used to enforce zone consistency when memory blocks are onlined. + * MMOP_OFFLINE means no online preference has been set yet. + */ + int last_online_type; }; =20 static DEFINE_MUTEX(cxl_memory_type_lock); @@ -158,7 +167,58 @@ static int cxl_sysram_offline_memory(struct range *ran= ge) return rc; } =20 -static int cxl_sysram_auto_online(struct device *dev, struct range *range) +/* + * Memory notifier callback to enforce zone consistency. + * + * When the user (or auto-online) requests memory to be onlined into + * ZONE_MOVABLE, reject any subsequent attempts to online memory blocks + * from this region into a different zone (e.g., ZONE_NORMAL). This preven= ts + * accidental zone mixing which could lead to memory fragmentation and + * offlining failures. + */ +static int cxl_sysram_memory_notify_cb(struct notifier_block *nb, + unsigned long action, void *arg) +{ + struct cxl_sysram_data *data =3D container_of(nb, struct cxl_sysram_data, + memory_notifier); + struct memory_notify *mhp =3D arg; + unsigned long start_phys =3D PFN_PHYS(mhp->start_pfn); + unsigned long size =3D PFN_PHYS(mhp->nr_pages); + struct page *page; + + if (action !=3D MEM_GOING_ONLINE) + return NOTIFY_DONE; + + /* Check if this memory block overlaps with our region */ + if (start_phys + size <=3D data->range.start || + start_phys > data->range.end) + return NOTIFY_DONE; + + /* + * If no online preference has been set (MMOP_OFFLINE), allow any zone. + * Also allow if the preference wasn't for ZONE_MOVABLE. + */ + if (data->last_online_type !=3D MMOP_ONLINE_MOVABLE) + return NOTIFY_DONE; + + /* + * The zone has already been assigned to the pages at this point + * via move_pfn_range_to_zone() before MEM_GOING_ONLINE is sent. + * Check if it's ZONE_MOVABLE as expected. + */ + page =3D pfn_to_page(mhp->start_pfn); + + if (!is_zone_movable_page(page)) { + pr_warn("CXL sysram: rejecting online to non-movable zone for range %#lx= -%#lx (expected ZONE_MOVABLE)\n", + start_phys, start_phys + size - 1); + return NOTIFY_BAD; + } + + return NOTIFY_OK; +} + +static int cxl_sysram_auto_online(struct device *dev, struct range *range, + struct cxl_sysram_data *data) { int online_type; int rc; @@ -173,6 +233,9 @@ static int cxl_sysram_auto_online(struct device *dev, s= truct range *range) else online_type =3D MMOP_ONLINE_MOVABLE; =20 + /* Record the auto-online type for zone enforcement */ + data->last_online_type =3D online_type; + rc =3D lock_device_hotplug_sysfs(); if (rc) return rc; @@ -187,17 +250,43 @@ static int cxl_sysram_auto_online(struct device *dev,= struct range *range) return rc; } =20 +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxl_sysram_data *data; + + data =3D dev_get_drvdata(dev); + if (!data) + return -ENODEV; + + switch (data->last_online_type) { + case MMOP_ONLINE_MOVABLE: + return sysfs_emit(buf, "online\n"); + case MMOP_ONLINE_KERNEL: + return sysfs_emit(buf, "online_normal\n"); + case MMOP_OFFLINE: + default: + return sysfs_emit(buf, "offline\n"); + } +} + static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct cxl_region *cxlr =3D to_cxl_region(dev); + struct cxl_sysram_data *data; struct range range; + int online_type =3D MMOP_OFFLINE; int rc; =20 if (!cxlr) return -ENODEV; =20 + data =3D dev_get_drvdata(dev); + if (!data) + return -ENODEV; + rc =3D cxl_sysram_range(cxlr, &range); if (rc) return rc; @@ -206,23 +295,30 @@ static ssize_t state_store(struct device *dev, if (rc) return rc; =20 - if (sysfs_streq(buf, "online")) - rc =3D cxl_sysram_online_memory(&range, MMOP_ONLINE_MOVABLE); - else if (sysfs_streq(buf, "online_normal")) - rc =3D cxl_sysram_online_memory(&range, MMOP_ONLINE); - else if (sysfs_streq(buf, "offline")) + if (sysfs_streq(buf, "online")) { + online_type =3D MMOP_ONLINE_MOVABLE; + rc =3D cxl_sysram_online_memory(&range, online_type); + } else if (sysfs_streq(buf, "online_normal")) { + online_type =3D MMOP_ONLINE; + rc =3D cxl_sysram_online_memory(&range, online_type); + } else if (sysfs_streq(buf, "offline")) { rc =3D cxl_sysram_offline_memory(&range); - else + } else { rc =3D -EINVAL; + } =20 unlock_device_hotplug(); =20 if (rc) return rc; =20 + /* Record the online type for zone enforcement on success */ + if (online_type !=3D MMOP_OFFLINE) + data->last_online_type =3D online_type; + return len; } -static DEVICE_ATTR_WO(state); +static DEVICE_ATTR_RW(state); =20 static ssize_t hotplug_store(struct device *dev, struct device_attribute *attr, @@ -274,6 +370,11 @@ static void cxl_sysram_unregister(void *_data) .end =3D data->res->end }; =20 + unregister_memory_notifier(&data->memory_notifier); + + range.start =3D data->res->start; + range.end =3D data->res->end; + /* We have one shot for removal, otherwise it's stuck til reboot */ if (!offline_and_remove_memory(range.start, range_len(&range))) { remove_resource(data->res); @@ -334,6 +435,10 @@ int devm_cxl_add_sysram_region(struct cxl_region *cxlr) goto err_data; } =20 + /* Initialize range and online type tracking */ + data->range =3D range; + data->last_online_type =3D MMOP_OFFLINE; + data->res_name =3D kstrdup(dev_name(dev), GFP_KERNEL); if (!data->res_name) { rc =3D -ENOMEM; @@ -373,11 +478,20 @@ int devm_cxl_add_sysram_region(struct cxl_region *cxl= r) dev_dbg(dev, "%s: added %llu bytes as System RAM\n", dev_name(dev), (unsigned long long)total_len); =20 - rc =3D cxl_sysram_auto_online(dev, &range); + /* Set drvdata early so auto_online can access it */ + dev_set_drvdata(dev, data); + + /* Register memory notifier for zone enforcement */ + data->memory_notifier.notifier_call =3D cxl_sysram_memory_notify_cb; + data->memory_notifier.priority =3D CXL_CALLBACK_PRI; + rc =3D register_memory_notifier(&data->memory_notifier); + if (rc) + goto err_notifier; + + rc =3D cxl_sysram_auto_online(dev, &range, data); if (rc) goto err_auto_online; =20 - dev_set_drvdata(dev, data); rc =3D devm_device_add_group(dev, &cxl_sysram_region_group); if (rc) goto err_add_group; @@ -385,9 +499,11 @@ int devm_cxl_add_sysram_region(struct cxl_region *cxlr) return devm_add_action_or_reset(dev, cxl_sysram_unregister, data); =20 err_add_group: - dev_set_drvdata(dev, NULL); err_auto_online: /* if this fails, memory cannot be removed from the system until reboot */ + unregister_memory_notifier(&data->memory_notifier); +err_notifier: + dev_set_drvdata(dev, NULL); remove_memory(range.start, range_len(&range)); err_add_memory: remove_resource(res); --=20 2.52.0