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

Configure Feed

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

Merge tag 'cxl-for-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl

Pull CXL (Compute Express Link) updates from Dave Jiang:
"The significant change of interest is the handling of soft reserved
memory conflict between CXL and HMEM. In essence CXL will be the first
to claim the soft reserved memory ranges that belongs to CXL and
attempt to enumerate them with best effort. If CXL is not able to
enumerate the ranges it will punt them to HMEM.

There are also MAINTAINERS email changes from Dan Williams and
Jonathan Cameron"

* tag 'cxl-for-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (37 commits)
MAINTAINERS: Update Jonathan Cameron's email address
cxl/hdm: Add support for 32 switch decoders
MAINTAINERS: Update address for Dan Williams
tools/testing/cxl: Enable replay of user regions as auto regions
cxl/region: Add a region sysfs interface for region lock status
tools/testing/cxl: Test dax_hmem takeover of CXL regions
tools/testing/cxl: Simulate auto-assembly failure
dax/hmem: Parent dax_hmem devices
dax/hmem: Fix singleton confusion between dax_hmem_work and hmem devices
dax/hmem: Reduce visibility of dax_cxl coordination symbols
cxl/region: Constify cxl_region_resource_contains()
cxl/region: Limit visibility of cxl_region_contains_resource()
dax/cxl: Fix HMEM dependencies
cxl/region: Fix use-after-free from auto assembly failure
cxl/core: Check existence of cxl_memdev_state in poison test
cxl/core: use cleanup.h for devm_cxl_add_dax_region
cxl/core/region: move dax region device logic into region_dax.c
cxl/core/region: move pmem region driver logic into region_pmem.c
dax/hmem, cxl: Defer and resolve Soft Reserved ownership
cxl/region: Add helper to check Soft Reserved containment by CXL regions
...

+1625 -654
+2
.mailmap
··· 208 208 Corey Minyard <minyard@acm.org> 209 209 Damian Hobson-Garcia <dhobsong@igel.co.jp> 210 210 Dan Carpenter <error27@gmail.com> <dan.carpenter@oracle.com> 211 + Dan Williams <djbw@kernel.org> <dan.j.williams@intel.com> 211 212 Daniel Borkmann <daniel@iogearbox.net> <danborkmann@googlemail.com> 212 213 Daniel Borkmann <daniel@iogearbox.net> <danborkmann@iogearbox.net> 213 214 Daniel Borkmann <daniel@iogearbox.net> <daniel.borkmann@tik.ee.ethz.ch> ··· 428 427 <jon.toppins+linux@gmail.com> <jtoppins@cumulusnetworks.com> 429 428 <jon.toppins+linux@gmail.com> <jtoppins@redhat.com> 430 429 Jonas Gorski <jonas.gorski@gmail.com> <jogo@openwrt.org> 430 + Jonathan Cameron <jic23@kernel.org> <jonathan.cameron@huawei.com> 431 431 Jordan Crouse <jordan@cosmicpenguin.net> <jcrouse@codeaurora.org> 432 432 <josh@joshtriplett.org> <josh@freedesktop.org> 433 433 <josh@joshtriplett.org> <josh@kernel.org>
+13
Documentation/ABI/testing/sysfs-bus-cxl
··· 508 508 (RO) The size of extended linear cache, if there is an extended 509 509 linear cache. Otherwise the attribute will not be visible. 510 510 511 + 512 + What: /sys/bus/cxl/devices/regionZ/locked 513 + Date: Mar, 2026 514 + KernelVersion: v7.1 515 + Contact: linux-cxl@vger.kernel.org 516 + Description: 517 + (RO) The CXL driver has the capability to lock a region based on 518 + a BIOS or platform dependent configuration. Regions created as 519 + locked are never permitted to be destroyed. Resets to participating 520 + decoders will not result in a region destroy and will not free the 521 + decoder resources. 522 + 523 + 511 524 What: /sys/bus/cxl/devices/regionZ/mode 512 525 Date: January, 2023 513 526 KernelVersion: v6.3
+13 -13
MAINTAINERS
··· 4055 4055 F: crypto/rsa* 4056 4056 4057 4057 ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API 4058 - R: Dan Williams <dan.j.williams@intel.com> 4058 + R: Dan Williams <djbw@kernel.org> 4059 4059 S: Odd fixes 4060 4060 W: http://sourceforge.net/projects/xscaleiop 4061 4061 F: Documentation/crypto/async-tx-api.rst ··· 6422 6422 6423 6423 COMPUTE EXPRESS LINK (CXL) 6424 6424 M: Davidlohr Bueso <dave@stgolabs.net> 6425 - M: Jonathan Cameron <jonathan.cameron@huawei.com> 6425 + M: Jonathan Cameron <jic23@kernel.org> 6426 6426 M: Dave Jiang <dave.jiang@intel.com> 6427 6427 M: Alison Schofield <alison.schofield@intel.com> 6428 6428 M: Vishal Verma <vishal.l.verma@intel.com> 6429 6429 M: Ira Weiny <ira.weiny@intel.com> 6430 - M: Dan Williams <dan.j.williams@intel.com> 6430 + M: Dan Williams <djbw@kernel.org> 6431 6431 L: linux-cxl@vger.kernel.org 6432 6432 S: Maintained 6433 6433 F: Documentation/driver-api/cxl ··· 6438 6438 F: tools/testing/cxl/ 6439 6439 6440 6440 COMPUTE EXPRESS LINK PMU (CPMU) 6441 - M: Jonathan Cameron <jonathan.cameron@huawei.com> 6441 + M: Jonathan Cameron <jic23@kernel.org> 6442 6442 L: linux-cxl@vger.kernel.org 6443 6443 S: Maintained 6444 6444 F: Documentation/admin-guide/perf/cxl.rst ··· 7295 7295 F: scripts/dev-needs.sh 7296 7296 7297 7297 DEVICE DIRECT ACCESS (DAX) 7298 - M: Dan Williams <dan.j.williams@intel.com> 7298 + M: Dan Williams <djbw@kernel.org> 7299 7299 M: Vishal Verma <vishal.l.verma@intel.com> 7300 7300 M: Dave Jiang <dave.jiang@intel.com> 7301 7301 L: nvdimm@lists.linux.dev ··· 9852 9852 F: include/uapi/linux/fcntl.h 9853 9853 9854 9854 FILESYSTEM DIRECT ACCESS (DAX) 9855 - M: Dan Williams <dan.j.williams@intel.com> 9855 + M: Dan Williams <djbw@kernel.org> 9856 9856 R: Matthew Wilcox <willy@infradead.org> 9857 9857 R: Jan Kara <jack@suse.cz> 9858 9858 L: linux-fsdevel@vger.kernel.org ··· 10597 10597 M: Dave Jiang <dave.jiang@intel.com> 10598 10598 M: Jason Gunthorpe <jgg@nvidia.com> 10599 10599 M: Saeed Mahameed <saeedm@nvidia.com> 10600 - R: Jonathan Cameron <Jonathan.Cameron@huawei.com> 10600 + R: Jonathan Cameron <jic23@kernel.org> 10601 10601 S: Maintained 10602 10602 F: Documentation/userspace-api/fwctl/ 10603 10603 F: drivers/fwctl/ ··· 12938 12938 12939 12939 INTEL I/OAT DMA DRIVER 12940 12940 M: Dave Jiang <dave.jiang@intel.com> 12941 - R: Dan Williams <dan.j.williams@intel.com> 12941 + R: Dan Williams <djbw@kernel.org> 12942 12942 L: dmaengine@vger.kernel.org 12943 12943 S: Supported 12944 12944 Q: https://patchwork.kernel.org/project/linux-dmaengine/list/ ··· 14657 14657 14658 14658 LIBNVDIMM BTT: BLOCK TRANSLATION TABLE 14659 14659 M: Vishal Verma <vishal.l.verma@intel.com> 14660 - M: Dan Williams <dan.j.williams@intel.com> 14660 + M: Dan Williams <djbw@kernel.org> 14661 14661 M: Dave Jiang <dave.jiang@intel.com> 14662 14662 L: nvdimm@lists.linux.dev 14663 14663 S: Supported ··· 14666 14666 F: drivers/nvdimm/btt* 14667 14667 14668 14668 LIBNVDIMM PMEM: PERSISTENT MEMORY DRIVER 14669 - M: Dan Williams <dan.j.williams@intel.com> 14669 + M: Dan Williams <djbw@kernel.org> 14670 14670 M: Vishal Verma <vishal.l.verma@intel.com> 14671 14671 M: Dave Jiang <dave.jiang@intel.com> 14672 14672 L: nvdimm@lists.linux.dev ··· 14684 14684 F: drivers/nvdimm/of_pmem.c 14685 14685 14686 14686 LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM 14687 - M: Dan Williams <dan.j.williams@intel.com> 14687 + M: Dan Williams <djbw@kernel.org> 14688 14688 M: Vishal Verma <vishal.l.verma@intel.com> 14689 14689 M: Dave Jiang <dave.jiang@intel.com> 14690 14690 M: Ira Weiny <ira.weiny@intel.com> ··· 25361 25361 25362 25362 STANDALONE CACHE CONTROLLER DRIVERS 25363 25363 M: Conor Dooley <conor@kernel.org> 25364 - M: Jonathan Cameron <jonathan.cameron@huawei.com> 25364 + M: Jonathan Cameron <jic23@kernel.org> 25365 25365 S: Maintained 25366 25366 T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ 25367 25367 F: Documentation/devicetree/bindings/cache/ ··· 27088 27088 F: Documentation/devicetree/bindings/trigger-source/* 27089 27089 27090 27090 TRUSTED EXECUTION ENVIRONMENT SECURITY MANAGER (TSM) 27091 - M: Dan Williams <dan.j.williams@intel.com> 27091 + M: Dan Williams <djbw@kernel.org> 27092 27092 L: linux-coco@lists.linux.dev 27093 27093 S: Maintained 27094 27094 F: Documentation/ABI/testing/configfs-tsm-report
+5 -2
drivers/acpi/numa/srat.c
··· 654 654 } 655 655 last_real_pxm = fake_pxm; 656 656 fake_pxm++; 657 - acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws, 658 - &fake_pxm); 657 + 658 + /* No need to expand numa nodes if CXL is disabled */ 659 + if (IS_ENABLED(CONFIG_CXL_ACPI)) 660 + acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws, 661 + &fake_pxm); 659 662 660 663 if (cnt < 0) 661 664 return cnt;
+1 -1
drivers/cxl/core/Makefile
··· 15 15 cxl_core-y += pmu.o 16 16 cxl_core-y += cdat.o 17 17 cxl_core-$(CONFIG_TRACING) += trace.o 18 - cxl_core-$(CONFIG_CXL_REGION) += region.o 18 + cxl_core-$(CONFIG_CXL_REGION) += region.o region_pmem.o region_dax.o 19 19 cxl_core-$(CONFIG_CXL_MCE) += mce.o 20 20 cxl_core-$(CONFIG_CXL_FEATURES) += features.o 21 21 cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += edac.o
+4
drivers/cxl/core/core.h
··· 50 50 struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa); 51 51 u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, 52 52 u64 dpa); 53 + int devm_cxl_add_dax_region(struct cxl_region *cxlr); 54 + int devm_cxl_add_pmem_region(struct cxl_region *cxlr); 53 55 54 56 #else 55 57 static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, ··· 226 224 u16 *return_code); 227 225 #endif 228 226 227 + resource_size_t cxl_rcd_component_reg_phys(struct device *dev, 228 + struct cxl_dport *dport); 229 229 #endif /* __CXL_CORE_H__ */
+1 -1
drivers/cxl/core/hdm.c
··· 170 170 } 171 171 172 172 parse_hdm_decoder_caps(cxlhdm); 173 - if (cxlhdm->decoder_count == 0) { 173 + if (cxlhdm->decoder_count < 0) { 174 174 dev_err(dev, "Spec violation. Caps invalid\n"); 175 175 return ERR_PTR(-ENXIO); 176 176 }
+8 -9
drivers/cxl/core/mbox.c
··· 893 893 } 894 894 EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, "CXL"); 895 895 896 - void cxl_event_trace_record(const struct cxl_memdev *cxlmd, 896 + void cxl_event_trace_record(struct cxl_memdev *cxlmd, 897 897 enum cxl_event_log_type type, 898 898 enum cxl_event_type event_type, 899 899 const uuid_t *uuid, union cxl_event *evt) ··· 920 920 * translations. Take topology mutation locks and lookup 921 921 * { HPA, REGION } from { DPA, MEMDEV } in the event record. 922 922 */ 923 + guard(device)(&cxlmd->dev); 923 924 guard(rwsem_read)(&cxl_rwsem.region); 924 925 guard(rwsem_read)(&cxl_rwsem.dpa); 925 926 ··· 969 968 } 970 969 EXPORT_SYMBOL_NS_GPL(cxl_event_trace_record, "CXL"); 971 970 972 - static void __cxl_event_trace_record(const struct cxl_memdev *cxlmd, 971 + static void __cxl_event_trace_record(struct cxl_memdev *cxlmd, 973 972 enum cxl_event_log_type type, 974 973 struct cxl_event_record_raw *record) 975 974 { ··· 1522 1521 } 1523 1522 EXPORT_SYMBOL_NS_GPL(cxl_mailbox_init, "CXL"); 1524 1523 1525 - struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev) 1524 + struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, 1525 + u16 dvsec) 1526 1526 { 1527 1527 struct cxl_memdev_state *mds; 1528 1528 int rc; 1529 1529 1530 - mds = devm_kzalloc(dev, sizeof(*mds), GFP_KERNEL); 1530 + mds = devm_cxl_dev_state_create(dev, CXL_DEVTYPE_CLASSMEM, serial, 1531 + dvsec, struct cxl_memdev_state, cxlds, 1532 + true); 1531 1533 if (!mds) { 1532 1534 dev_err(dev, "No memory available\n"); 1533 1535 return ERR_PTR(-ENOMEM); 1534 1536 } 1535 1537 1536 1538 mutex_init(&mds->event.log_lock); 1537 - mds->cxlds.dev = dev; 1538 - mds->cxlds.reg_map.host = dev; 1539 - mds->cxlds.cxl_mbox.host = dev; 1540 - mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE; 1541 - mds->cxlds.type = CXL_DEVTYPE_CLASSMEM; 1542 1539 1543 1540 rc = devm_cxl_register_mce_notifier(dev, &mds->mce_notifier); 1544 1541 if (rc == -EOPNOTSUPP)
+27
drivers/cxl/core/memdev.c
··· 204 204 { 205 205 struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); 206 206 207 + if (!mds) 208 + return 0; 209 + 207 210 return test_bit(cmd, mds->poison.enabled_cmds); 208 211 } 209 212 ··· 658 655 } 659 656 660 657 static struct lock_class_key cxl_memdev_key; 658 + 659 + struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev, 660 + enum cxl_devtype type, 661 + u64 serial, u16 dvsec, 662 + size_t size, bool has_mbox) 663 + { 664 + struct cxl_dev_state *cxlds = devm_kzalloc(dev, size, GFP_KERNEL); 665 + 666 + if (!cxlds) 667 + return NULL; 668 + 669 + cxlds->dev = dev; 670 + cxlds->type = type; 671 + cxlds->serial = serial; 672 + cxlds->cxl_dvsec = dvsec; 673 + cxlds->reg_map.host = dev; 674 + cxlds->reg_map.resource = CXL_RESOURCE_NONE; 675 + 676 + if (has_mbox) 677 + cxlds->cxl_mbox.host = dev; 678 + 679 + return cxlds; 680 + } 681 + EXPORT_SYMBOL_NS_GPL(_devm_cxl_dev_state_create, "CXL"); 661 682 662 683 static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds, 663 684 const struct file_operations *fops,
+57
drivers/cxl/core/pci.c
··· 696 696 } 697 697 EXPORT_SYMBOL_NS_GPL(cxl_endpoint_decoder_reset_detected, "CXL"); 698 698 699 + static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev, 700 + struct cxl_register_map *map, 701 + struct cxl_dport *dport) 702 + { 703 + resource_size_t component_reg_phys; 704 + 705 + *map = (struct cxl_register_map) { 706 + .host = &pdev->dev, 707 + .resource = CXL_RESOURCE_NONE, 708 + }; 709 + 710 + component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport); 711 + if (component_reg_phys == CXL_RESOURCE_NONE) 712 + return -ENXIO; 713 + 714 + map->resource = component_reg_phys; 715 + map->reg_type = CXL_REGLOC_RBI_COMPONENT; 716 + map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE; 717 + 718 + return 0; 719 + } 720 + 721 + int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, 722 + struct cxl_register_map *map) 723 + { 724 + int rc; 725 + 726 + rc = cxl_find_regblock(pdev, type, map); 727 + 728 + /* 729 + * If the Register Locator DVSEC does not exist, check if it 730 + * is an RCH and try to extract the Component Registers from 731 + * an RCRB. 732 + */ 733 + if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev)) { 734 + struct cxl_dport *dport; 735 + struct cxl_port *port __free(put_cxl_port) = 736 + cxl_pci_find_port(pdev, &dport); 737 + if (!port) 738 + return -EPROBE_DEFER; 739 + 740 + rc = cxl_rcrb_get_comp_regs(pdev, map, dport); 741 + if (rc) 742 + return rc; 743 + 744 + rc = cxl_dport_map_rcd_linkcap(pdev, dport); 745 + if (rc) 746 + return rc; 747 + 748 + } else if (rc) { 749 + return rc; 750 + } 751 + 752 + return cxl_setup_regs(map); 753 + } 754 + EXPORT_SYMBOL_NS_GPL(cxl_pci_setup_regs, "CXL"); 755 + 699 756 int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c) 700 757 { 701 758 int speed, bw;
+161 -318
drivers/cxl/core/region.c
··· 485 485 486 486 static const struct attribute_group *get_cxl_region_target_group(void); 487 487 488 - static ssize_t interleave_ways_store(struct device *dev, 489 - struct device_attribute *attr, 490 - const char *buf, size_t len) 488 + static int set_interleave_ways(struct cxl_region *cxlr, int val) 491 489 { 492 - struct cxl_region *cxlr = to_cxl_region(dev); 493 490 struct cxl_root_decoder *cxlrd = cxlr->cxlrd; 494 491 struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 495 492 struct cxl_region_params *p = &cxlr->params; 496 - unsigned int val, save; 497 - int rc; 493 + int save, rc; 498 494 u8 iw; 499 - 500 - rc = kstrtouint(buf, 0, &val); 501 - if (rc) 502 - return rc; 503 495 504 496 rc = ways_to_eiw(val, &iw); 505 497 if (rc) ··· 507 515 return -EINVAL; 508 516 } 509 517 510 - ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); 511 - if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) 512 - return rc; 518 + lockdep_assert_held_write(&cxl_rwsem.region); 513 519 514 520 if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) 515 521 return -EBUSY; ··· 515 525 save = p->interleave_ways; 516 526 p->interleave_ways = val; 517 527 rc = sysfs_update_group(&cxlr->dev.kobj, get_cxl_region_target_group()); 518 - if (rc) { 528 + if (rc) 519 529 p->interleave_ways = save; 530 + 531 + return rc; 532 + } 533 + 534 + static ssize_t interleave_ways_store(struct device *dev, 535 + struct device_attribute *attr, 536 + const char *buf, size_t len) 537 + { 538 + struct cxl_region *cxlr = to_cxl_region(dev); 539 + int val; 540 + int rc; 541 + 542 + rc = kstrtoint(buf, 0, &val); 543 + if (rc) 520 544 return rc; 521 - } 545 + 546 + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); 547 + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) 548 + return rc; 549 + 550 + rc = set_interleave_ways(cxlr, val); 551 + if (rc) 552 + return rc; 522 553 523 554 return len; 524 555 } ··· 559 548 return sysfs_emit(buf, "%d\n", p->interleave_granularity); 560 549 } 561 550 562 - static ssize_t interleave_granularity_store(struct device *dev, 563 - struct device_attribute *attr, 564 - const char *buf, size_t len) 551 + static int set_interleave_granularity(struct cxl_region *cxlr, int val) 565 552 { 566 - struct cxl_region *cxlr = to_cxl_region(dev); 567 553 struct cxl_root_decoder *cxlrd = cxlr->cxlrd; 568 554 struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; 569 555 struct cxl_region_params *p = &cxlr->params; 570 - int rc, val; 556 + int rc; 571 557 u16 ig; 572 - 573 - rc = kstrtoint(buf, 0, &val); 574 - if (rc) 575 - return rc; 576 558 577 559 rc = granularity_to_eig(val, &ig); 578 560 if (rc) ··· 582 578 if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity) 583 579 return -EINVAL; 584 580 585 - ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); 586 - if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) 587 - return rc; 581 + lockdep_assert_held_write(&cxl_rwsem.region); 588 582 589 583 if (p->state >= CXL_CONFIG_INTERLEAVE_ACTIVE) 590 584 return -EBUSY; 591 585 592 586 p->interleave_granularity = val; 587 + return 0; 588 + } 589 + 590 + static ssize_t interleave_granularity_store(struct device *dev, 591 + struct device_attribute *attr, 592 + const char *buf, size_t len) 593 + { 594 + struct cxl_region *cxlr = to_cxl_region(dev); 595 + int rc, val; 596 + 597 + rc = kstrtoint(buf, 0, &val); 598 + if (rc) 599 + return rc; 600 + 601 + ACQUIRE(rwsem_write_kill, rwsem)(&cxl_rwsem.region); 602 + if ((rc = ACQUIRE_ERR(rwsem_write_kill, &rwsem))) 603 + return rc; 604 + 605 + rc = set_interleave_granularity(cxlr, val); 606 + if (rc) 607 + return rc; 593 608 594 609 return len; 595 610 } ··· 790 767 } 791 768 static DEVICE_ATTR_RO(extended_linear_cache_size); 792 769 770 + static ssize_t locked_show(struct device *dev, 771 + struct device_attribute *attr, 772 + char *buf) 773 + { 774 + struct cxl_region *cxlr = to_cxl_region(dev); 775 + int rc; 776 + 777 + ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region); 778 + if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem))) 779 + return rc; 780 + 781 + rc = test_bit(CXL_REGION_F_LOCK, &cxlr->flags); 782 + return sysfs_emit(buf, "%d\n", rc); 783 + } 784 + static DEVICE_ATTR_RO(locked); 785 + 793 786 static struct attribute *cxl_region_attrs[] = { 794 787 &dev_attr_uuid.attr, 795 788 &dev_attr_commit.attr, ··· 815 776 &dev_attr_size.attr, 816 777 &dev_attr_mode.attr, 817 778 &dev_attr_extended_linear_cache_size.attr, 779 + &dev_attr_locked.attr, 818 780 NULL, 819 781 }; 820 782 ··· 1103 1063 1104 1064 if (!cxld->region) { 1105 1065 cxld->region = cxlr; 1066 + 1067 + /* 1068 + * Now that cxld->region is set the intermediate staging state 1069 + * can be cleared. 1070 + */ 1071 + if (cxld == &cxled->cxld && 1072 + cxled->state == CXL_DECODER_STATE_AUTO_STAGED) 1073 + cxled->state = CXL_DECODER_STATE_AUTO; 1106 1074 get_device(&cxlr->dev); 1107 1075 } 1108 1076 ··· 1852 1804 pos = p->nr_targets; 1853 1805 p->targets[pos] = cxled; 1854 1806 cxled->pos = pos; 1807 + cxled->state = CXL_DECODER_STATE_AUTO_STAGED; 1855 1808 p->nr_targets++; 1856 1809 1857 1810 return 0; ··· 2202 2153 return 0; 2203 2154 } 2204 2155 2156 + static int cxl_region_by_target(struct device *dev, const void *data) 2157 + { 2158 + const struct cxl_endpoint_decoder *cxled = data; 2159 + struct cxl_region_params *p; 2160 + struct cxl_region *cxlr; 2161 + 2162 + if (!is_cxl_region(dev)) 2163 + return 0; 2164 + 2165 + cxlr = to_cxl_region(dev); 2166 + p = &cxlr->params; 2167 + return p->targets[cxled->pos] == cxled; 2168 + } 2169 + 2170 + /* 2171 + * When an auto-region fails to assemble the decoder may be listed as a target, 2172 + * but not fully attached. 2173 + */ 2174 + static void cxl_cancel_auto_attach(struct cxl_endpoint_decoder *cxled) 2175 + { 2176 + struct cxl_region_params *p; 2177 + struct cxl_region *cxlr; 2178 + int pos = cxled->pos; 2179 + 2180 + if (cxled->state != CXL_DECODER_STATE_AUTO_STAGED) 2181 + return; 2182 + 2183 + struct device *dev __free(put_device) = 2184 + bus_find_device(&cxl_bus_type, NULL, cxled, cxl_region_by_target); 2185 + if (!dev) 2186 + return; 2187 + 2188 + cxlr = to_cxl_region(dev); 2189 + p = &cxlr->params; 2190 + 2191 + p->nr_targets--; 2192 + cxled->state = CXL_DECODER_STATE_AUTO; 2193 + cxled->pos = -1; 2194 + p->targets[pos] = NULL; 2195 + } 2196 + 2205 2197 static struct cxl_region * 2206 2198 __cxl_decoder_detach(struct cxl_region *cxlr, 2207 2199 struct cxl_endpoint_decoder *cxled, int pos, ··· 2266 2176 cxled = p->targets[pos]; 2267 2177 } else { 2268 2178 cxlr = cxled->cxld.region; 2269 - if (!cxlr) 2179 + if (!cxlr) { 2180 + cxl_cancel_auto_attach(cxled); 2270 2181 return NULL; 2182 + } 2271 2183 p = &cxlr->params; 2272 2184 } 2273 2185 ··· 2742 2650 } 2743 2651 2744 2652 static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, 2745 - enum cxl_partition_mode mode, int id) 2653 + enum cxl_partition_mode mode, int id, 2654 + enum cxl_decoder_type target_type) 2746 2655 { 2747 2656 int rc; 2748 2657 ··· 2765 2672 return ERR_PTR(-EBUSY); 2766 2673 } 2767 2674 2768 - return devm_cxl_add_region(cxlrd, id, mode, CXL_DECODER_HOSTONLYMEM); 2675 + return devm_cxl_add_region(cxlrd, id, mode, target_type); 2769 2676 } 2770 2677 2771 2678 static ssize_t create_region_store(struct device *dev, const char *buf, ··· 2779 2686 if (rc != 1) 2780 2687 return -EINVAL; 2781 2688 2782 - cxlr = __create_region(cxlrd, mode, id); 2689 + cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM); 2783 2690 if (IS_ERR(cxlr)) 2784 2691 return PTR_ERR(cxlr); 2785 2692 ··· 2849 2756 return len; 2850 2757 } 2851 2758 DEVICE_ATTR_WO(delete_region); 2852 - 2853 - static void cxl_pmem_region_release(struct device *dev) 2854 - { 2855 - struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev); 2856 - int i; 2857 - 2858 - for (i = 0; i < cxlr_pmem->nr_mappings; i++) { 2859 - struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd; 2860 - 2861 - put_device(&cxlmd->dev); 2862 - } 2863 - 2864 - kfree(cxlr_pmem); 2865 - } 2866 - 2867 - static const struct attribute_group *cxl_pmem_region_attribute_groups[] = { 2868 - &cxl_base_attribute_group, 2869 - NULL, 2870 - }; 2871 - 2872 - const struct device_type cxl_pmem_region_type = { 2873 - .name = "cxl_pmem_region", 2874 - .release = cxl_pmem_region_release, 2875 - .groups = cxl_pmem_region_attribute_groups, 2876 - }; 2877 - 2878 - bool is_cxl_pmem_region(struct device *dev) 2879 - { 2880 - return dev->type == &cxl_pmem_region_type; 2881 - } 2882 - EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL"); 2883 - 2884 - struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) 2885 - { 2886 - if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), 2887 - "not a cxl_pmem_region device\n")) 2888 - return NULL; 2889 - return container_of(dev, struct cxl_pmem_region, dev); 2890 - } 2891 - EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL"); 2892 2759 2893 2760 struct cxl_poison_context { 2894 2761 struct cxl_port *port; ··· 3003 2950 struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa) 3004 2951 { 3005 2952 struct cxl_dpa_to_region_context ctx; 3006 - struct cxl_port *port; 2953 + struct cxl_port *port = cxlmd->endpoint; 2954 + 2955 + if (!cxlmd->dev.driver) 2956 + return NULL; 3007 2957 3008 2958 ctx = (struct cxl_dpa_to_region_context) { 3009 2959 .dpa = dpa, 3010 2960 }; 3011 - port = cxlmd->endpoint; 3012 - if (port && is_cxl_endpoint(port) && cxl_num_decoders_committed(port)) 2961 + if (cxl_num_decoders_committed(port)) 3013 2962 device_for_each_child(&port->dev, &ctx, __cxl_dpa_to_region); 3014 2963 3015 2964 return ctx.cxlr; ··· 3505 3450 return -ENXIO; 3506 3451 } 3507 3452 3508 - static struct lock_class_key cxl_pmem_region_key; 3509 - 3510 - static int cxl_pmem_region_alloc(struct cxl_region *cxlr) 3511 - { 3512 - struct cxl_region_params *p = &cxlr->params; 3513 - struct cxl_nvdimm_bridge *cxl_nvb; 3514 - struct device *dev; 3515 - int i; 3516 - 3517 - guard(rwsem_read)(&cxl_rwsem.region); 3518 - if (p->state != CXL_CONFIG_COMMIT) 3519 - return -ENXIO; 3520 - 3521 - struct cxl_pmem_region *cxlr_pmem __free(kfree) = 3522 - kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets); 3523 - if (!cxlr_pmem) 3524 - return -ENOMEM; 3525 - 3526 - cxlr_pmem->hpa_range.start = p->res->start; 3527 - cxlr_pmem->hpa_range.end = p->res->end; 3528 - 3529 - /* Snapshot the region configuration underneath the cxl_rwsem.region */ 3530 - cxlr_pmem->nr_mappings = p->nr_targets; 3531 - for (i = 0; i < p->nr_targets; i++) { 3532 - struct cxl_endpoint_decoder *cxled = p->targets[i]; 3533 - struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 3534 - struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; 3535 - 3536 - /* 3537 - * Regions never span CXL root devices, so by definition the 3538 - * bridge for one device is the same for all. 3539 - */ 3540 - if (i == 0) { 3541 - cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint); 3542 - if (!cxl_nvb) 3543 - return -ENODEV; 3544 - cxlr->cxl_nvb = cxl_nvb; 3545 - } 3546 - m->cxlmd = cxlmd; 3547 - get_device(&cxlmd->dev); 3548 - m->start = cxled->dpa_res->start; 3549 - m->size = resource_size(cxled->dpa_res); 3550 - m->position = i; 3551 - } 3552 - 3553 - dev = &cxlr_pmem->dev; 3554 - device_initialize(dev); 3555 - lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); 3556 - device_set_pm_not_required(dev); 3557 - dev->parent = &cxlr->dev; 3558 - dev->bus = &cxl_bus_type; 3559 - dev->type = &cxl_pmem_region_type; 3560 - cxlr_pmem->cxlr = cxlr; 3561 - cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem); 3562 - 3563 - return 0; 3564 - } 3565 - 3566 - static void cxl_dax_region_release(struct device *dev) 3567 - { 3568 - struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev); 3569 - 3570 - kfree(cxlr_dax); 3571 - } 3572 - 3573 - static const struct attribute_group *cxl_dax_region_attribute_groups[] = { 3574 - &cxl_base_attribute_group, 3575 - NULL, 3576 - }; 3577 - 3578 - const struct device_type cxl_dax_region_type = { 3579 - .name = "cxl_dax_region", 3580 - .release = cxl_dax_region_release, 3581 - .groups = cxl_dax_region_attribute_groups, 3582 - }; 3583 - 3584 - static bool is_cxl_dax_region(struct device *dev) 3585 - { 3586 - return dev->type == &cxl_dax_region_type; 3587 - } 3588 - 3589 - struct cxl_dax_region *to_cxl_dax_region(struct device *dev) 3590 - { 3591 - if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev), 3592 - "not a cxl_dax_region device\n")) 3593 - return NULL; 3594 - return container_of(dev, struct cxl_dax_region, dev); 3595 - } 3596 - EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL"); 3597 - 3598 - static struct lock_class_key cxl_dax_region_key; 3599 - 3600 - static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr) 3601 - { 3602 - struct cxl_region_params *p = &cxlr->params; 3603 - struct cxl_dax_region *cxlr_dax; 3604 - struct device *dev; 3605 - 3606 - guard(rwsem_read)(&cxl_rwsem.region); 3607 - if (p->state != CXL_CONFIG_COMMIT) 3608 - return ERR_PTR(-ENXIO); 3609 - 3610 - cxlr_dax = kzalloc_obj(*cxlr_dax); 3611 - if (!cxlr_dax) 3612 - return ERR_PTR(-ENOMEM); 3613 - 3614 - cxlr_dax->hpa_range.start = p->res->start; 3615 - cxlr_dax->hpa_range.end = p->res->end; 3616 - 3617 - dev = &cxlr_dax->dev; 3618 - cxlr_dax->cxlr = cxlr; 3619 - device_initialize(dev); 3620 - lockdep_set_class(&dev->mutex, &cxl_dax_region_key); 3621 - device_set_pm_not_required(dev); 3622 - dev->parent = &cxlr->dev; 3623 - dev->bus = &cxl_bus_type; 3624 - dev->type = &cxl_dax_region_type; 3625 - 3626 - return cxlr_dax; 3627 - } 3628 - 3629 - static void cxlr_pmem_unregister(void *_cxlr_pmem) 3630 - { 3631 - struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem; 3632 - struct cxl_region *cxlr = cxlr_pmem->cxlr; 3633 - struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; 3634 - 3635 - /* 3636 - * Either the bridge is in ->remove() context under the device_lock(), 3637 - * or cxlr_release_nvdimm() is cancelling the bridge's release action 3638 - * for @cxlr_pmem and doing it itself (while manually holding the bridge 3639 - * lock). 3640 - */ 3641 - device_lock_assert(&cxl_nvb->dev); 3642 - cxlr->cxlr_pmem = NULL; 3643 - cxlr_pmem->cxlr = NULL; 3644 - device_unregister(&cxlr_pmem->dev); 3645 - } 3646 - 3647 - static void cxlr_release_nvdimm(void *_cxlr) 3648 - { 3649 - struct cxl_region *cxlr = _cxlr; 3650 - struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; 3651 - 3652 - scoped_guard(device, &cxl_nvb->dev) { 3653 - if (cxlr->cxlr_pmem) 3654 - devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister, 3655 - cxlr->cxlr_pmem); 3656 - } 3657 - cxlr->cxl_nvb = NULL; 3658 - put_device(&cxl_nvb->dev); 3659 - } 3660 - 3661 - /** 3662 - * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge 3663 - * @cxlr: parent CXL region for this pmem region bridge device 3664 - * 3665 - * Return: 0 on success negative error code on failure. 3666 - */ 3667 - static int devm_cxl_add_pmem_region(struct cxl_region *cxlr) 3668 - { 3669 - struct cxl_pmem_region *cxlr_pmem; 3670 - struct cxl_nvdimm_bridge *cxl_nvb; 3671 - struct device *dev; 3672 - int rc; 3673 - 3674 - rc = cxl_pmem_region_alloc(cxlr); 3675 - if (rc) 3676 - return rc; 3677 - cxlr_pmem = cxlr->cxlr_pmem; 3678 - cxl_nvb = cxlr->cxl_nvb; 3679 - 3680 - dev = &cxlr_pmem->dev; 3681 - rc = dev_set_name(dev, "pmem_region%d", cxlr->id); 3682 - if (rc) 3683 - goto err; 3684 - 3685 - rc = device_add(dev); 3686 - if (rc) 3687 - goto err; 3688 - 3689 - dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), 3690 - dev_name(dev)); 3691 - 3692 - scoped_guard(device, &cxl_nvb->dev) { 3693 - if (cxl_nvb->dev.driver) 3694 - rc = devm_add_action_or_reset(&cxl_nvb->dev, 3695 - cxlr_pmem_unregister, 3696 - cxlr_pmem); 3697 - else 3698 - rc = -ENXIO; 3699 - } 3700 - 3701 - if (rc) 3702 - goto err_bridge; 3703 - 3704 - /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */ 3705 - return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr); 3706 - 3707 - err: 3708 - put_device(dev); 3709 - err_bridge: 3710 - put_device(&cxl_nvb->dev); 3711 - cxlr->cxl_nvb = NULL; 3712 - return rc; 3713 - } 3714 - 3715 - static void cxlr_dax_unregister(void *_cxlr_dax) 3716 - { 3717 - struct cxl_dax_region *cxlr_dax = _cxlr_dax; 3718 - 3719 - device_unregister(&cxlr_dax->dev); 3720 - } 3721 - 3722 - static int devm_cxl_add_dax_region(struct cxl_region *cxlr) 3723 - { 3724 - struct cxl_dax_region *cxlr_dax; 3725 - struct device *dev; 3726 - int rc; 3727 - 3728 - cxlr_dax = cxl_dax_region_alloc(cxlr); 3729 - if (IS_ERR(cxlr_dax)) 3730 - return PTR_ERR(cxlr_dax); 3731 - 3732 - dev = &cxlr_dax->dev; 3733 - rc = dev_set_name(dev, "dax_region%d", cxlr->id); 3734 - if (rc) 3735 - goto err; 3736 - 3737 - rc = device_add(dev); 3738 - if (rc) 3739 - goto err; 3740 - 3741 - dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), 3742 - dev_name(dev)); 3743 - 3744 - return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister, 3745 - cxlr_dax); 3746 - err: 3747 - put_device(dev); 3748 - return rc; 3749 - } 3750 - 3751 3453 static int match_root_decoder(struct device *dev, const void *data) 3752 3454 { 3753 3455 const struct range *r1, *r2 = data; ··· 3716 3904 3717 3905 do { 3718 3906 cxlr = __create_region(cxlrd, cxlds->part[part].mode, 3719 - atomic_read(&cxlrd->region_id)); 3907 + atomic_read(&cxlrd->region_id), 3908 + cxled->cxld.target_type); 3720 3909 } while (IS_ERR(cxlr) && PTR_ERR(cxlr) == -EBUSY); 3721 3910 3722 3911 if (IS_ERR(cxlr)) { ··· 3987 4174 3988 4175 return devm_add_action_or_reset(dev, remove_debugfs, dentry); 3989 4176 } 4177 + 4178 + static int region_contains_resource(struct device *dev, const void *data) 4179 + { 4180 + const struct resource *res = data; 4181 + struct cxl_region *cxlr; 4182 + struct cxl_region_params *p; 4183 + 4184 + if (!is_cxl_region(dev)) 4185 + return 0; 4186 + 4187 + cxlr = to_cxl_region(dev); 4188 + p = &cxlr->params; 4189 + 4190 + if (p->state != CXL_CONFIG_COMMIT) 4191 + return 0; 4192 + 4193 + if (!p->res) 4194 + return 0; 4195 + 4196 + return resource_contains(p->res, res) ? 1 : 0; 4197 + } 4198 + 4199 + bool cxl_region_contains_resource(const struct resource *res) 4200 + { 4201 + guard(rwsem_read)(&cxl_rwsem.region); 4202 + struct device *dev __free(put_device) = bus_find_device( 4203 + &cxl_bus_type, NULL, res, region_contains_resource); 4204 + return !!dev; 4205 + } 4206 + EXPORT_SYMBOL_FOR_MODULES(cxl_region_contains_resource, "dax_hmem"); 3990 4207 3991 4208 static int cxl_region_can_probe(struct cxl_region *cxlr) 3992 4209 {
+106
drivers/cxl/core/region_dax.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright(c) 2022 Intel Corporation. All rights reserved. 4 + * Copyright(c) 2026 Meta Technologies Inc. All rights reserved. 5 + */ 6 + #include <linux/device.h> 7 + #include <linux/slab.h> 8 + #include <cxlmem.h> 9 + #include <cxl.h> 10 + #include "core.h" 11 + 12 + static void cxl_dax_region_release(struct device *dev) 13 + { 14 + struct cxl_dax_region *cxlr_dax = to_cxl_dax_region(dev); 15 + 16 + kfree(cxlr_dax); 17 + } 18 + 19 + static const struct attribute_group *cxl_dax_region_attribute_groups[] = { 20 + &cxl_base_attribute_group, 21 + NULL 22 + }; 23 + 24 + const struct device_type cxl_dax_region_type = { 25 + .name = "cxl_dax_region", 26 + .release = cxl_dax_region_release, 27 + .groups = cxl_dax_region_attribute_groups, 28 + }; 29 + 30 + static bool is_cxl_dax_region(struct device *dev) 31 + { 32 + return dev->type == &cxl_dax_region_type; 33 + } 34 + 35 + struct cxl_dax_region *to_cxl_dax_region(struct device *dev) 36 + { 37 + if (dev_WARN_ONCE(dev, !is_cxl_dax_region(dev), 38 + "not a cxl_dax_region device\n")) 39 + return NULL; 40 + return container_of(dev, struct cxl_dax_region, dev); 41 + } 42 + EXPORT_SYMBOL_NS_GPL(to_cxl_dax_region, "CXL"); 43 + 44 + static struct lock_class_key cxl_dax_region_key; 45 + 46 + static struct cxl_dax_region *cxl_dax_region_alloc(struct cxl_region *cxlr) 47 + { 48 + struct cxl_region_params *p = &cxlr->params; 49 + struct cxl_dax_region *cxlr_dax; 50 + struct device *dev; 51 + 52 + guard(rwsem_read)(&cxl_rwsem.region); 53 + if (p->state != CXL_CONFIG_COMMIT) 54 + return ERR_PTR(-ENXIO); 55 + 56 + cxlr_dax = kzalloc_obj(*cxlr_dax); 57 + if (!cxlr_dax) 58 + return ERR_PTR(-ENOMEM); 59 + 60 + cxlr_dax->hpa_range.start = p->res->start; 61 + cxlr_dax->hpa_range.end = p->res->end; 62 + 63 + dev = &cxlr_dax->dev; 64 + cxlr_dax->cxlr = cxlr; 65 + device_initialize(dev); 66 + lockdep_set_class(&dev->mutex, &cxl_dax_region_key); 67 + device_set_pm_not_required(dev); 68 + dev->parent = &cxlr->dev; 69 + dev->bus = &cxl_bus_type; 70 + dev->type = &cxl_dax_region_type; 71 + 72 + return cxlr_dax; 73 + } 74 + 75 + static void cxlr_dax_unregister(void *_cxlr_dax) 76 + { 77 + struct cxl_dax_region *cxlr_dax = _cxlr_dax; 78 + 79 + device_unregister(&cxlr_dax->dev); 80 + } 81 + 82 + int devm_cxl_add_dax_region(struct cxl_region *cxlr) 83 + { 84 + struct device *dev; 85 + int rc; 86 + 87 + struct cxl_dax_region *cxlr_dax __free(put_cxl_dax_region) = 88 + cxl_dax_region_alloc(cxlr); 89 + if (IS_ERR(cxlr_dax)) 90 + return PTR_ERR(cxlr_dax); 91 + 92 + dev = &cxlr_dax->dev; 93 + rc = dev_set_name(dev, "dax_region%d", cxlr->id); 94 + if (rc) 95 + return rc; 96 + 97 + rc = device_add(dev); 98 + if (rc) 99 + return rc; 100 + 101 + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), 102 + dev_name(dev)); 103 + 104 + return devm_add_action_or_reset(&cxlr->dev, cxlr_dax_unregister, 105 + no_free_ptr(cxlr_dax)); 106 + }
+191
drivers/cxl/core/region_pmem.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ 3 + #include <linux/device.h> 4 + #include <linux/slab.h> 5 + #include <cxlmem.h> 6 + #include <cxl.h> 7 + #include "core.h" 8 + 9 + static void cxl_pmem_region_release(struct device *dev) 10 + { 11 + struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev); 12 + int i; 13 + 14 + for (i = 0; i < cxlr_pmem->nr_mappings; i++) { 15 + struct cxl_memdev *cxlmd = cxlr_pmem->mapping[i].cxlmd; 16 + 17 + put_device(&cxlmd->dev); 18 + } 19 + 20 + kfree(cxlr_pmem); 21 + } 22 + 23 + static const struct attribute_group *cxl_pmem_region_attribute_groups[] = { 24 + &cxl_base_attribute_group, 25 + NULL 26 + }; 27 + 28 + const struct device_type cxl_pmem_region_type = { 29 + .name = "cxl_pmem_region", 30 + .release = cxl_pmem_region_release, 31 + .groups = cxl_pmem_region_attribute_groups, 32 + }; 33 + 34 + bool is_cxl_pmem_region(struct device *dev) 35 + { 36 + return dev->type == &cxl_pmem_region_type; 37 + } 38 + EXPORT_SYMBOL_NS_GPL(is_cxl_pmem_region, "CXL"); 39 + 40 + struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev) 41 + { 42 + if (dev_WARN_ONCE(dev, !is_cxl_pmem_region(dev), 43 + "not a cxl_pmem_region device\n")) 44 + return NULL; 45 + return container_of(dev, struct cxl_pmem_region, dev); 46 + } 47 + EXPORT_SYMBOL_NS_GPL(to_cxl_pmem_region, "CXL"); 48 + 49 + static struct lock_class_key cxl_pmem_region_key; 50 + 51 + static int cxl_pmem_region_alloc(struct cxl_region *cxlr) 52 + { 53 + struct cxl_region_params *p = &cxlr->params; 54 + struct cxl_nvdimm_bridge *cxl_nvb; 55 + struct device *dev; 56 + int i; 57 + 58 + guard(rwsem_read)(&cxl_rwsem.region); 59 + if (p->state != CXL_CONFIG_COMMIT) 60 + return -ENXIO; 61 + 62 + struct cxl_pmem_region *cxlr_pmem __free(kfree) = 63 + kzalloc_flex(*cxlr_pmem, mapping, p->nr_targets); 64 + if (!cxlr_pmem) 65 + return -ENOMEM; 66 + 67 + cxlr_pmem->hpa_range.start = p->res->start; 68 + cxlr_pmem->hpa_range.end = p->res->end; 69 + 70 + /* Snapshot the region configuration underneath the cxl_rwsem.region */ 71 + cxlr_pmem->nr_mappings = p->nr_targets; 72 + for (i = 0; i < p->nr_targets; i++) { 73 + struct cxl_endpoint_decoder *cxled = p->targets[i]; 74 + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); 75 + struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i]; 76 + 77 + /* 78 + * Regions never span CXL root devices, so by definition the 79 + * bridge for one device is the same for all. 80 + */ 81 + if (i == 0) { 82 + cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint); 83 + if (!cxl_nvb) 84 + return -ENODEV; 85 + cxlr->cxl_nvb = cxl_nvb; 86 + } 87 + m->cxlmd = cxlmd; 88 + get_device(&cxlmd->dev); 89 + m->start = cxled->dpa_res->start; 90 + m->size = resource_size(cxled->dpa_res); 91 + m->position = i; 92 + } 93 + 94 + dev = &cxlr_pmem->dev; 95 + device_initialize(dev); 96 + lockdep_set_class(&dev->mutex, &cxl_pmem_region_key); 97 + device_set_pm_not_required(dev); 98 + dev->parent = &cxlr->dev; 99 + dev->bus = &cxl_bus_type; 100 + dev->type = &cxl_pmem_region_type; 101 + cxlr_pmem->cxlr = cxlr; 102 + cxlr->cxlr_pmem = no_free_ptr(cxlr_pmem); 103 + 104 + return 0; 105 + } 106 + 107 + static void cxlr_pmem_unregister(void *_cxlr_pmem) 108 + { 109 + struct cxl_pmem_region *cxlr_pmem = _cxlr_pmem; 110 + struct cxl_region *cxlr = cxlr_pmem->cxlr; 111 + struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; 112 + 113 + /* 114 + * Either the bridge is in ->remove() context under the device_lock(), 115 + * or cxlr_release_nvdimm() is cancelling the bridge's release action 116 + * for @cxlr_pmem and doing it itself (while manually holding the bridge 117 + * lock). 118 + */ 119 + device_lock_assert(&cxl_nvb->dev); 120 + cxlr->cxlr_pmem = NULL; 121 + cxlr_pmem->cxlr = NULL; 122 + device_unregister(&cxlr_pmem->dev); 123 + } 124 + 125 + static void cxlr_release_nvdimm(void *_cxlr) 126 + { 127 + struct cxl_region *cxlr = _cxlr; 128 + struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb; 129 + 130 + scoped_guard(device, &cxl_nvb->dev) { 131 + if (cxlr->cxlr_pmem) 132 + devm_release_action(&cxl_nvb->dev, cxlr_pmem_unregister, 133 + cxlr->cxlr_pmem); 134 + } 135 + cxlr->cxl_nvb = NULL; 136 + put_device(&cxl_nvb->dev); 137 + } 138 + 139 + /** 140 + * devm_cxl_add_pmem_region() - add a cxl_region-to-nd_region bridge 141 + * @cxlr: parent CXL region for this pmem region bridge device 142 + * 143 + * Return: 0 on success negative error code on failure. 144 + */ 145 + int devm_cxl_add_pmem_region(struct cxl_region *cxlr) 146 + { 147 + struct cxl_pmem_region *cxlr_pmem; 148 + struct cxl_nvdimm_bridge *cxl_nvb; 149 + struct device *dev; 150 + int rc; 151 + 152 + rc = cxl_pmem_region_alloc(cxlr); 153 + if (rc) 154 + return rc; 155 + cxlr_pmem = cxlr->cxlr_pmem; 156 + cxl_nvb = cxlr->cxl_nvb; 157 + 158 + dev = &cxlr_pmem->dev; 159 + rc = dev_set_name(dev, "pmem_region%d", cxlr->id); 160 + if (rc) 161 + goto err; 162 + 163 + rc = device_add(dev); 164 + if (rc) 165 + goto err; 166 + 167 + dev_dbg(&cxlr->dev, "%s: register %s\n", dev_name(dev->parent), 168 + dev_name(dev)); 169 + 170 + scoped_guard(device, &cxl_nvb->dev) { 171 + if (cxl_nvb->dev.driver) 172 + rc = devm_add_action_or_reset(&cxl_nvb->dev, 173 + cxlr_pmem_unregister, 174 + cxlr_pmem); 175 + else 176 + rc = -ENXIO; 177 + } 178 + 179 + if (rc) 180 + goto err_bridge; 181 + 182 + /* @cxlr carries a reference on @cxl_nvb until cxlr_release_nvdimm */ 183 + return devm_add_action_or_reset(&cxlr->dev, cxlr_release_nvdimm, cxlr); 184 + 185 + err: 186 + put_device(dev); 187 + err_bridge: 188 + put_device(&cxl_nvb->dev); 189 + cxlr->cxl_nvb = NULL; 190 + return rc; 191 + }
-1
drivers/cxl/core/regs.c
··· 641 641 return CXL_RESOURCE_NONE; 642 642 return __rcrb_to_component(dev, &dport->rcrb, CXL_RCRB_UPSTREAM); 643 643 } 644 - EXPORT_SYMBOL_NS_GPL(cxl_rcd_component_reg_phys, "CXL");
+22 -101
drivers/cxl/cxl.h
··· 12 12 #include <linux/node.h> 13 13 #include <linux/io.h> 14 14 #include <linux/range.h> 15 + #include <cxl/cxl.h> 15 16 16 17 extern const struct nvdimm_security_ops *cxl_security_ops; 17 18 ··· 78 77 { 79 78 int val = FIELD_GET(CXL_HDM_DECODER_COUNT_MASK, cap_hdr); 80 79 81 - return val ? val * 2 : 1; 80 + switch (val) { 81 + case 0: 82 + return 1; 83 + case 1 ... 8: 84 + return val * 2; 85 + case 9 ... 12: 86 + return (val - 4) * 4; 87 + default: 88 + return -ENXIO; 89 + } 82 90 } 83 91 84 92 /* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */ ··· 211 201 #define CXLDEV_MBOX_BG_CMD_COMMAND_VENDOR_MASK GENMASK_ULL(63, 48) 212 202 #define CXLDEV_MBOX_PAYLOAD_OFFSET 0x20 213 203 214 - /* 215 - * Using struct_group() allows for per register-block-type helper routines, 216 - * without requiring block-type agnostic code to include the prefix. 217 - */ 218 - struct cxl_regs { 219 - /* 220 - * Common set of CXL Component register block base pointers 221 - * @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure 222 - * @ras: CXL 2.0 8.2.5.9 CXL RAS Capability Structure 223 - */ 224 - struct_group_tagged(cxl_component_regs, component, 225 - void __iomem *hdm_decoder; 226 - void __iomem *ras; 227 - ); 228 - /* 229 - * Common set of CXL Device register block base pointers 230 - * @status: CXL 2.0 8.2.8.3 Device Status Registers 231 - * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers 232 - * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers 233 - */ 234 - struct_group_tagged(cxl_device_regs, device_regs, 235 - void __iomem *status, *mbox, *memdev; 236 - ); 237 - 238 - struct_group_tagged(cxl_pmu_regs, pmu_regs, 239 - void __iomem *pmu; 240 - ); 241 - 242 - /* 243 - * RCH downstream port specific RAS register 244 - * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB 245 - */ 246 - struct_group_tagged(cxl_rch_regs, rch_regs, 247 - void __iomem *dport_aer; 248 - ); 249 - 250 - /* 251 - * RCD upstream port specific PCIe cap register 252 - * @pcie_cap: CXL 3.0 8.2.1.2 RCD Upstream Port RCRB 253 - */ 254 - struct_group_tagged(cxl_rcd_regs, rcd_regs, 255 - void __iomem *rcd_pcie_cap; 256 - ); 257 - }; 258 - 259 - struct cxl_reg_map { 260 - bool valid; 261 - int id; 262 - unsigned long offset; 263 - unsigned long size; 264 - }; 265 - 266 - struct cxl_component_reg_map { 267 - struct cxl_reg_map hdm_decoder; 268 - struct cxl_reg_map ras; 269 - }; 270 - 271 - struct cxl_device_reg_map { 272 - struct cxl_reg_map status; 273 - struct cxl_reg_map mbox; 274 - struct cxl_reg_map memdev; 275 - }; 276 - 277 - struct cxl_pmu_reg_map { 278 - struct cxl_reg_map pmu; 279 - }; 280 - 281 - /** 282 - * struct cxl_register_map - DVSEC harvested register block mapping parameters 283 - * @host: device for devm operations and logging 284 - * @base: virtual base of the register-block-BAR + @block_offset 285 - * @resource: physical resource base of the register block 286 - * @max_size: maximum mapping size to perform register search 287 - * @reg_type: see enum cxl_regloc_type 288 - * @component_map: cxl_reg_map for component registers 289 - * @device_map: cxl_reg_maps for device registers 290 - * @pmu_map: cxl_reg_maps for CXL Performance Monitoring Units 291 - */ 292 - struct cxl_register_map { 293 - struct device *host; 294 - void __iomem *base; 295 - resource_size_t resource; 296 - resource_size_t max_size; 297 - u8 reg_type; 298 - union { 299 - struct cxl_component_reg_map component_map; 300 - struct cxl_device_reg_map device_map; 301 - struct cxl_pmu_reg_map pmu_map; 302 - }; 303 - }; 304 - 305 204 void cxl_probe_component_regs(struct device *dev, void __iomem *base, 306 205 struct cxl_component_reg_map *map); 307 206 void cxl_probe_device_regs(struct device *dev, void __iomem *base, ··· 231 312 struct cxl_register_map *map); 232 313 int cxl_setup_regs(struct cxl_register_map *map); 233 314 struct cxl_dport; 234 - resource_size_t cxl_rcd_component_reg_phys(struct device *dev, 235 - struct cxl_dport *dport); 236 315 int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport); 237 316 238 317 #define CXL_RESOURCE_NONE ((resource_size_t) -1) ··· 250 333 #define CXL_DECODER_F_LOCK BIT(4) 251 334 #define CXL_DECODER_F_ENABLE BIT(5) 252 335 #define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6) 336 + #define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK) 253 337 254 338 enum cxl_decoder_type { 255 339 CXL_DECODER_DEVMEM = 2, ··· 296 378 }; 297 379 298 380 /* 299 - * Track whether this decoder is reserved for region autodiscovery, or 300 - * free for userspace provisioning. 381 + * Track whether this decoder is free for userspace provisioning, reserved for 382 + * region autodiscovery, whether it is started connecting (awaiting other 383 + * peers), or has completed auto assembly. 301 384 */ 302 385 enum cxl_decoder_state { 303 386 CXL_DECODER_STATE_MANUAL, 304 387 CXL_DECODER_STATE_AUTO, 388 + CXL_DECODER_STATE_AUTO_STAGED, 305 389 }; 306 390 307 391 /** ··· 415 495 struct cxl_endpoint_decoder *targets[CXL_DECODER_MAX_INTERLEAVE]; 416 496 int nr_targets; 417 497 resource_size_t cache_size; 418 - }; 419 - 420 - enum cxl_partition_mode { 421 - CXL_PARTMODE_RAM, 422 - CXL_PARTMODE_PMEM, 423 498 }; 424 499 425 500 /* ··· 723 808 DEFINE_FREE(put_cxl_port, struct cxl_port *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev)) 724 809 DEFINE_FREE(put_cxl_root_decoder, struct cxl_root_decoder *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->cxlsd.cxld.dev)) 725 810 DEFINE_FREE(put_cxl_region, struct cxl_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev)) 811 + DEFINE_FREE(put_cxl_dax_region, struct cxl_dax_region *, if (!IS_ERR_OR_NULL(_T)) put_device(&_T->dev)) 726 812 727 813 int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd); 728 814 void cxl_bus_rescan(void); ··· 855 939 int cxl_add_to_region(struct cxl_endpoint_decoder *cxled); 856 940 struct cxl_dax_region *to_cxl_dax_region(struct device *dev); 857 941 u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa); 942 + bool cxl_region_contains_resource(const struct resource *res); 858 943 #else 859 944 static inline bool is_cxl_pmem_region(struct device *dev) 860 945 { ··· 877 960 u64 spa) 878 961 { 879 962 return 0; 963 + } 964 + static inline bool cxl_region_contains_resource(const struct resource *res) 965 + { 966 + return false; 880 967 } 881 968 #endif 882 969
+4 -86
drivers/cxl/cxlmem.h
··· 113 113 resource_size_t base, resource_size_t len, 114 114 resource_size_t skipped); 115 115 116 - #define CXL_NR_PARTITIONS_MAX 2 117 - 118 116 struct cxl_dpa_info { 119 117 u64 size; 120 118 struct cxl_dpa_part_info { ··· 369 371 bool sanitize_active; 370 372 struct delayed_work poll_dwork; 371 373 struct kernfs_node *sanitize_node; 372 - }; 373 - 374 - /* 375 - * enum cxl_devtype - delineate type-2 from a generic type-3 device 376 - * @CXL_DEVTYPE_DEVMEM - Vendor specific CXL Type-2 device implementing HDM-D or 377 - * HDM-DB, no requirement that this device implements a 378 - * mailbox, or other memory-device-standard manageability 379 - * flows. 380 - * @CXL_DEVTYPE_CLASSMEM - Common class definition of a CXL Type-3 device with 381 - * HDM-H and class-mandatory memory device registers 382 - */ 383 - enum cxl_devtype { 384 - CXL_DEVTYPE_DEVMEM, 385 - CXL_DEVTYPE_CLASSMEM, 386 - }; 387 - 388 - /** 389 - * struct cxl_dpa_perf - DPA performance property entry 390 - * @dpa_range: range for DPA address 391 - * @coord: QoS performance data (i.e. latency, bandwidth) 392 - * @cdat_coord: raw QoS performance data from CDAT 393 - * @qos_class: QoS Class cookies 394 - */ 395 - struct cxl_dpa_perf { 396 - struct range dpa_range; 397 - struct access_coordinate coord[ACCESS_COORDINATE_MAX]; 398 - struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX]; 399 - int qos_class; 400 - }; 401 - 402 - /** 403 - * struct cxl_dpa_partition - DPA partition descriptor 404 - * @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res) 405 - * @perf: performance attributes of the partition from CDAT 406 - * @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic... 407 - */ 408 - struct cxl_dpa_partition { 409 - struct resource res; 410 - struct cxl_dpa_perf perf; 411 - enum cxl_partition_mode mode; 412 - }; 413 - 414 - /** 415 - * struct cxl_dev_state - The driver device state 416 - * 417 - * cxl_dev_state represents the CXL driver/device state. It provides an 418 - * interface to mailbox commands as well as some cached data about the device. 419 - * Currently only memory devices are represented. 420 - * 421 - * @dev: The device associated with this CXL state 422 - * @cxlmd: The device representing the CXL.mem capabilities of @dev 423 - * @reg_map: component and ras register mapping parameters 424 - * @regs: Class device "Device" registers 425 - * @cxl_dvsec: Offset to the PCIe device DVSEC 426 - * @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH) 427 - * @media_ready: Indicate whether the device media is usable 428 - * @dpa_res: Overall DPA resource tree for the device 429 - * @part: DPA partition array 430 - * @nr_partitions: Number of DPA partitions 431 - * @serial: PCIe Device Serial Number 432 - * @type: Generic Memory Class device or Vendor Specific Memory device 433 - * @cxl_mbox: CXL mailbox context 434 - * @cxlfs: CXL features context 435 - */ 436 - struct cxl_dev_state { 437 - struct device *dev; 438 - struct cxl_memdev *cxlmd; 439 - struct cxl_register_map reg_map; 440 - struct cxl_device_regs regs; 441 - int cxl_dvsec; 442 - bool rcd; 443 - bool media_ready; 444 - struct resource dpa_res; 445 - struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX]; 446 - unsigned int nr_partitions; 447 - u64 serial; 448 - enum cxl_devtype type; 449 - struct cxl_mailbox cxl_mbox; 450 - #ifdef CONFIG_CXL_FEATURES 451 - struct cxl_features_state *cxlfs; 452 - #endif 453 374 }; 454 375 455 376 static inline resource_size_t cxl_pmem_size(struct cxl_dev_state *cxlds) ··· 775 858 int cxl_await_media_ready(struct cxl_dev_state *cxlds); 776 859 int cxl_enumerate_cmds(struct cxl_memdev_state *mds); 777 860 int cxl_mem_dpa_fetch(struct cxl_memdev_state *mds, struct cxl_dpa_info *info); 778 - struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev); 861 + struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, 862 + u16 dvsec); 779 863 void set_exclusive_cxl_commands(struct cxl_memdev_state *mds, 780 864 unsigned long *cmds); 781 865 void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds, 782 866 unsigned long *cmds); 783 867 void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status); 784 - void cxl_event_trace_record(const struct cxl_memdev *cxlmd, 868 + void cxl_event_trace_record(struct cxl_memdev *cxlmd, 785 869 enum cxl_event_log_type type, 786 870 enum cxl_event_type event_type, 787 871 const uuid_t *uuid, union cxl_event *evt); ··· 841 923 */ 842 924 struct cxl_hdm { 843 925 struct cxl_component_regs regs; 844 - unsigned int decoder_count; 926 + int decoder_count; 845 927 unsigned int target_count; 846 928 unsigned int interleave_mask; 847 929 unsigned long iw_cap_mask;
+13
drivers/cxl/cxlpci.h
··· 74 74 return lnksta2 & PCI_EXP_LNKSTA2_FLIT; 75 75 } 76 76 77 + /* 78 + * Assume that the caller has already validated that @pdev has CXL 79 + * capabilities, any RCiEP with CXL capabilities is treated as a 80 + * Restricted CXL Device (RCD) and finds upstream port and endpoint 81 + * registers in a Root Complex Register Block (RCRB). 82 + */ 83 + static inline bool is_cxl_restricted(struct pci_dev *pdev) 84 + { 85 + return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END; 86 + } 87 + 77 88 struct cxl_dev_state; 78 89 void read_cdat_data(struct cxl_port *port); 79 90 ··· 112 101 } 113 102 #endif 114 103 104 + int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, 105 + struct cxl_register_map *map); 115 106 #endif /* __CXL_PCI_H__ */
+26 -77
drivers/cxl/pci.c
··· 465 465 return 0; 466 466 } 467 467 468 - /* 469 - * Assume that any RCIEP that emits the CXL memory expander class code 470 - * is an RCD 471 - */ 472 - static bool is_cxl_restricted(struct pci_dev *pdev) 473 - { 474 - return pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END; 475 - } 476 - 477 - static int cxl_rcrb_get_comp_regs(struct pci_dev *pdev, 478 - struct cxl_register_map *map, 479 - struct cxl_dport *dport) 480 - { 481 - resource_size_t component_reg_phys; 482 - 483 - *map = (struct cxl_register_map) { 484 - .host = &pdev->dev, 485 - .resource = CXL_RESOURCE_NONE, 486 - }; 487 - 488 - struct cxl_port *port __free(put_cxl_port) = 489 - cxl_pci_find_port(pdev, &dport); 490 - if (!port) 491 - return -EPROBE_DEFER; 492 - 493 - component_reg_phys = cxl_rcd_component_reg_phys(&pdev->dev, dport); 494 - if (component_reg_phys == CXL_RESOURCE_NONE) 495 - return -ENXIO; 496 - 497 - map->resource = component_reg_phys; 498 - map->reg_type = CXL_REGLOC_RBI_COMPONENT; 499 - map->max_size = CXL_COMPONENT_REG_BLOCK_SIZE; 500 - 501 - return 0; 502 - } 503 - 504 - static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, 505 - struct cxl_register_map *map) 506 - { 507 - int rc; 508 - 509 - rc = cxl_find_regblock(pdev, type, map); 510 - 511 - /* 512 - * If the Register Locator DVSEC does not exist, check if it 513 - * is an RCH and try to extract the Component Registers from 514 - * an RCRB. 515 - */ 516 - if (rc && type == CXL_REGLOC_RBI_COMPONENT && is_cxl_restricted(pdev)) { 517 - struct cxl_dport *dport; 518 - struct cxl_port *port __free(put_cxl_port) = 519 - cxl_pci_find_port(pdev, &dport); 520 - if (!port) 521 - return -EPROBE_DEFER; 522 - 523 - rc = cxl_rcrb_get_comp_regs(pdev, map, dport); 524 - if (rc) 525 - return rc; 526 - 527 - rc = cxl_dport_map_rcd_linkcap(pdev, dport); 528 - if (rc) 529 - return rc; 530 - 531 - } else if (rc) { 532 - return rc; 533 - } 534 - 535 - return cxl_setup_regs(map); 536 - } 537 - 538 468 static void free_event_buf(void *buf) 539 469 { 540 470 kvfree(buf); ··· 795 865 int rc, pmu_count; 796 866 unsigned int i; 797 867 bool irq_avail; 868 + u16 dvsec; 798 869 799 870 rc = pcim_enable_device(pdev); 800 871 if (rc) 801 872 return rc; 802 873 pci_set_master(pdev); 803 874 804 - mds = cxl_memdev_state_create(&pdev->dev); 875 + dvsec = pci_find_dvsec_capability(pdev, PCI_VENDOR_ID_CXL, 876 + PCI_DVSEC_CXL_DEVICE); 877 + if (!dvsec) 878 + pci_warn(pdev, "Device DVSEC not present, skip CXL.mem init\n"); 879 + 880 + mds = cxl_memdev_state_create(&pdev->dev, pci_get_dsn(pdev), dvsec); 805 881 if (IS_ERR(mds)) 806 882 return PTR_ERR(mds); 807 883 cxlds = &mds->cxlds; 808 884 pci_set_drvdata(pdev, cxlds); 809 885 810 886 cxlds->rcd = is_cxl_restricted(pdev); 811 - cxlds->serial = pci_get_dsn(pdev); 812 - cxlds->cxl_dvsec = pci_find_dvsec_capability( 813 - pdev, PCI_VENDOR_ID_CXL, PCI_DVSEC_CXL_DEVICE); 814 - if (!cxlds->cxl_dvsec) 815 - dev_warn(&pdev->dev, 816 - "Device DVSEC not present, skip CXL.mem init\n"); 817 887 818 888 rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map); 819 889 if (rc) ··· 960 1030 dev->driver ? "successful" : "failed"); 961 1031 } 962 1032 1033 + static int cxl_endpoint_decoder_clear_reset_flags(struct device *dev, void *data) 1034 + { 1035 + struct cxl_endpoint_decoder *cxled; 1036 + 1037 + if (!is_endpoint_decoder(dev)) 1038 + return 0; 1039 + 1040 + cxled = to_cxl_endpoint_decoder(dev); 1041 + cxled->cxld.flags &= ~CXL_DECODER_F_RESET_MASK; 1042 + 1043 + return 0; 1044 + } 1045 + 963 1046 static void cxl_reset_done(struct pci_dev *pdev) 964 1047 { 965 1048 struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); ··· 986 1043 * that no longer exists. 987 1044 */ 988 1045 guard(device)(&cxlmd->dev); 1046 + if (!cxlmd->dev.driver) 1047 + return; 1048 + 989 1049 if (cxlmd->endpoint && 990 1050 cxl_endpoint_decoder_reset_detected(cxlmd->endpoint)) { 1051 + device_for_each_child(&cxlmd->endpoint->dev, NULL, 1052 + cxl_endpoint_decoder_clear_reset_flags); 1053 + 991 1054 dev_crit(dev, "SBR happened without memory regions removal.\n"); 992 1055 dev_crit(dev, "System may be unstable if regions hosted system memory.\n"); 993 1056 add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+4
drivers/dax/Kconfig
··· 32 32 depends on EFI_SOFT_RESERVE 33 33 select NUMA_KEEP_MEMINFO if NUMA_MEMBLKS 34 34 default DEV_DAX 35 + depends on CXL_ACPI || !CXL_ACPI 36 + depends on CXL_PCI || !CXL_PCI 37 + depends on CXL_BUS || !CXL_BUS 35 38 help 36 39 EFI 2.8 platforms, and others, may advertise 'specific purpose' 37 40 memory. For example, a high bandwidth memory pool. The ··· 51 48 tristate "CXL DAX: direct access to CXL RAM regions" 52 49 depends on CXL_BUS && CXL_REGION && DEV_DAX 53 50 default CXL_REGION && DEV_DAX 51 + depends on DEV_DAX_HMEM || !DEV_DAX_HMEM 54 52 help 55 53 CXL RAM regions are either mapped by platform-firmware 56 54 and published in the initial system-memory map as "System RAM", mapped
+1 -2
drivers/dax/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 + obj-y += hmem/ 2 3 obj-$(CONFIG_DAX) += dax.o 3 4 obj-$(CONFIG_DEV_DAX) += device_dax.o 4 5 obj-$(CONFIG_DEV_DAX_KMEM) += kmem.o ··· 11 10 device_dax-y := device.o 12 11 dax_pmem-y := pmem.o 13 12 dax_cxl-y := cxl.o 14 - 15 - obj-y += hmem/
+17 -3
drivers/dax/bus.c
··· 10 10 #include "dax-private.h" 11 11 #include "bus.h" 12 12 13 + static struct resource dax_regions = DEFINE_RES_MEM_NAMED(0, -1, "DAX Regions"); 13 14 static DEFINE_MUTEX(dax_bus_lock); 14 15 15 16 /* ··· 628 627 629 628 sysfs_remove_groups(&dax_region->dev->kobj, 630 629 dax_region_attribute_groups); 630 + release_resource(&dax_region->res); 631 631 dax_region_put(dax_region); 632 632 } 633 633 ··· 637 635 unsigned long flags) 638 636 { 639 637 struct dax_region *dax_region; 638 + int rc; 640 639 641 640 /* 642 641 * The DAX core assumes that it can store its private data in ··· 670 667 .flags = IORESOURCE_MEM | flags, 671 668 }; 672 669 673 - if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) { 674 - kfree(dax_region); 675 - return NULL; 670 + rc = request_resource(&dax_regions, &dax_region->res); 671 + if (rc) { 672 + dev_dbg(parent, "dax_region resource conflict for %pR\n", 673 + &dax_region->res); 674 + goto err_res; 676 675 } 676 + 677 + if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) 678 + goto err_sysfs; 677 679 678 680 if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region)) 679 681 return NULL; 680 682 return dax_region; 683 + 684 + err_sysfs: 685 + release_resource(&dax_region->res); 686 + err_res: 687 + dax_region_put(dax_region); 688 + return NULL; 681 689 } 682 690 EXPORT_SYMBOL_GPL(alloc_dax_region); 683 691
+20
drivers/dax/bus.h
··· 3 3 #ifndef __DAX_BUS_H__ 4 4 #define __DAX_BUS_H__ 5 5 #include <linux/device.h> 6 + #include <linux/platform_device.h> 6 7 #include <linux/range.h> 8 + #include <linux/workqueue.h> 7 9 8 10 struct dev_dax; 9 11 struct resource; ··· 50 48 void dax_driver_unregister(struct dax_device_driver *dax_drv); 51 49 void kill_dev_dax(struct dev_dax *dev_dax); 52 50 bool static_dev_dax(struct dev_dax *dev_dax); 51 + 52 + struct hmem_platform_device { 53 + struct platform_device pdev; 54 + struct work_struct work; 55 + bool did_probe; 56 + }; 57 + 58 + static inline struct hmem_platform_device * 59 + to_hmem_platform_device(struct platform_device *pdev) 60 + { 61 + return container_of(pdev, struct hmem_platform_device, pdev); 62 + } 63 + 64 + #if IS_ENABLED(CONFIG_DEV_DAX_HMEM) 65 + void dax_hmem_flush_work(void); 66 + #else 67 + static inline void dax_hmem_flush_work(void) { } 68 + #endif 53 69 54 70 #define MODULE_ALIAS_DAX_DEVICE(type) \ 55 71 MODULE_ALIAS("dax:t" __stringify(type) "*")
+27 -1
drivers/dax/cxl.c
··· 38 38 .id = CXL_DEVICE_DAX_REGION, 39 39 .drv = { 40 40 .suppress_bind_attrs = true, 41 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 41 42 }, 42 43 }; 43 44 44 - module_cxl_driver(cxl_dax_region_driver); 45 + static void cxl_dax_region_driver_register(struct work_struct *work) 46 + { 47 + dax_hmem_flush_work(); 48 + cxl_driver_register(&cxl_dax_region_driver); 49 + } 50 + 51 + static DECLARE_WORK(cxl_dax_region_driver_work, cxl_dax_region_driver_register); 52 + 53 + static int __init cxl_dax_region_init(void) 54 + { 55 + /* 56 + * Need to resolve a race with dax_hmem wanting to drive regions 57 + * instead of CXL 58 + */ 59 + queue_work(system_long_wq, &cxl_dax_region_driver_work); 60 + return 0; 61 + } 62 + module_init(cxl_dax_region_init); 63 + 64 + static void __exit cxl_dax_region_exit(void) 65 + { 66 + flush_work(&cxl_dax_region_driver_work); 67 + cxl_driver_unregister(&cxl_dax_region_driver); 68 + } 69 + module_exit(cxl_dax_region_exit); 70 + 45 71 MODULE_ALIAS_CXL(CXL_DEVICE_DAX_REGION); 46 72 MODULE_DESCRIPTION("CXL DAX: direct access to CXL regions"); 47 73 MODULE_LICENSE("GPL");
+17 -8
drivers/dax/hmem/device.c
··· 4 4 #include <linux/module.h> 5 5 #include <linux/dax.h> 6 6 #include <linux/mm.h> 7 + #include "../bus.h" 7 8 8 9 static bool nohmem; 9 10 module_param_named(disable, nohmem, bool, 0444); ··· 34 33 } 35 34 EXPORT_SYMBOL_GPL(walk_hmem_resources); 36 35 36 + static void hmem_work(struct work_struct *work) 37 + { 38 + /* place holder until dax_hmem driver attaches */ 39 + } 40 + 41 + static struct hmem_platform_device hmem_platform = { 42 + .pdev = { 43 + .name = "hmem_platform", 44 + .id = 0, 45 + }, 46 + .work = __WORK_INITIALIZER(hmem_platform.work, hmem_work), 47 + }; 48 + 37 49 static void __hmem_register_resource(int target_nid, struct resource *res) 38 50 { 39 - struct platform_device *pdev; 40 51 struct resource *new; 41 52 int rc; 42 53 ··· 64 51 if (platform_initialized) 65 52 return; 66 53 67 - pdev = platform_device_alloc("hmem_platform", 0); 68 - if (!pdev) { 54 + rc = platform_device_register(&hmem_platform.pdev); 55 + if (rc) { 69 56 pr_err_once("failed to register device-dax hmem_platform device\n"); 70 57 return; 71 58 } 72 59 73 - rc = platform_device_add(pdev); 74 - if (rc) 75 - platform_device_put(pdev); 76 - else 77 - platform_initialized = true; 60 + platform_initialized = true; 78 61 } 79 62 80 63 void hmem_register_resource(int target_nid, struct resource *res)
+102 -18
drivers/dax/hmem/hmem.c
··· 3 3 #include <linux/memregion.h> 4 4 #include <linux/module.h> 5 5 #include <linux/dax.h> 6 + #include "../../cxl/cxl.h" 6 7 #include "../bus.h" 7 8 8 9 static bool region_idle; ··· 59 58 platform_device_unregister(pdev); 60 59 } 61 60 62 - static int hmem_register_device(struct device *host, int target_nid, 63 - const struct resource *res) 61 + static struct workqueue_struct *dax_hmem_wq; 62 + 63 + void dax_hmem_flush_work(void) 64 + { 65 + flush_workqueue(dax_hmem_wq); 66 + } 67 + EXPORT_SYMBOL_FOR_MODULES(dax_hmem_flush_work, "dax_cxl"); 68 + 69 + static int __hmem_register_device(struct device *host, int target_nid, 70 + const struct resource *res) 64 71 { 65 72 struct platform_device *pdev; 66 73 struct memregion_info info; 67 74 long id; 68 75 int rc; 69 - 70 - if (IS_ENABLED(CONFIG_CXL_REGION) && 71 - region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 72 - IORES_DESC_CXL) != REGION_DISJOINT) { 73 - dev_dbg(host, "deferring range to CXL: %pr\n", res); 74 - return 0; 75 - } 76 76 77 77 rc = region_intersects_soft_reserve(res->start, resource_size(res)); 78 78 if (rc != REGION_INTERSECTS) ··· 96 94 return -ENOMEM; 97 95 } 98 96 97 + pdev->dev.parent = host; 99 98 pdev->dev.numa_node = numa_map_to_online_node(target_nid); 100 99 info = (struct memregion_info) { 101 100 .target_node = target_nid, ··· 126 123 return rc; 127 124 } 128 125 126 + static int hmem_register_cxl_device(struct device *host, int target_nid, 127 + const struct resource *res) 128 + { 129 + if (region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 130 + IORES_DESC_CXL) == REGION_DISJOINT) 131 + return 0; 132 + 133 + if (cxl_region_contains_resource(res)) { 134 + dev_dbg(host, "CXL claims resource, dropping: %pr\n", res); 135 + return 0; 136 + } 137 + 138 + dev_dbg(host, "CXL did not claim resource, registering: %pr\n", res); 139 + return __hmem_register_device(host, target_nid, res); 140 + } 141 + 142 + static void process_defer_work(struct work_struct *w) 143 + { 144 + struct hmem_platform_device *hpdev = container_of(w, typeof(*hpdev), work); 145 + struct device *dev = &hpdev->pdev.dev; 146 + 147 + /* Relies on cxl_acpi and cxl_pci having had a chance to load */ 148 + wait_for_device_probe(); 149 + 150 + guard(device)(dev); 151 + if (!dev->driver) 152 + goto out; 153 + 154 + if (!hpdev->did_probe) { 155 + hpdev->did_probe = true; 156 + walk_hmem_resources(dev, hmem_register_cxl_device); 157 + } 158 + out: 159 + put_device(dev); 160 + } 161 + 162 + static int hmem_register_device(struct device *host, int target_nid, 163 + const struct resource *res) 164 + { 165 + struct platform_device *pdev = to_platform_device(host); 166 + struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev); 167 + 168 + if (IS_ENABLED(CONFIG_DEV_DAX_CXL) && 169 + region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 170 + IORES_DESC_CXL) != REGION_DISJOINT) { 171 + if (!hpdev->did_probe) { 172 + dev_dbg(host, "await CXL initial probe: %pr\n", res); 173 + hpdev->work.func = process_defer_work; 174 + get_device(host); 175 + if (!queue_work(dax_hmem_wq, &hpdev->work)) 176 + put_device(host); 177 + return 0; 178 + } 179 + dev_dbg(host, "deferring range to CXL: %pr\n", res); 180 + return 0; 181 + } 182 + 183 + return __hmem_register_device(host, target_nid, res); 184 + } 185 + 129 186 static int dax_hmem_platform_probe(struct platform_device *pdev) 130 187 { 188 + struct hmem_platform_device *hpdev = to_hmem_platform_device(pdev); 189 + 190 + /* queue is only flushed on module unload, fail rebind with pending work */ 191 + if (work_pending(&hpdev->work)) 192 + return -EBUSY; 193 + 131 194 return walk_hmem_resources(&pdev->dev, hmem_register_device); 132 195 } 133 196 ··· 208 139 { 209 140 int rc; 210 141 142 + /* 143 + * Ensure that cxl_acpi and cxl_pci have a chance to kick off 144 + * CXL topology discovery at least once before scanning the 145 + * iomem resource tree for IORES_DESC_CXL resources. 146 + */ 147 + if (IS_ENABLED(CONFIG_DEV_DAX_CXL)) { 148 + request_module("cxl_acpi"); 149 + request_module("cxl_pci"); 150 + } 151 + 152 + dax_hmem_wq = alloc_ordered_workqueue("dax_hmem_wq", 0); 153 + if (!dax_hmem_wq) 154 + return -ENOMEM; 155 + 211 156 rc = platform_driver_register(&dax_hmem_platform_driver); 212 157 if (rc) 213 - return rc; 158 + goto err_platform_driver; 214 159 215 160 rc = platform_driver_register(&dax_hmem_driver); 216 161 if (rc) 217 - platform_driver_unregister(&dax_hmem_platform_driver); 162 + goto err_driver; 163 + 164 + return 0; 165 + 166 + err_driver: 167 + platform_driver_unregister(&dax_hmem_platform_driver); 168 + err_platform_driver: 169 + destroy_workqueue(dax_hmem_wq); 218 170 219 171 return rc; 220 172 } ··· 244 154 { 245 155 platform_driver_unregister(&dax_hmem_driver); 246 156 platform_driver_unregister(&dax_hmem_platform_driver); 157 + destroy_workqueue(dax_hmem_wq); 247 158 } 248 159 249 160 module_init(dax_hmem_init); 250 161 module_exit(dax_hmem_exit); 251 - 252 - /* Allow for CXL to define its own dax regions */ 253 - #if IS_ENABLED(CONFIG_CXL_REGION) 254 - #if IS_MODULE(CONFIG_CXL_ACPI) 255 - MODULE_SOFTDEP("pre: cxl_acpi"); 256 - #endif 257 - #endif 258 162 259 163 MODULE_ALIAS("platform:hmem*"); 260 164 MODULE_ALIAS("platform:hmem_platform*");
+226
include/cxl/cxl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright(c) 2020 Intel Corporation. */ 3 + /* Copyright(c) 2026 Advanced Micro Devices, Inc. */ 4 + 5 + #ifndef __CXL_CXL_H__ 6 + #define __CXL_CXL_H__ 7 + 8 + #include <linux/node.h> 9 + #include <linux/ioport.h> 10 + #include <cxl/mailbox.h> 11 + 12 + /** 13 + * enum cxl_devtype - delineate type-2 from a generic type-3 device 14 + * @CXL_DEVTYPE_DEVMEM: Vendor specific CXL Type-2 device implementing HDM-D or 15 + * HDM-DB, no requirement that this device implements a 16 + * mailbox, or other memory-device-standard manageability 17 + * flows. 18 + * @CXL_DEVTYPE_CLASSMEM: Common class definition of a CXL Type-3 device with 19 + * HDM-H and class-mandatory memory device registers 20 + */ 21 + enum cxl_devtype { 22 + CXL_DEVTYPE_DEVMEM, 23 + CXL_DEVTYPE_CLASSMEM, 24 + }; 25 + 26 + struct device; 27 + 28 + /* 29 + * Using struct_group() allows for per register-block-type helper routines, 30 + * without requiring block-type agnostic code to include the prefix. 31 + */ 32 + struct cxl_regs { 33 + /* 34 + * Common set of CXL Component register block base pointers 35 + * @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure 36 + * @ras: CXL 2.0 8.2.5.9 CXL RAS Capability Structure 37 + */ 38 + struct_group_tagged(cxl_component_regs, component, 39 + void __iomem *hdm_decoder; 40 + void __iomem *ras; 41 + ); 42 + /* 43 + * Common set of CXL Device register block base pointers 44 + * @status: CXL 2.0 8.2.8.3 Device Status Registers 45 + * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers 46 + * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers 47 + */ 48 + struct_group_tagged(cxl_device_regs, device_regs, 49 + void __iomem *status, *mbox, *memdev; 50 + ); 51 + 52 + struct_group_tagged(cxl_pmu_regs, pmu_regs, 53 + void __iomem *pmu; 54 + ); 55 + 56 + /* 57 + * RCH downstream port specific RAS register 58 + * @aer: CXL 3.0 8.2.1.1 RCH Downstream Port RCRB 59 + */ 60 + struct_group_tagged(cxl_rch_regs, rch_regs, 61 + void __iomem *dport_aer; 62 + ); 63 + 64 + /* 65 + * RCD upstream port specific PCIe cap register 66 + * @pcie_cap: CXL 3.0 8.2.1.2 RCD Upstream Port RCRB 67 + */ 68 + struct_group_tagged(cxl_rcd_regs, rcd_regs, 69 + void __iomem *rcd_pcie_cap; 70 + ); 71 + }; 72 + 73 + struct cxl_reg_map { 74 + bool valid; 75 + int id; 76 + unsigned long offset; 77 + unsigned long size; 78 + }; 79 + 80 + struct cxl_component_reg_map { 81 + struct cxl_reg_map hdm_decoder; 82 + struct cxl_reg_map ras; 83 + }; 84 + 85 + struct cxl_device_reg_map { 86 + struct cxl_reg_map status; 87 + struct cxl_reg_map mbox; 88 + struct cxl_reg_map memdev; 89 + }; 90 + 91 + struct cxl_pmu_reg_map { 92 + struct cxl_reg_map pmu; 93 + }; 94 + 95 + /** 96 + * struct cxl_register_map - DVSEC harvested register block mapping parameters 97 + * @host: device for devm operations and logging 98 + * @base: virtual base of the register-block-BAR + @block_offset 99 + * @resource: physical resource base of the register block 100 + * @max_size: maximum mapping size to perform register search 101 + * @reg_type: see enum cxl_regloc_type 102 + * @component_map: cxl_reg_map for component registers 103 + * @device_map: cxl_reg_maps for device registers 104 + * @pmu_map: cxl_reg_maps for CXL Performance Monitoring Units 105 + */ 106 + struct cxl_register_map { 107 + struct device *host; 108 + void __iomem *base; 109 + resource_size_t resource; 110 + resource_size_t max_size; 111 + u8 reg_type; 112 + union { 113 + struct cxl_component_reg_map component_map; 114 + struct cxl_device_reg_map device_map; 115 + struct cxl_pmu_reg_map pmu_map; 116 + }; 117 + }; 118 + 119 + /** 120 + * struct cxl_dpa_perf - DPA performance property entry 121 + * @dpa_range: range for DPA address 122 + * @coord: QoS performance data (i.e. latency, bandwidth) 123 + * @cdat_coord: raw QoS performance data from CDAT 124 + * @qos_class: QoS Class cookies 125 + */ 126 + struct cxl_dpa_perf { 127 + struct range dpa_range; 128 + struct access_coordinate coord[ACCESS_COORDINATE_MAX]; 129 + struct access_coordinate cdat_coord[ACCESS_COORDINATE_MAX]; 130 + int qos_class; 131 + }; 132 + 133 + enum cxl_partition_mode { 134 + CXL_PARTMODE_RAM, 135 + CXL_PARTMODE_PMEM, 136 + }; 137 + 138 + /** 139 + * struct cxl_dpa_partition - DPA partition descriptor 140 + * @res: shortcut to the partition in the DPA resource tree (cxlds->dpa_res) 141 + * @perf: performance attributes of the partition from CDAT 142 + * @mode: operation mode for the DPA capacity, e.g. ram, pmem, dynamic... 143 + */ 144 + struct cxl_dpa_partition { 145 + struct resource res; 146 + struct cxl_dpa_perf perf; 147 + enum cxl_partition_mode mode; 148 + }; 149 + 150 + #define CXL_NR_PARTITIONS_MAX 2 151 + 152 + /** 153 + * struct cxl_dev_state - The driver device state 154 + * 155 + * cxl_dev_state represents the CXL driver/device state. It provides an 156 + * interface to mailbox commands as well as some cached data about the device. 157 + * Currently only memory devices are represented. 158 + * 159 + * @dev: The device associated with this CXL state 160 + * @cxlmd: The device representing the CXL.mem capabilities of @dev 161 + * @reg_map: component and ras register mapping parameters 162 + * @regs: Parsed register blocks 163 + * @cxl_dvsec: Offset to the PCIe device DVSEC 164 + * @rcd: operating in RCD mode (CXL 3.0 9.11.8 CXL Devices Attached to an RCH) 165 + * @media_ready: Indicate whether the device media is usable 166 + * @dpa_res: Overall DPA resource tree for the device 167 + * @part: DPA partition array 168 + * @nr_partitions: Number of DPA partitions 169 + * @serial: PCIe Device Serial Number 170 + * @type: Generic Memory Class device or Vendor Specific Memory device 171 + * @cxl_mbox: CXL mailbox context 172 + * @cxlfs: CXL features context 173 + */ 174 + struct cxl_dev_state { 175 + /* public for Type2 drivers */ 176 + struct device *dev; 177 + struct cxl_memdev *cxlmd; 178 + 179 + /* private for Type2 drivers */ 180 + struct cxl_register_map reg_map; 181 + struct cxl_device_regs regs; 182 + int cxl_dvsec; 183 + bool rcd; 184 + bool media_ready; 185 + struct resource dpa_res; 186 + struct cxl_dpa_partition part[CXL_NR_PARTITIONS_MAX]; 187 + unsigned int nr_partitions; 188 + u64 serial; 189 + enum cxl_devtype type; 190 + struct cxl_mailbox cxl_mbox; 191 + #ifdef CONFIG_CXL_FEATURES 192 + struct cxl_features_state *cxlfs; 193 + #endif 194 + }; 195 + 196 + struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev, 197 + enum cxl_devtype type, 198 + u64 serial, u16 dvsec, 199 + size_t size, bool has_mbox); 200 + 201 + /** 202 + * cxl_dev_state_create - safely create and cast a cxl dev state embedded in a 203 + * driver specific struct. 204 + * 205 + * @parent: device behind the request 206 + * @type: CXL device type 207 + * @serial: device identification 208 + * @dvsec: dvsec capability offset 209 + * @drv_struct: driver struct embedding a cxl_dev_state struct 210 + * @member: name of the struct cxl_dev_state member in drv_struct 211 + * @mbox: true if mailbox supported 212 + * 213 + * Returns a pointer to the drv_struct allocated and embedding a cxl_dev_state 214 + * struct initialized. 215 + * 216 + * Introduced for Type2 driver support. 217 + */ 218 + #define devm_cxl_dev_state_create(parent, type, serial, dvsec, drv_struct, member, mbox) \ 219 + ({ \ 220 + static_assert(__same_type(struct cxl_dev_state, \ 221 + ((drv_struct *)NULL)->member)); \ 222 + static_assert(offsetof(drv_struct, member) == 0); \ 223 + (drv_struct *)_devm_cxl_dev_state_create(parent, type, serial, dvsec, \ 224 + sizeof(drv_struct), mbox); \ 225 + }) 226 + #endif /* __CXL_CXL_H__ */
+8 -1
tools/testing/cxl/Kbuild
··· 11 11 ldflags-y += --wrap=hmat_get_extended_linear_cache_size 12 12 ldflags-y += --wrap=devm_cxl_add_dport_by_dev 13 13 ldflags-y += --wrap=devm_cxl_switch_port_decoders_setup 14 + ldflags-y += --wrap=walk_hmem_resources 15 + ldflags-y += --wrap=region_intersects 16 + ldflags-y += --wrap=region_intersects_soft_reserve 14 17 15 18 DRIVERS := ../../../drivers 19 + DAX_HMEM_SRC := $(DRIVERS)/dax/hmem 16 20 CXL_SRC := $(DRIVERS)/cxl 17 21 CXL_CORE_SRC := $(DRIVERS)/cxl/core 18 22 ccflags-y := -I$(srctree)/drivers/cxl/ ··· 63 59 cxl_core-y += $(CXL_CORE_SRC)/pmu.o 64 60 cxl_core-y += $(CXL_CORE_SRC)/cdat.o 65 61 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o 66 - cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o 62 + cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o $(CXL_CORE_SRC)/region_pmem.o $(CXL_CORE_SRC)/region_dax.o 67 63 cxl_core-$(CONFIG_CXL_MCE) += $(CXL_CORE_SRC)/mce.o 68 64 cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o 69 65 cxl_core-$(CONFIG_CXL_EDAC_MEM_FEATURES) += $(CXL_CORE_SRC)/edac.o ··· 73 69 cxl_core-y += config_check.o 74 70 cxl_core-y += cxl_core_test.o 75 71 cxl_core-y += cxl_core_exports.o 72 + 73 + obj-m += dax_hmem.o 74 + dax_hmem-y := $(DAX_HMEM_SRC)/hmem.o 76 75 77 76 KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS)) 78 77
+1
tools/testing/cxl/test/Kbuild
··· 7 7 obj-m += cxl_translate.o 8 8 9 9 cxl_test-y := cxl.o 10 + cxl_test-y += hmem_test.o 10 11 cxl_mock-y := mock.o 11 12 cxl_mock_mem-y := mem.o 12 13
+439 -10
tools/testing/cxl/test/cxl.c
··· 16 16 17 17 static int interleave_arithmetic; 18 18 static bool extended_linear_cache; 19 + static bool fail_autoassemble; 19 20 20 21 #define FAKE_QTG_ID 42 21 22 ··· 51 50 52 51 static struct platform_device *cxl_rch[NR_CXL_RCH]; 53 52 static struct platform_device *cxl_rcd[NR_CXL_RCH]; 53 + 54 + /* 55 + * Decoder registry 56 + * 57 + * Record decoder programming so that the topology can be reconstructed 58 + * after cxl_acpi unbind/bind. This allows a user-created region config 59 + * to be replayed as if firmware had provided the region at enumeration 60 + * time. 61 + * 62 + * Entries are keyed by a stable port identity (port->uport_dev) combined 63 + * with the decoder id. Decoder state is saved at initialization and 64 + * updated on commit and reset. 65 + * 66 + * On re-enumeration mock_init_hdm_decoder() consults this registry to 67 + * restore enabled decoders. Disabled decoders are reinitialized to a 68 + * clean default state rather than replaying stale programming. 69 + */ 70 + static DEFINE_XARRAY(decoder_registry); 71 + 72 + /* 73 + * When set, decoder reset will not update the registry. This allows 74 + * region destroy operations to reset live decoders without erasing 75 + * the saved programming needed for replay after re-enumeration. 76 + */ 77 + static bool decoder_reset_preserve_registry; 54 78 55 79 static inline bool is_multi_bridge(struct device *dev) 56 80 { ··· 730 704 return 0; 731 705 } 732 706 707 + /* 708 + * Build a stable registry key from the decoder's upstream port identity 709 + * and decoder id. 710 + * 711 + * Decoder objects and cxl_port objects are reallocated on each enumeration, 712 + * so their addresses cannot be used directly as replay keys. However, 713 + * port->uport_dev is stable for a given topology across cxl_acpi unbind/bind 714 + * in cxl_test, so use that as the port identity and pack the local decoder 715 + * id into the low bits. 716 + * 717 + * The key is formed as: 718 + * ((unsigned long)port->uport_dev << 4) | cxld->id 719 + * 720 + * The low bits hold the decoder id (which must fit in 4 bits) while 721 + * the remaining bits identify the upstream port. This key is only used 722 + * within cxl_test to locate saved decoder state during replay. 723 + */ 724 + static unsigned long cxld_registry_index(struct cxl_decoder *cxld) 725 + { 726 + struct cxl_port *port = to_cxl_port(cxld->dev.parent); 727 + 728 + dev_WARN_ONCE(&port->dev, cxld->id >= 16, 729 + "decoder id:%d out of range\n", cxld->id); 730 + return (((unsigned long)port->uport_dev) << 4) | cxld->id; 731 + } 732 + 733 + struct cxl_test_decoder { 734 + union { 735 + struct cxl_switch_decoder cxlsd; 736 + struct cxl_endpoint_decoder cxled; 737 + }; 738 + struct range dpa_range; 739 + }; 740 + 741 + static struct cxl_test_decoder *cxld_registry_find(struct cxl_decoder *cxld) 742 + { 743 + return xa_load(&decoder_registry, cxld_registry_index(cxld)); 744 + } 745 + 746 + #define dbg_cxld(port, msg, cxld) \ 747 + do { \ 748 + struct cxl_decoder *___d = (cxld); \ 749 + dev_dbg((port)->uport_dev, \ 750 + "decoder%d: %s range: %#llx-%#llx iw: %d ig: %d flags: %#lx\n", \ 751 + ___d->id, msg, ___d->hpa_range.start, \ 752 + ___d->hpa_range.end + 1, ___d->interleave_ways, \ 753 + ___d->interleave_granularity, ___d->flags); \ 754 + } while (0) 755 + 756 + static int mock_decoder_commit(struct cxl_decoder *cxld); 757 + static void mock_decoder_reset(struct cxl_decoder *cxld); 758 + static void init_disabled_mock_decoder(struct cxl_decoder *cxld); 759 + 760 + static void cxld_copy(struct cxl_decoder *a, struct cxl_decoder *b) 761 + { 762 + a->id = b->id; 763 + a->hpa_range = b->hpa_range; 764 + a->interleave_ways = b->interleave_ways; 765 + a->interleave_granularity = b->interleave_granularity; 766 + a->target_type = b->target_type; 767 + a->flags = b->flags; 768 + a->commit = mock_decoder_commit; 769 + a->reset = mock_decoder_reset; 770 + } 771 + 772 + /* 773 + * Restore decoder programming saved in the registry. 774 + * 775 + * Only decoders that were saved enabled are restored. Disabled decoders 776 + * are left in their default inactive state so that stale programming is 777 + * not resurrected after topology replay. 778 + * 779 + * For endpoint decoders this also restores the DPA reservation needed 780 + * to reconstruct committed mappings. 781 + */ 782 + static int cxld_registry_restore(struct cxl_decoder *cxld, 783 + struct cxl_test_decoder *td) 784 + { 785 + struct cxl_port *port = to_cxl_port(cxld->dev.parent); 786 + int rc; 787 + 788 + if (is_switch_decoder(&cxld->dev)) { 789 + struct cxl_switch_decoder *cxlsd = 790 + to_cxl_switch_decoder(&cxld->dev); 791 + 792 + if (!(td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE)) 793 + return 0; 794 + 795 + dbg_cxld(port, "restore", &td->cxlsd.cxld); 796 + cxld_copy(cxld, &td->cxlsd.cxld); 797 + WARN_ON(cxlsd->nr_targets != td->cxlsd.nr_targets); 798 + 799 + /* Restore saved target intent; live dport binding happens later */ 800 + for (int i = 0; i < cxlsd->nr_targets; i++) { 801 + cxlsd->target[i] = NULL; 802 + cxld->target_map[i] = td->cxlsd.cxld.target_map[i]; 803 + } 804 + 805 + port->commit_end = cxld->id; 806 + 807 + } else { 808 + struct cxl_endpoint_decoder *cxled = 809 + to_cxl_endpoint_decoder(&cxld->dev); 810 + 811 + if (!(td->cxled.cxld.flags & CXL_DECODER_F_ENABLE)) 812 + return 0; 813 + 814 + dbg_cxld(port, "restore", &td->cxled.cxld); 815 + cxld_copy(cxld, &td->cxled.cxld); 816 + cxled->state = td->cxled.state; 817 + cxled->skip = td->cxled.skip; 818 + if (range_len(&td->dpa_range)) { 819 + rc = devm_cxl_dpa_reserve(cxled, td->dpa_range.start, 820 + range_len(&td->dpa_range), 821 + td->cxled.skip); 822 + if (rc) { 823 + init_disabled_mock_decoder(cxld); 824 + return rc; 825 + } 826 + } 827 + port->commit_end = cxld->id; 828 + } 829 + 830 + return 0; 831 + } 832 + 833 + static void __cxld_registry_save(struct cxl_test_decoder *td, 834 + struct cxl_decoder *cxld) 835 + { 836 + if (is_switch_decoder(&cxld->dev)) { 837 + struct cxl_switch_decoder *cxlsd = 838 + to_cxl_switch_decoder(&cxld->dev); 839 + 840 + cxld_copy(&td->cxlsd.cxld, cxld); 841 + td->cxlsd.nr_targets = cxlsd->nr_targets; 842 + 843 + /* Save target port_id as a stable identify for the dport */ 844 + for (int i = 0; i < cxlsd->nr_targets; i++) { 845 + struct cxl_dport *dport; 846 + 847 + if (!cxlsd->target[i]) 848 + continue; 849 + 850 + dport = cxlsd->target[i]; 851 + td->cxlsd.cxld.target_map[i] = dport->port_id; 852 + } 853 + } else { 854 + struct cxl_endpoint_decoder *cxled = 855 + to_cxl_endpoint_decoder(&cxld->dev); 856 + 857 + cxld_copy(&td->cxled.cxld, cxld); 858 + td->cxled.state = cxled->state; 859 + td->cxled.skip = cxled->skip; 860 + 861 + if (!(cxld->flags & CXL_DECODER_F_ENABLE)) { 862 + td->dpa_range.start = 0; 863 + td->dpa_range.end = -1; 864 + } else if (cxled->dpa_res) { 865 + td->dpa_range.start = cxled->dpa_res->start; 866 + td->dpa_range.end = cxled->dpa_res->end; 867 + } else { 868 + td->dpa_range.start = 0; 869 + td->dpa_range.end = -1; 870 + } 871 + } 872 + } 873 + 874 + static void cxld_registry_save(struct cxl_test_decoder *td, 875 + struct cxl_decoder *cxld) 876 + { 877 + struct cxl_port *port = to_cxl_port(cxld->dev.parent); 878 + 879 + dbg_cxld(port, "save", cxld); 880 + __cxld_registry_save(td, cxld); 881 + } 882 + 883 + static void cxld_registry_update(struct cxl_decoder *cxld) 884 + { 885 + struct cxl_test_decoder *td = cxld_registry_find(cxld); 886 + struct cxl_port *port = to_cxl_port(cxld->dev.parent); 887 + 888 + if (WARN_ON_ONCE(!td)) 889 + return; 890 + 891 + dbg_cxld(port, "update", cxld); 892 + __cxld_registry_save(td, cxld); 893 + } 894 + 733 895 static int mock_decoder_commit(struct cxl_decoder *cxld) 734 896 { 735 897 struct cxl_port *port = to_cxl_port(cxld->dev.parent); ··· 937 723 938 724 port->commit_end++; 939 725 cxld->flags |= CXL_DECODER_F_ENABLE; 726 + if (is_endpoint_decoder(&cxld->dev)) { 727 + struct cxl_endpoint_decoder *cxled = 728 + to_cxl_endpoint_decoder(&cxld->dev); 729 + 730 + cxled->state = CXL_DECODER_STATE_AUTO; 731 + } 732 + cxld_registry_update(cxld); 940 733 941 734 return 0; 942 735 } ··· 964 743 "%s: out of order reset, expected decoder%d.%d\n", 965 744 dev_name(&cxld->dev), port->id, port->commit_end); 966 745 cxld->flags &= ~CXL_DECODER_F_ENABLE; 746 + 747 + if (is_endpoint_decoder(&cxld->dev)) { 748 + struct cxl_endpoint_decoder *cxled = 749 + to_cxl_endpoint_decoder(&cxld->dev); 750 + 751 + cxled->state = CXL_DECODER_STATE_MANUAL; 752 + cxled->skip = 0; 753 + } 754 + if (decoder_reset_preserve_registry) 755 + dev_dbg(port->uport_dev, "decoder%d: skip registry update\n", 756 + cxld->id); 757 + else 758 + cxld_registry_update(cxld); 759 + } 760 + 761 + static struct cxl_test_decoder *cxld_registry_new(struct cxl_decoder *cxld) 762 + { 763 + struct cxl_test_decoder *td __free(kfree) = 764 + kzalloc(sizeof(*td), GFP_KERNEL); 765 + unsigned long key = cxld_registry_index(cxld); 766 + 767 + if (!td) 768 + return NULL; 769 + 770 + if (xa_insert(&decoder_registry, key, td, GFP_KERNEL)) { 771 + WARN_ON(1); 772 + return NULL; 773 + } 774 + 775 + cxld_registry_save(td, cxld); 776 + return no_free_ptr(td); 777 + } 778 + 779 + static void init_disabled_mock_decoder(struct cxl_decoder *cxld) 780 + { 781 + cxld->hpa_range.start = 0; 782 + cxld->hpa_range.end = -1; 783 + cxld->interleave_ways = 1; 784 + cxld->interleave_granularity = 0; 785 + cxld->target_type = CXL_DECODER_HOSTONLYMEM; 786 + cxld->flags = 0; 787 + cxld->commit = mock_decoder_commit; 788 + cxld->reset = mock_decoder_reset; 789 + 790 + if (is_switch_decoder(&cxld->dev)) { 791 + struct cxl_switch_decoder *cxlsd = 792 + to_cxl_switch_decoder(&cxld->dev); 793 + 794 + for (int i = 0; i < cxlsd->nr_targets; i++) { 795 + cxlsd->target[i] = NULL; 796 + cxld->target_map[i] = 0; 797 + } 798 + } else { 799 + struct cxl_endpoint_decoder *cxled = 800 + to_cxl_endpoint_decoder(&cxld->dev); 801 + 802 + cxled->state = CXL_DECODER_STATE_MANUAL; 803 + cxled->skip = 0; 804 + } 967 805 } 968 806 969 807 static void default_mock_decoder(struct cxl_decoder *cxld) ··· 1037 757 cxld->target_type = CXL_DECODER_HOSTONLYMEM; 1038 758 cxld->commit = mock_decoder_commit; 1039 759 cxld->reset = mock_decoder_reset; 760 + 761 + WARN_ON_ONCE(!cxld_registry_new(cxld)); 1040 762 } 1041 763 1042 764 static int first_decoder(struct device *dev, const void *data) ··· 1053 771 return 0; 1054 772 } 1055 773 1056 - static void mock_init_hdm_decoder(struct cxl_decoder *cxld) 774 + /* 775 + * Initialize a decoder during HDM enumeration. 776 + * 777 + * If a saved registry entry exists: 778 + * - enabled decoders are restored from the saved programming 779 + * - disabled decoders are initialized in a clean disabled state 780 + * 781 + * If no registry entry exists the decoder follows the normal mock 782 + * initialization path, including the special auto-region setup for 783 + * the first endpoints under host-bridge0. 784 + * 785 + * Returns true if decoder state was restored from the registry. In 786 + * that case the saved decode configuration (including target mapping) 787 + * has already been applied and the map_targets() is skipped. 788 + */ 789 + static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) 1057 790 { 1058 791 struct acpi_cedt_cfmws *window = mock_cfmws[0]; 1059 792 struct platform_device *pdev = NULL; 1060 793 struct cxl_endpoint_decoder *cxled; 1061 794 struct cxl_switch_decoder *cxlsd; 1062 795 struct cxl_port *port, *iter; 796 + struct cxl_test_decoder *td; 1063 797 struct cxl_memdev *cxlmd; 1064 798 struct cxl_dport *dport; 1065 799 struct device *dev; ··· 1102 804 port = NULL; 1103 805 } while (port); 1104 806 port = cxled_to_port(cxled); 807 + } else { 808 + port = to_cxl_port(cxld->dev.parent); 809 + } 810 + 811 + td = cxld_registry_find(cxld); 812 + if (td) { 813 + bool enabled; 814 + 815 + if (is_switch_decoder(&cxld->dev)) 816 + enabled = td->cxlsd.cxld.flags & CXL_DECODER_F_ENABLE; 817 + else 818 + enabled = td->cxled.cxld.flags & CXL_DECODER_F_ENABLE; 819 + 820 + if (enabled) 821 + return !cxld_registry_restore(cxld, td); 822 + 823 + init_disabled_mock_decoder(cxld); 824 + return false; 1105 825 } 1106 826 1107 827 /* ··· 1130 814 * 1131 815 * See 'cxl list -BMPu -m cxl_mem.0,cxl_mem.4' 1132 816 */ 1133 - if (!hb0 || pdev->id % 4 || pdev->id > 4 || cxld->id > 0) { 817 + if (!is_endpoint_decoder(&cxld->dev) || !hb0 || pdev->id % 4 || 818 + pdev->id > 4 || cxld->id > 0) { 1134 819 default_mock_decoder(cxld); 1135 - return; 820 + return false; 821 + } 822 + 823 + /* Simulate missing cxl_mem.4 configuration */ 824 + if (hb0 && pdev->id == 4 && cxld->id == 0 && fail_autoassemble) { 825 + default_mock_decoder(cxld); 826 + return false; 1136 827 } 1137 828 1138 829 base = window->base_hpa; ··· 1161 838 cxld->commit = mock_decoder_commit; 1162 839 cxld->reset = mock_decoder_reset; 1163 840 841 + WARN_ON_ONCE(!cxld_registry_new(cxld)); 1164 842 /* 1165 843 * Now that endpoint decoder is set up, walk up the hierarchy 1166 844 * and setup the switch and root port decoders targeting @cxlmd. ··· 1183 859 /* put cxl_mem.4 second in the decode order */ 1184 860 if (pdev->id == 4) { 1185 861 cxlsd->target[1] = dport; 1186 - cxld->target_map[1] = dport->port_id; 862 + cxlsd->cxld.target_map[1] = dport->port_id; 1187 863 } else { 1188 864 cxlsd->target[0] = dport; 1189 - cxld->target_map[0] = dport->port_id; 865 + cxlsd->cxld.target_map[0] = dport->port_id; 1190 866 } 1191 867 } else { 1192 868 cxlsd->target[0] = dport; 1193 - cxld->target_map[0] = dport->port_id; 869 + cxlsd->cxld.target_map[0] = dport->port_id; 1194 870 } 1195 871 cxld = &cxlsd->cxld; 1196 872 cxld->target_type = CXL_DECODER_HOSTONLYMEM; ··· 1209 885 .start = base, 1210 886 .end = base + mock_auto_region_size - 1, 1211 887 }; 888 + cxld->commit = mock_decoder_commit; 889 + cxld->reset = mock_decoder_reset; 890 + 891 + cxld_registry_update(cxld); 1212 892 put_device(dev); 1213 893 } 894 + 895 + return false; 1214 896 } 1215 897 1216 898 static int mock_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm, ··· 1225 895 struct cxl_port *port = cxlhdm->port; 1226 896 struct cxl_port *parent_port = to_cxl_port(port->dev.parent); 1227 897 int target_count, i; 898 + bool restored; 1228 899 1229 900 if (is_cxl_endpoint(port)) 1230 901 target_count = 0; ··· 1265 934 } 1266 935 1267 936 ctx.target_map = cxld->target_map; 1268 - 1269 - mock_init_hdm_decoder(cxld); 1270 - 1271 - if (target_count) { 937 + restored = mock_init_hdm_decoder(cxld); 938 + if (target_count && !restored) { 1272 939 rc = device_for_each_child(port->uport_dev, &ctx, 1273 940 map_targets); 1274 941 if (rc) { ··· 1443 1114 cxl_endpoint_get_perf_coordinates(port, ep_c); 1444 1115 } 1445 1116 1117 + /* 1118 + * Simulate that the first half of mock CXL Window 0 is "Soft Reserve" capacity 1119 + */ 1120 + static int mock_walk_hmem_resources(struct device *host, walk_hmem_fn fn) 1121 + { 1122 + struct acpi_cedt_cfmws *cfmws = mock_cfmws[0]; 1123 + struct resource window = 1124 + DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2); 1125 + 1126 + dev_dbg(host, "walk cxl_test resource: %pr\n", &window); 1127 + return fn(host, 0, &window); 1128 + } 1129 + 1130 + /* 1131 + * This should only be called by the dax_hmem case, treat mismatches (negative 1132 + * result) as "fallback to base region_intersects()". Simulate that the first 1133 + * half of mock CXL Window 0 is IORES_DESC_CXL capacity. 1134 + */ 1135 + static int mock_region_intersects(resource_size_t start, size_t size, 1136 + unsigned long flags, unsigned long desc) 1137 + { 1138 + struct resource res = DEFINE_RES_MEM(start, size); 1139 + struct acpi_cedt_cfmws *cfmws = mock_cfmws[0]; 1140 + struct resource window = 1141 + DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2); 1142 + 1143 + if (resource_overlaps(&res, &window)) 1144 + return REGION_INTERSECTS; 1145 + pr_debug("warning: no cxl_test CXL intersection for %pr\n", &res); 1146 + return -1; 1147 + } 1148 + 1149 + 1150 + static int 1151 + mock_region_intersects_soft_reserve(resource_size_t start, size_t size) 1152 + { 1153 + struct resource res = DEFINE_RES_MEM(start, size); 1154 + struct acpi_cedt_cfmws *cfmws = mock_cfmws[0]; 1155 + struct resource window = 1156 + DEFINE_RES_MEM(cfmws->base_hpa, cfmws->window_size / 2); 1157 + 1158 + if (resource_overlaps(&res, &window)) 1159 + return REGION_INTERSECTS; 1160 + pr_debug("warning: no cxl_test soft reserve intersection for %pr\n", &res); 1161 + return -1; 1162 + } 1163 + 1446 1164 static struct cxl_mock_ops cxl_mock_ops = { 1447 1165 .is_mock_adev = is_mock_adev, 1448 1166 .is_mock_bridge = is_mock_bridge, ··· 1505 1129 .devm_cxl_add_dport_by_dev = mock_cxl_add_dport_by_dev, 1506 1130 .hmat_get_extended_linear_cache_size = 1507 1131 mock_hmat_get_extended_linear_cache_size, 1132 + .walk_hmem_resources = mock_walk_hmem_resources, 1133 + .region_intersects = mock_region_intersects, 1134 + .region_intersects_soft_reserve = mock_region_intersects_soft_reserve, 1508 1135 .list = LIST_HEAD_INIT(cxl_mock_ops.list), 1509 1136 }; 1510 1137 ··· 1794 1415 return rc; 1795 1416 } 1796 1417 1418 + static ssize_t 1419 + decoder_reset_preserve_registry_show(struct device *dev, 1420 + struct device_attribute *attr, char *buf) 1421 + { 1422 + return sysfs_emit(buf, "%d\n", decoder_reset_preserve_registry); 1423 + } 1424 + 1425 + static ssize_t 1426 + decoder_reset_preserve_registry_store(struct device *dev, 1427 + struct device_attribute *attr, 1428 + const char *buf, size_t count) 1429 + { 1430 + int rc; 1431 + 1432 + rc = kstrtobool(buf, &decoder_reset_preserve_registry); 1433 + if (rc) 1434 + return rc; 1435 + return count; 1436 + } 1437 + 1438 + static DEVICE_ATTR_RW(decoder_reset_preserve_registry); 1439 + 1440 + static struct attribute *cxl_acpi_attrs[] = { 1441 + &dev_attr_decoder_reset_preserve_registry.attr, NULL 1442 + }; 1443 + ATTRIBUTE_GROUPS(cxl_acpi); 1444 + 1797 1445 static __init int cxl_test_init(void) 1798 1446 { 1799 1447 int rc, i; ··· 1951 1545 1952 1546 mock_companion(&acpi0017_mock, &cxl_acpi->dev); 1953 1547 acpi0017_mock.dev.bus = &platform_bus_type; 1548 + cxl_acpi->dev.groups = cxl_acpi_groups; 1954 1549 1955 1550 rc = platform_device_add(cxl_acpi); 1956 1551 if (rc) ··· 1961 1554 if (rc) 1962 1555 goto err_root; 1963 1556 1557 + rc = hmem_test_init(); 1558 + if (rc) 1559 + goto err_mem; 1560 + 1964 1561 return 0; 1965 1562 1563 + err_mem: 1564 + cxl_mem_exit(); 1966 1565 err_root: 1967 1566 platform_device_put(cxl_acpi); 1968 1567 err_rch: ··· 2002 1589 return rc; 2003 1590 } 2004 1591 1592 + static void free_decoder_registry(void) 1593 + { 1594 + unsigned long index; 1595 + void *entry; 1596 + 1597 + xa_for_each(&decoder_registry, index, entry) { 1598 + xa_erase(&decoder_registry, index); 1599 + kfree(entry); 1600 + } 1601 + } 1602 + 2005 1603 static __exit void cxl_test_exit(void) 2006 1604 { 2007 1605 int i; 2008 1606 1607 + hmem_test_exit(); 2009 1608 cxl_mem_exit(); 2010 1609 platform_device_unregister(cxl_acpi); 2011 1610 cxl_rch_topo_exit(); ··· 2039 1614 depopulate_all_mock_resources(); 2040 1615 gen_pool_destroy(cxl_mock_pool); 2041 1616 unregister_cxl_mock_ops(&cxl_mock_ops); 1617 + free_decoder_registry(); 1618 + xa_destroy(&decoder_registry); 2042 1619 } 2043 1620 2044 1621 module_param(interleave_arithmetic, int, 0444); 2045 1622 MODULE_PARM_DESC(interleave_arithmetic, "Modulo:0, XOR:1"); 2046 1623 module_param(extended_linear_cache, bool, 0444); 2047 1624 MODULE_PARM_DESC(extended_linear_cache, "Enable extended linear cache support"); 1625 + module_param(fail_autoassemble, bool, 0444); 1626 + MODULE_PARM_DESC(fail_autoassemble, "Simulate missing member of an auto-region"); 2048 1627 module_init(cxl_test_init); 2049 1628 module_exit(cxl_test_exit); 2050 1629 MODULE_LICENSE("GPL v2");
+47
tools/testing/cxl/test/hmem_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2026 Intel Corporation */ 3 + #include <linux/moduleparam.h> 4 + #include <linux/workqueue.h> 5 + #include "../../../drivers/dax/bus.h" 6 + 7 + static bool hmem_test; 8 + 9 + static void hmem_test_work(struct work_struct *work) 10 + { 11 + } 12 + 13 + static void hmem_test_release(struct device *dev) 14 + { 15 + struct hmem_platform_device *hpdev = 16 + container_of(dev, typeof(*hpdev), pdev.dev); 17 + 18 + memset(hpdev, 0, sizeof(*hpdev)); 19 + } 20 + 21 + static struct hmem_platform_device hmem_test_device = { 22 + .pdev = { 23 + .name = "hmem_platform", 24 + .id = 1, 25 + .dev = { 26 + .release = hmem_test_release, 27 + }, 28 + }, 29 + .work = __WORK_INITIALIZER(hmem_test_device.work, hmem_test_work), 30 + }; 31 + 32 + int hmem_test_init(void) 33 + { 34 + if (!hmem_test) 35 + return 0; 36 + 37 + return platform_device_register(&hmem_test_device.pdev); 38 + } 39 + 40 + void hmem_test_exit(void) 41 + { 42 + if (hmem_test) 43 + platform_device_unregister(&hmem_test_device.pdev); 44 + } 45 + 46 + module_param(hmem_test, bool, 0444); 47 + MODULE_PARM_DESC(hmem_test, "Enable/disable the dax_hmem test platform device");
+4 -2
tools/testing/cxl/test/mem.c
··· 1695 1695 struct cxl_dpa_info range_info = { 0 }; 1696 1696 int rc; 1697 1697 1698 + /* Increase async probe race window */ 1699 + usleep_range(500*1000, 1000*1000); 1700 + 1698 1701 mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL); 1699 1702 if (!mdata) 1700 1703 return -ENOMEM; ··· 1719 1716 if (rc) 1720 1717 return rc; 1721 1718 1722 - mds = cxl_memdev_state_create(dev); 1719 + mds = cxl_memdev_state_create(dev, pdev->id + 1, 0); 1723 1720 if (IS_ERR(mds)) 1724 1721 return PTR_ERR(mds); 1725 1722 ··· 1735 1732 mds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf; 1736 1733 INIT_DELAYED_WORK(&mds->security.poll_dwork, cxl_mockmem_sanitize_work); 1737 1734 1738 - cxlds->serial = pdev->id + 1; 1739 1735 if (is_rcd(pdev)) 1740 1736 cxlds->rcd = true; 1741 1737
+50
tools/testing/cxl/test/mock.c
··· 251 251 } 252 252 EXPORT_SYMBOL_NS_GPL(__wrap_devm_cxl_add_dport_by_dev, "CXL"); 253 253 254 + int __wrap_region_intersects(resource_size_t start, size_t size, 255 + unsigned long flags, unsigned long desc) 256 + { 257 + int rc = -1; 258 + int index; 259 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 260 + 261 + if (ops) 262 + rc = ops->region_intersects(start, size, flags, desc); 263 + if (rc < 0) 264 + rc = region_intersects(start, size, flags, desc); 265 + put_cxl_mock_ops(index); 266 + 267 + return rc; 268 + } 269 + EXPORT_SYMBOL_GPL(__wrap_region_intersects); 270 + 271 + int __wrap_region_intersects_soft_reserve(resource_size_t start, size_t size) 272 + { 273 + int rc = -1; 274 + int index; 275 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 276 + 277 + if (ops) 278 + rc = ops->region_intersects_soft_reserve(start, size); 279 + if (rc < 0) 280 + rc = region_intersects_soft_reserve(start, size); 281 + put_cxl_mock_ops(index); 282 + 283 + return rc; 284 + } 285 + EXPORT_SYMBOL_GPL(__wrap_region_intersects_soft_reserve); 286 + 287 + int __wrap_walk_hmem_resources(struct device *host, walk_hmem_fn fn) 288 + { 289 + int index, rc = 0; 290 + bool is_mock = strcmp(dev_name(host), "hmem_platform.1") == 0; 291 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 292 + 293 + if (is_mock) { 294 + if (ops) 295 + rc = ops->walk_hmem_resources(host, fn); 296 + } else { 297 + rc = walk_hmem_resources(host, fn); 298 + } 299 + put_cxl_mock_ops(index); 300 + return rc; 301 + } 302 + EXPORT_SYMBOL_GPL(__wrap_walk_hmem_resources); 303 + 254 304 MODULE_LICENSE("GPL v2"); 255 305 MODULE_DESCRIPTION("cxl_test: emulation module"); 256 306 MODULE_IMPORT_NS("ACPI");
+8
tools/testing/cxl/test/mock.h
··· 2 2 3 3 #include <linux/list.h> 4 4 #include <linux/acpi.h> 5 + #include <linux/dax.h> 5 6 #include <cxl.h> 6 7 7 8 struct cxl_mock_ops { ··· 28 27 int (*hmat_get_extended_linear_cache_size)(struct resource *backing_res, 29 28 int nid, 30 29 resource_size_t *cache_size); 30 + int (*walk_hmem_resources)(struct device *host, walk_hmem_fn fn); 31 + int (*region_intersects)(resource_size_t start, size_t size, 32 + unsigned long flags, unsigned long desc); 33 + int (*region_intersects_soft_reserve)(resource_size_t start, 34 + size_t size); 31 35 }; 32 36 37 + int hmem_test_init(void); 38 + void hmem_test_exit(void); 33 39 void register_cxl_mock_ops(struct cxl_mock_ops *ops); 34 40 void unregister_cxl_mock_ops(struct cxl_mock_ops *ops); 35 41 struct cxl_mock_ops *get_cxl_mock_ops(int *index);