Re: [PATCH v6 7/7] cxl/test: Add Dynamic Capacity tests
From: Dave Jiang
Date: Mon Jun 08 2026 - 20:24:21 EST
On 5/23/26 2:50 AM, Anisa Su wrote:
> From: Ira Weiny <ira.weiny@xxxxxxxxx>
>
> cxl_test provides a good way to ensure quick smoke and regression
> testing. The complexity of DCD and the new sparse DAX regions
> required to use them benefits greatly with a series of smoke tests.
>
> The only part of the kernel stack which must be bypassed is the actual
> irq of DCD events. However, the event processing itself can be tested
> via cxl_test calling directly into the event processing.
>
> In this way the rest of the stack; management of sparse regions, the
> extent device lifetimes, and the dax device operations can be tested.
>
> Add Dynamic Capacity Device tests for kernels which have DCD support.
>
> Signed-off-by: Ira Weiny <ira.weiny@xxxxxxxxx>
> Signed-off-by: Anisa Su <anisa.su887@xxxxxxxxx>
A nit below. Otherwise
Reviewed-by: Dave Jiang <dave.jiang@xxxxxxxxx>
>
> ---
> Changes:
> [anisa: align tests with kernel DCD redesign + add sharable coverage]
>
> Rewrite tests against the post-redesign kernel (uuid sysfs claim,
> sparse size-grow rejection, tag-group atomic release, cross-More
> uniqueness):
>
> - Use real UUID strings; route untagged claims via --uuid "0" so
> daxctl exercises the kernel uuid_store path end to end.
> - Pre-existing-extent test handles the tagged pre2_ext (deadbeef)
> separately from the untagged pre_ext.
> - Spanning / aggregation tests drive both extents in one More-chain:
> cross-event tagged adds are dropped by the cross-More uniqueness
> gate, and untagged events become independent dax_resources so
> only one is claimed per --uuid "0".
> - test_dax_device_ops asserts that sparse size grow returns
> -EOPNOTSUPP after a shrink (real grow path is --uuid only).
> - test_reject_overlapping math now produces an actual overlap
> inside the DC region (the prior arithmetic landed past the end).
>
> Add coverage for new validators:
> - test_uuid_no_match / test_uuid_no_match_seed_intact
> - test_uuid_show (reads back the dax-dev uuid sysfs attribute)
> - test_cross_more_uniqueness (rejects tag reuse across More-chains)
> - test_alignment_rejection (rejects misaligned extents)
>
> Sharable-partition coverage (test_shared_extent_inject,
> test_seq_integrity_gap) is routed at runtime to a dedicated mock
> memdev that tools/testing/cxl stamps with serial 0xDCDC; the script
> enumerates both regimes from one cxl_test module load.
>
> Localize positional-arg assignments in every helper so functions
> no longer clobber caller globals (the previous behavior leaked
> the sharable memdev into later tests).
>
> Update inject_extent to optionally pass shared_extn_seq (5th arg)
> and remove_extent to carry the tag (now required by the mock's
> delete sysfs). Add inject_shared_extent for dc_inject_shared_extent.
> ---
> test/cxl-dcd.sh | 1267 ++++++++++++++++++++++++++++++++++++++++++++++
> test/meson.build | 2 +
> 2 files changed, 1269 insertions(+)
> create mode 100644 test/cxl-dcd.sh
>
> diff --git a/test/cxl-dcd.sh b/test/cxl-dcd.sh
> new file mode 100644
> index 0000000..3194b00
> --- /dev/null
> +++ b/test/cxl-dcd.sh
> @@ -0,0 +1,1267 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2024 Intel Corporation. All rights reserved.
> +
> +. "$(dirname "$0")/common"
> +
> +rc=77
> +set -ex
> +
> +trap 'err $LINENO' ERR
> +
> +check_prereq "jq"
> +
> +rc=1
> +
> +dev_path="/sys/bus/platform/devices"
> +cxl_path="/sys/bus/cxl/devices"
> +
> +# test extent tags (UUIDs). pre_ext_tag matches the second pre-injected
> +# extent tag in the kernel mock (tools/testing/cxl/test/mem.c).
> +pre_ext_tag="deadbeef-cafe-baad-f00d-fedcba987654"
> +test_tag_a="11111111-1111-1111-1111-111111111111"
> +test_tag_b="22222222-2222-2222-2222-222222222222"
> +unknown_tag="33333333-3333-3333-3333-333333333333"
> +
> +#
> +# The test devices have 2G of non DC capacity. A single DC reagion of 1G is
> +# added beyond that.
> +#
> +# The testing centers around 3 extents. Two are "pre-existing" on test load
> +# called pre-ext and pre2-ext. The other is created within this script alone
> +# called base.
> +
> +#
> +# | 2G non- | DC region (1G) |
> +# | DC cap | |
> +# | ... |-------------------------------------------------------|
> +# | |--------| |----------| |----------| |
> +# | | (base) | |(pre-ext) | |(pre2-ext)| |
> +
> +dra_size=""
> +
> +base_dpa=0x80000000
> +
> +# base extent at dpa 2G - 64M long
> +base_ext_offset=0x0
> +base_ext_dpa=$(($base_dpa + $base_ext_offset))
> +base_ext_length=0x4000000
> +
> +# pre existing extent base + 128M, 64M length
> +# 0x00000088000000-0x0000008bffffff
> +pre_ext_offset=0x8000000
> +pre_ext_dpa=$(($base_dpa + $pre_ext_offset))
> +pre_ext_length=0x4000000
> +
> +# pre2 existing extent base + 256M, 64M length
> +# 0x00000090000000-0x00000093ffffff
> +pre2_ext_offset=0x10000000
> +pre2_ext_dpa=$(($base_dpa + $pre2_ext_offset))
> +pre2_ext_length=0x4000000
> +
> +mem=""
> +bus=""
> +device=""
> +decoder=""
> +
> +# ========================================================================
> +# Support functions
> +# ========================================================================
> +
> +create_dcd_region()
> +{
> + local mem="$1"
> + local decoder="$2"
> + local reg_size_string=""
> + local region
> + if [ "$3" != "" ]; then
> + reg_size_string="-s $3"
> + fi
> +
> + # create region
> + region=$($CXL create-region -t dynamic_ram_a -d "$decoder" -m "$mem" ${reg_size_string} | jq -r ".region")
> +
> + if [[ ! $region ]]; then
> + echo "create-region failed for $decoder / $mem"
> + err "$LINENO"
> + fi
> +
> + echo ${region}
> +}
> +
> +check_region()
> +{
> + local search=$1
> + local region_size=$2
> + local result
> +
> + result=$($CXL list -r "$search" | jq -r ".[].region")
> + if [ "$result" != "$search" ]; then
> + echo "check region failed to find $search"
> + err "$LINENO"
> + fi
> +
> + result=$($CXL list -r "$search" | jq -r ".[].size")
> + if [ "$result" != "$region_size" ]; then
> + echo "check region failed invalid size $result != $region_size"
> + err "$LINENO"
> + fi
> +}
> +
> +check_not_region()
> +{
> + local search=$1
> + local result
> +
> + result=$($CXL list -r "$search" | jq -r ".[].region")
> + if [ "$result" == "$search" ]; then
> + echo "check not region failed; $search found"
> + err "$LINENO"
> + fi
> +}
> +
> +destroy_region()
> +{
> + local region=$1
> + $CXL disable-region $region
> + $CXL destroy-region $region
> +}
> +
> +inject_extent()
> +{
> + local device="$1"
> + local dpa="$2"
> + local length="$3"
> + local tag="$4"
> + local seq="$6"
> + local more="0"
> + local cmd
> +
> + if [ "$5" != "" ]; then
> + more="1"
> + fi
> +
> + cmd="${dpa}:${length}:${tag}:${more}"
> + if [ -n "$seq" ]; then
> + cmd="${cmd}:${seq}"
> + fi
> + echo ${cmd} > "${dev_path}/${device}/dc_inject_extent"
> +}
> +
> +# Shared-extent inject targets a *sharable* DC partition. The mock
> +# stamps the sentinel serial onto a single cxl_mem instance, and the
> +# script routes shared-extent tests to that memdev via $sharable_device.
> +inject_shared_extent()
> +{
> + local device="$1"
> + local dpa="$2"
> + local length="$3"
> + local tag="$4"
> + local seq="$6"
> + local more="0"
> + local cmd
> +
> + if [ "$5" != "" ]; then
> + more="1"
> + fi
> +
> + cmd="${dpa}:${length}:${tag}:${more}"
> + if [ -n "$seq" ]; then
> + cmd="${cmd}:${seq}"
> + fi
> + echo ${cmd} > "${dev_path}/${device}/dc_inject_shared_extent"
> +}
> +
> +remove_extent()
> +{
> + local device="$1"
> + local dpa="$2"
> + local length="$3"
> + local tag="$4"
> +
> + echo ${dpa}:${length}:${tag} > "${dev_path}/${device}/dc_del_extent"
> +}
> +
> +create_dax_dev()
> +{
> + local reg="$1"
> + local dax_dev
> +
> + dax_dev=$($DAXCTL create-device -r $reg | jq -er '.[].chardev')
> +
> + echo ${dax_dev}
> +}
> +
> +create_dax_dev_with_uuid()
> +{
> + local reg="$1"
> + local uuid="$2"
> + local dax_dev
> +
> + dax_dev=$($DAXCTL create-device -r $reg --uuid "$uuid" \
> + | jq -er '.[].chardev')
> +
> + echo ${dax_dev}
> +}
> +
> +fail_create_dax_dev_with_uuid()
> +{
> + local reg="$1"
> + local uuid="$2"
> + local create_rc
> +
> + set +e
> + $DAXCTL create-device -r $reg --uuid "$uuid"
> + create_rc=$?
> + set -e
> + if [ $create_rc -eq 0 ]; then
> + echo "FAIL create-device with uuid $uuid unexpectedly succeeded"
> + err "$LINENO"
> + fi
> +}
> +
> +fail_create_dax_dev()
> +{
> + local reg="$1"
> + local result
> +
> + set +e
> + result=$($DAXCTL create-device -r $reg)
> + set -e
> + if [ "$result" == "0" ]; then
> + echo "FAIL device created"
> + err "$LINENO"
> + fi
> +}
> +
> +# Try to resize a sparse dax device via reconfigure -s; must fail because
> +# the kernel rejects any non-zero size_store on a dynamic dax region with
> +# -EOPNOTSUPP (drivers/dax/bus.c:dev_dax_resize). The only valid resize is
> +# to size 0, which is the path `daxctl destroy-device` takes.
> +fail_resize_dax_dev()
> +{
> + local dev="$1"
> + local new_size="$2"
> + local resize_rc
> +
> + $DAXCTL disable-device $dev
> + set +e
> + $DAXCTL reconfigure-device $dev -s $new_size
> + resize_rc=$?
> + set -e
> + # Re-enable regardless so subsequent checks/cleanup work.
> + $DAXCTL enable-device $dev
> + if [ $resize_rc -eq 0 ]; then
> + echo "FAIL resize of $dev to $new_size unexpectedly succeeded"
> + err "$LINENO"
> + fi
> +}
> +
> +# Read /sys/bus/dax/devices/<dax>/uuid and compare to $expected. "0"
> +# matches the null UUID (kernel emits "0\n" when the resource is empty,
> +# untagged, or non-DCD).
> +check_dax_uuid()
> +{
> + local dax_dev="$1"
> + local expected="$2"
> + local uuid_path got want
> +
> + uuid_path="/sys/bus/dax/devices/${dax_dev}/uuid"
> + if [ ! -e "$uuid_path" ]; then
> + echo "FAIL no uuid attribute at $uuid_path"
> + err "$LINENO"
> + fi
> + got=$(cat "$uuid_path" | tr -d '[:space:]')
> + want=$(echo "$expected" | tr -d '[:space:]')
> + if [ "$got" != "$want" ]; then
> + echo "FAIL uuid show on $dax_dev: got '$got' want '$want'"
> + err "$LINENO"
> + fi
> +}
> +
> +destroy_dax_dev()
> +{
> + local dev="$1"
> +
> + $DAXCTL disable-device $dev
> + $DAXCTL destroy-device $dev
> +}
> +
> +check_dax_dev()
> +{
> + local search="$1"
> + local size=$(($2))
> + local result
> +
> + result=$($DAXCTL list -d $search | jq -er '.[].chardev')
> + if [ "$result" != "$search" ]; then
> + echo "check dax device failed to find $search"
> + err "$LINENO"
> + fi
> + result=$($DAXCTL list -d $search | jq -er '.[].size')
> + if [ "$result" -ne "$size" ]; then
> + echo "check dax device failed incorrect size $result; exp $size"
> + err "$LINENO"
> + fi
> +}
> +
> +# check that the dax device is not there.
> +check_not_dax_dev()
> +{
> + local reg="$1"
> + local search="$2"
> + local result
> + result=$($DAXCTL list -r $reg -D | jq -r '.[].chardev')
> + if [ "$result" == "$search" ]; then
> + echo "FAIL found $search"
> + err "$LINENO"
> + fi
> +}
> +
> +check_extent()
> +{
> + local region=$1
> + local offset=$(($2))
> + local length=$(($3))
> + local result
> +
> + result=$($CXL list -r "$region" -N | jq -r ".[].extents[] | select(.offset == ${offset}) | .length")
> + if [[ $result != $length ]]; then
> + echo "FAIL region $1 could not find extent @ $offset ($length)"
> + err "$LINENO"
> + fi
> +}
> +
> +check_extent_cnt()
> +{
> + local region=$1
> + local count=$(($2))
> + local result
> +
> + result=$($CXL list -r $region -N | jq -r '.[].extents[].offset' | wc -l)
> + if [[ $result != $count ]]; then
> + echo "FAIL region $1: found wrong number of extents $result; expect $count"
> + err "$LINENO"
> + fi
> +}
> +
> +
> +# ========================================================================
> +# Tests
> +# ========================================================================
> +
> +# testing pre existing extents must be called first as the extents were created
> +# by cxl-test being loaded. The mock fixture is one untagged extent at
> +# pre_ext_dpa and one tagged (pre_ext_tag) extent at pre2_ext_dpa, so the
> +# untagged extent is claimed via --uuid "0" and the tagged one via its UUID.
> +test_pre_existing_extents()
> +{
> + echo ""
> + echo "Test: pre-existing extents (untagged + tagged)"
> + echo ""
> + region=$(create_dcd_region ${mem} ${decoder})
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + # | | |----------| |----------| |
> + # | | |(pre-ext) | |(pre2-ext)| |
> + # | | | untagged | | tagged | |
> + check_region ${region} ${dra_size}
> + check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> + check_extent ${region} ${pre2_ext_offset} ${pre2_ext_length}
> +
> + # Untagged claim picks up the untagged pre-extent only
> + dax_dev_u=$(create_dax_dev_with_uuid ${region} "0")
> + check_dax_dev ${dax_dev_u} $pre_ext_length
> +
> + # Tagged claim picks up the tagged pre2-extent
> + dax_dev_t=$(create_dax_dev_with_uuid ${region} "$pre_ext_tag")
> + check_dax_dev ${dax_dev_t} $pre2_ext_length
> +
> + destroy_dax_dev ${dax_dev_u}
> + destroy_dax_dev ${dax_dev_t}
> +
> + # Release events must carry the tag identity so cxl_rm_extent's
> + # uuid_equal lookup matches the right region_extent.
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + remove_extent ${device} $pre2_ext_dpa $pre2_ext_length "$pre_ext_tag"
> + check_extent_cnt ${region} 0
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_remove_extent_under_dax_device()
> +{
> + # Remove the pre-created test extent out from under dax device
> + # stack should hold ref until dax device deleted
> + echo ""
> + echo "Test: Remove extent from under DAX dev"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + # | | |
> + # | | |
> +
> +
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + # | | |----------| |
> + # | | |(pre-ext) | |
> + check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> +
> + # Sparse seed starts at size 0; --uuid "0" is the only sizing path.
> + dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> + # | | |----------| |
> + # | | |(pre-ext) | |
> + # | | | daxX.1 | |
> + check_extent_cnt ${region} 1
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + # In-use extents are not released (dax-layer EBUSY defers release).
> + check_dax_dev ${dax_dev} $pre_ext_length
> +
> + check_extent_cnt ${region} 1
> + destroy_dax_dev ${dax_dev}
> + # | | |----------| |
> + # | | |(pre-ext) | |
> + check_not_dax_dev ${region} ${dax_dev}
> +
> + check_extent_cnt ${region} 1
> + # Re-issuing the release after the dax device dropped its hold
> + # completes the deferred release.
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + # | | |
> + # | | |
> + check_extent_cnt ${region} 0
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_remove_extents_in_use()
> +{
> + echo ""
> + echo "Test: Remove extents under sparse dax device"
> + echo ""
> + # Tagged release events are deferred while a dax device pins the
> + # tag group; extent count stays at 2.
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 2
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 2
> +}
> +
> +test_create_dax_dev_spanning_two_extents()
> +{
> + echo ""
> + echo "Test: Create dax device spanning 2 extents in a tagged group"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + #
> + # A single dax device spanning two extents is only possible when
> + # both extents belong to the same tagged More-chain. Cross-event
> + # tagged adds are rejected by the cross-More uniqueness gate;
> + # untagged adds become independent dax_resources and only one is
> + # claimed per --uuid "0". Drive both extents in one More-chain:
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a" 1
> + check_extent_cnt ${region} 0 # held off until More=0 closes chain
> + inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> + check_extent ${region} ${base_ext_offset} ${base_ext_length}
> + # | |--------| |----------| |
> + # | | (base) | |(pre-ext) | tag_a |
> +
> + check_extent_cnt ${region} 2
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + # | |--------| |----------| |
> + # | | (base) | |(pre-ext) | |
> + # | | daxX.1 | | daxX.1 | |
> +
> + echo "Checking if dev dax is spanning sparse extents"
> + ext_sum_length="$(($base_ext_length + $pre_ext_length))"
> + check_dax_dev ${dax_dev} $ext_sum_length
> +
> + test_remove_extents_in_use
> +
> + destroy_dax_dev ${dax_dev}
> + check_not_dax_dev ${region} ${dax_dev}
> +
> + # In-use extents were not released. Check they can be removed after
> + # the dax device is removed.
> + check_extent_cnt ${region} 2
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 0
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_inject_tag_support()
> +{
> + echo ""
> + echo "Test: inject untagged + tagged extents, claim each via --uuid"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # untagged extent
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> + # tagged extent (different DPA so both can land)
> + inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +
> + # both extents should be accepted
> + check_extent_cnt ${region} 2
> +
> + # claim the tagged extent into one dax device
> + dax_dev_a=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + check_dax_dev ${dax_dev_a} $base_ext_length
> +
> + # claim the untagged extent into a second dax device via "0"
> + dax_dev_0=$(create_dax_dev_with_uuid ${region} "0")
> + check_dax_dev ${dax_dev_0} $pre_ext_length
> +
> + destroy_dax_dev ${dax_dev_a}
> + destroy_dax_dev ${dax_dev_0}
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 0
> +
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_uuid_no_match()
> +{
> + echo ""
> + echo "Test: daxctl create-device --uuid with no matching extent fails"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # inject only one tagged extent; ask daxctl to claim a different uuid
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 1
> +
> + fail_create_dax_dev_with_uuid ${region} "$unknown_tag"
> +
> + # the extent should still be claimable via its real tag
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + check_dax_dev ${dax_dev} $pre_ext_length
> + destroy_dax_dev ${dax_dev}
> +
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 0
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_uuid_aggregation()
> +{
> + echo ""
> + echo "Test: multiple extents in a single More-chain aggregate under one tag"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # Both extents must arrive in the same More-chain to land in the
> + # same tag group. Re-using a tag across More-chains hits the
> + # cross-More uniqueness gate and the second event would be dropped.
> + inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a" 1
> + check_extent_cnt ${region} 0
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 2
> +
> + # a single --uuid claim should pick up both extents
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + expected=$(($base_ext_length + $pre_ext_length))
> + check_dax_dev ${dax_dev} $expected
> +
> + destroy_dax_dev ${dax_dev}
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +
> +test_partial_extent_remove ()
> +{
> + echo ""
> + echo "Test: partial extent remove"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> +
> + inject_extent ${device} $base_ext_dpa $base_ext_length ""
> + # | ... |-------------------------------------------------------|
> + # | |--------| |
> + # | | (base) | |
> +
> + dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> +
> + # | ... |-------------------------------------------------------|
> + # | |--------| |
> + # | | (base) | |
> + # | | daxX.1 | |
> +
> + partial_ext_dpa="$(($base_ext_dpa + ($base_ext_length / 2)))"
> + partial_ext_length="$(($base_ext_length / 2))"
> + echo "Removing Partial : $partial_ext_dpa $partial_ext_length"
> +
> + # | | |---| |
> + # Partial
> +
> + remove_extent ${device} $partial_ext_dpa $partial_ext_length ""
> + # In-use extents are not released.
> + check_extent_cnt ${region} 1
> +
> + # | ... |-------------------------------------------------------|
> + # | |--------| |
> + # | | (base) | |
> + # | | daxX.1 | |
> +
> + destroy_dax_dev ${dax_dev}
> + check_not_dax_dev ${region} ${dax_dev}
> +
> + # | ... |-------------------------------------------------------|
> + # | |--------| |
> + # | | (base) | |
> +
> + # Partial release event re-targets the whole containing region_extent.
> + check_extent_cnt ${region} 1
> + remove_extent ${device} $partial_ext_dpa $partial_ext_length ""
> + # | | |---| |
> + # Partial
> + check_extent_cnt ${region} 0
> +
> + # | ... |-------------------------------------------------------|
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_multiple_extent_remove ()
> +{
> + # Per-tag-group release: a release event whose DPA range straddles
> + # multiple region_extents in the same tag group should target them
> + # atomically. Set up two extents in one tagged More-chain.
> + echo ""
> + echo "Test: per-group release event covering two extents"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> +
> + inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a" 1
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + # | ... |-------------------------------------------------------|
> + # | |--------| |-------------------| |
> + # | | (base) | | (pre)-existing | tag_a |
> +
> + check_extent_cnt ${region} 2
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> +
> + # | | daxX.1 | | daxX.1 | tag_a |
> +
> + # A release event addressed at any DPA inside the tag group releases
> + # the whole group atomically. Use base_ext_dpa so the kernel finds
> + # the tag group's region_extent on that DPA.
> + echo "Issuing tag-group release at $base_ext_dpa"
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +
> + # In-use extents are not released (dax-layer EBUSY defer).
> + check_extent_cnt ${region} 2
> +
> + destroy_dax_dev ${dax_dev}
> + check_not_dax_dev ${region} ${dax_dev}
> +
> + # | ... |-------------------------------------------------------|
> + # | |--------| |-------------------| |
> + # | | (base) | | (pre)-existing | |
> +
> + # Now that the dax dev is gone, re-issue the release(s) for each
> + # extent. Each release event targets the containing region_extent.
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 0
> +
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_destroy_region_without_extent_removal ()
> +{
> + echo ""
> + echo "Test: Destroy region without extent removal"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + inject_extent ${device} $base_ext_dpa $base_ext_length ""
> + check_extent_cnt ${region} 2
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_destroy_with_extent_and_dax ()
> +{
> + echo ""
> + echo "Test: Destroy region with extents and dax devices"
> + echo ""
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + check_extent_cnt ${region} 0
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + # | | |----------| |
> + # | | |(pre-ext) | |
> +
> + check_extent_cnt ${region} 1
> + dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> + # | | |<dax_dev> | |
> + check_dax_dev ${dax_dev} ${pre_ext_length}
> + destroy_region ${region}
> + check_not_region ${region}
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + # | | |
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + check_extent_cnt ${region} 0
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_dax_device_ops ()
> +{
> + echo ""
> + echo "Test: Fail sparse dax dev creation without space"
> + echo ""
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + # | | |-------------------| |
> + # | | | (pre)-existing | |
> +
> + check_extent_cnt ${region} 1
> +
> + # | | | daxX.1 | |
> +
> + dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> + check_dax_dev ${dax_dev} $pre_ext_length
> + # No more untagged dax_resource avail: untagged claim returns -ENOENT
> + fail_create_dax_dev_with_uuid ${region} "0"
> + # Plain create-device on sparse: size grow is -EOPNOTSUPP
> + fail_create_dax_dev ${region}
> +
> + # DC dax devices cannot be resized to a non-zero size. The
> + # kernel rejects any size_store with -EOPNOTSUPP unless size == 0,
> + # in which case dev_dax_shrink releases every range -- the same path
> + # `daxctl destroy-device` takes.
> + echo ""
> + echo "Test: Resize of sparse dax device to non-zero is rejected"
> + echo ""
> + half=$(($pre_ext_length / 2))
> + fail_resize_dax_dev ${dax_dev} $half
> + check_dax_dev ${dax_dev} $pre_ext_length
> +
> + # Destroy (size=0) is the only valid resize. After it the
> + # dax_resource has avail again, so --uuid "0" can claim it back
> + # into a new dax device.
> + # | | | daxX.2 | |
> + echo ""
> + echo "Test: Destroy (size=0) of sparse dax device releases the resource"
> + echo ""
> + destroy_dax_dev ${dax_dev}
> + check_not_dax_dev ${region} ${dax_dev}
> + dax_dev2=$(create_dax_dev_with_uuid ${region} "0")
> + check_dax_dev ${dax_dev2} $pre_ext_length
> +
> + destroy_region ${region}
> + check_not_region ${region}
> +
> + # Multi-extent device must come from a single tagged group; cross-
> + # event untagged adds land in independent dax_resources and cannot
> + # be claimed as one dax device. The no-resize rule applies equally
> + # across the tagged group.
> + echo ""
> + echo "Test: Resize of tagged multi-extent dax device is rejected"
> + echo ""
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a" 1
> + inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + # | |--------| |-------------------| |
> + # | | (base) | | (pre)-existing | tag_a |
> +
> + check_extent_cnt ${region} 2
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + ext_sum_length="$(($base_ext_length + $pre_ext_length))"
> + check_dax_dev ${dax_dev} $ext_sum_length
> +
> + # Any non-zero resize (here a shrink to 32M, which would sit inside
> + # the first extent of the group) is rejected.
> + fail_resize_dax_dev ${dax_dev} 33554432 # 32MB
> + check_dax_dev ${dax_dev} $ext_sum_length
> +
> + # Only size=0 / destroy is valid for the tagged group too.
> + destroy_dax_dev ${dax_dev}
> + check_not_dax_dev ${region} ${dax_dev}
> +
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_reject_overlapping ()
> +{
> + echo ""
> + echo "Test: Rejecting overlapping extents"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> +
> + # | 2G non- | DC region |
> + # | DC cap | |
> + # | ... |-------------------------------------------------------|
> + # | | |-------------------| |
> + # | | | (pre)-existing | |
> +
> + check_extent_cnt ${region} 1
> +
> + # Attempt overlapping extent: start halfway through pre_ext, same
> + # length so it straddles the end of pre_ext.
> + #
> + # | | |---------|---------| |
> + # | | |(pre-ext)| overlap | |
> +
> + partial_ext_dpa="$(($pre_ext_dpa + ($pre_ext_length / 2)))"
> + partial_ext_length=$pre_ext_length
> + inject_extent ${device} $partial_ext_dpa $partial_ext_length ""
> +
> + # Should only see the original ext
> + check_extent_cnt ${region} 1
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_two_regions()
> +{
> + echo ""
> + echo "Test: create 2 regions in the same DC partition"
> + echo ""
> + region_size=$(($dra_size / 2))
> + region=$(create_dcd_region ${mem} ${decoder} ${region_size})
> + check_region ${region} ${region_size}
> +
> + region_two=$(create_dcd_region ${mem} ${decoder} ${region_size})
> + check_region ${region_two} ${region_size}
> +
> + destroy_region ${region_two}
> + check_not_region ${region_two}
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_more_bit()
> +{
> + echo ""
> + echo "Test: More bit"
> + echo ""
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "" 1
> + # More bit should hold off surfacing extent until the more bit is 0
> + check_extent_cnt ${region} 0
> + inject_extent ${device} $base_ext_dpa $base_ext_length ""
> + check_extent_cnt ${region} 2
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_driver_tear_down()
> +{
> + echo ""
> + echo "Test: driver remove tear down"
> + echo ""
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + check_extent ${region} ${pre_ext_offset} ${pre_ext_length}
> + dax_dev=$(create_dax_dev_with_uuid ${region} "0")
> + # remove driver releases extents
> + modprobe -r dax_cxl
> + check_extent_cnt ${region} 0
> +}
> +
> +test_driver_bring_up()
> +{
> + # leave region up, driver removed.
> + echo ""
> + echo "Test: no driver inject ok"
> + echo ""
> + check_region ${region} ${dra_size}
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + check_extent_cnt ${region} 1
> +
> + modprobe dax_cxl
> + check_extent_cnt ${region} 1
> +
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_driver_reload()
> +{
> + test_driver_tear_down
> + test_driver_bring_up
> +}
> +
> +# Verify the dax dev's uuid sysfs attribute reflects the claim source:
> +# "0" for untagged seed/claim, the tag string for tagged claims.
> +test_uuid_show()
> +{
> + echo ""
> + echo "Test: dax dev uuid attribute reflects the claim source"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # Pre-claim the seed device's uuid (before any extent injected)
> + # must read back as "0". -i includes the idle seed at size 0.
> + seed=$($DAXCTL list -r ${region} -D -i | jq -r '.[].chardev')
> + check_dax_uuid ${seed} "0"
> +
> + # Untagged extent + claim: uuid stays "0"
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + dax_u=$(create_dax_dev_with_uuid ${region} "0")
> + check_dax_uuid ${dax_u} "0"
> +
> + # Tagged extent + claim: uuid reads back as the tag
> + inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + dax_t=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + check_dax_uuid ${dax_t} "$test_tag_a"
> +
> + destroy_dax_dev ${dax_u}
> + destroy_dax_dev ${dax_t}
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length ""
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +# --uuid <unknown> on a sparse region must return -ENOENT before any
> +# size is committed. The region's available_size must be unchanged and
> +# the underlying extent must still be claimable via its real tag.
> +test_uuid_no_match_seed_intact()
> +{
> + echo ""
> + echo "Test: --uuid mismatch does not consume any extent space"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 1
> +
> + # Capture region available size before the failed claim
> + avail_before=$($DAXCTL list -r ${region} | jq -r '.[].available_size')
> +
> + fail_create_dax_dev_with_uuid ${region} "$unknown_tag"
> +
> + avail_after=$($DAXCTL list -r ${region} | jq -r '.[].available_size')
> + if [ "$avail_before" != "$avail_after" ]; then
> + echo "FAIL avail size changed by unmatched --uuid: $avail_before -> $avail_after"
> + err "$LINENO"
> + fi
> +
> + # The real tag still claims successfully
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + check_dax_dev ${dax_dev} $pre_ext_length
> +
> + destroy_dax_dev ${dax_dev}
> + remove_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +# A second tagged Add-event reusing a previously committed tag must be
> +# dropped by the cross-More uniqueness gate (firmware-bug path). Skipped
> +# for the null UUID.
> +test_cross_more_uniqueness()
> +{
> + echo ""
> + echo "Test: cross-More uniqueness drops second event with same tag"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # First Add-event (single extent) commits tag_a at base_ext_dpa.
> + inject_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 1
> +
> + # Second Add-event reuses tag_a at a different DPA: must be dropped.
> + inject_extent ${device} $pre_ext_dpa $pre_ext_length "$test_tag_a"
> + check_extent_cnt ${region} 1
> +
> + # The original tag_a allocation is still usable.
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_a")
> + check_dax_dev ${dax_dev} $base_ext_length
> +
> + destroy_dax_dev ${dax_dev}
> + remove_extent ${device} $base_ext_dpa $base_ext_length "$test_tag_a"
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +# Sharable-partition test: two extents in one tagged More-chain with
> +# device-stamped seq 1..2. Uses the sharable memdev (serial 0xDCDC).
> +test_shared_extent_inject()
> +{
> + echo ""
> + echo "Test: shared extent inject on sharable partition"
> + echo ""
> +
> + region=$(create_dcd_region ${sharable_mem} ${sharable_decoder})
> + check_region ${region} ${sharable_dra_size}
> +
> + inject_shared_extent ${sharable_device} $base_ext_dpa $base_ext_length \
> + "$test_tag_b" 1 1
> + check_extent_cnt ${region} 0
> + inject_shared_extent ${sharable_device} $pre_ext_dpa $pre_ext_length \
> + "$test_tag_b" "" 2
> + check_extent_cnt ${region} 2
> +
> + dax_dev=$(create_dax_dev_with_uuid ${region} "$test_tag_b")
> + expected=$(($base_ext_length + $pre_ext_length))
> + check_dax_dev ${dax_dev} $expected
> +
> + destroy_dax_dev ${dax_dev}
> + remove_extent ${sharable_device} $base_ext_dpa $base_ext_length "$test_tag_b"
> + remove_extent ${sharable_device} $pre_ext_dpa $pre_ext_length "$test_tag_b"
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +# Sharable extents must arrive with a dense 1..n shared_extn_seq. A gap
> +# (1, 3) lets the cxl side accept the extents but uuid_claim_tagged
> +# refuses the group on its density check. Uses the sharable memdev.
> +test_seq_integrity_gap()
> +{
> + echo ""
> + echo "Test: sharable extents with seq gap (1,3) refused on claim"
> + echo ""
> +
> + region=$(create_dcd_region ${sharable_mem} ${sharable_decoder})
> + check_region ${region} ${sharable_dra_size}
> +
> + inject_shared_extent ${sharable_device} $base_ext_dpa $base_ext_length \
> + "$test_tag_b" 1 1
> + check_extent_cnt ${region} 0
> + inject_shared_extent ${sharable_device} $pre_ext_dpa $pre_ext_length \
> + "$test_tag_b" "" 3
> +
> + if [ "$(jq -r '.[].extents | length' <($CXL list -r ${region} -N))" = "2" ]; then
> + fail_create_dax_dev_with_uuid ${region} "$test_tag_b"
> + remove_extent ${sharable_device} $base_ext_dpa $base_ext_length "$test_tag_b"
> + remove_extent ${sharable_device} $pre_ext_dpa $pre_ext_length "$test_tag_b"
> + fi
> + check_extent_cnt ${region} 0
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +# CXL_DCD_EXTENT_ALIGN is 2M; an extent that is not 2M-aligned must drop
> +# the whole group. Inject a 64M extent at a 1M-offset DPA.
> +test_alignment_rejection()
> +{
> + echo ""
> + echo "Test: misaligned extent drops the group"
> + echo ""
> +
> + region=$(create_dcd_region ${mem} ${decoder})
> + check_region ${region} ${dra_size}
> +
> + # 1M past base — not 2M aligned
> + mis_dpa=$(($base_ext_dpa + 0x100000))
> + inject_extent ${device} $mis_dpa $base_ext_length ""
> + check_extent_cnt ${region} 0
> +
> + destroy_region ${region}
> + check_not_region ${region}
> +}
> +
> +test_event_reporting()
> +{
> + # Test event reporting
> + # results expected
> + num_dcd_events_expected=2
> +
> + echo "Test: Prep event trace"
> + echo "" > /sys/kernel/tracing/trace
> + echo 1 > /sys/kernel/tracing/events/cxl/enable
> + echo 1 > /sys/kernel/tracing/tracing_on
> +
> + inject_extent ${device} $base_ext_dpa $base_ext_length ""
> + remove_extent ${device} $base_ext_dpa $base_ext_length
Missing the tag arg
DJ
> +
> + echo 0 > /sys/kernel/tracing/tracing_on
> +
> + echo "Test: Events seen"
> + trace_out=$(cat /sys/kernel/tracing/trace)
> +
> + # Look for DCD events
> + num_dcd_events=$(grep -c "cxl_dynamic_capacity" <<< "${trace_out}")
> + echo " LOG (Expected) : (Found)"
> + echo " DCD events ($num_dcd_events_expected) : $num_dcd_events"
> +
> + if [ "$num_dcd_events" -ne $num_dcd_events_expected ]; then
> + err "$LINENO"
> + fi
> +}
> +
> +
> +# ========================================================================
> +# main()
> +# ========================================================================
> +
> +modprobe -r cxl_test
> +modprobe cxl_test
> +
> +# The mock stamps a single cxl_mem instance with this serial (0xDCDC).
> +# That memdev's DC partition is advertised as sharable in CDAT and is
> +# the only one that can host the shared-extent tests. All other DCD
> +# memdevs stay non-sharable and host the rest of the suite.
> +MOCK_DC_SHARABLE_SERIAL=56540
> +
> +readarray -t memdevs < <("$CXL" list -b cxl_test -Mi | jq -r '.[].memdev')
> +
> +sharable_mem=""
> +sharable_decoder=""
> +sharable_bus=""
> +sharable_device=""
> +sharable_dra_size=""
> +
> +for cand in ${memdevs[@]}; do
> + cand_dra=$($CXL list -m $cand | jq -r '.[].dynamic_ram_a_size')
> + if [ "$cand_dra" == "null" ]; then
> + continue
> + fi
> + cand_decoder=$($CXL list -b cxl_test -D -d root -m "$cand" |
> + jq -r ".[] |
> + select(.volatile_capable == true) |
> + select(.nr_targets == 1) |
> + select(.max_available_extent >= ${cand_dra}) |
> + .decoder")
> + if [[ -z "$cand_decoder" ]]; then
> + continue
> + fi
> + cand_serial=$($CXL list -m $cand | jq -r '.[].serial')
> + cand_bus=`"$CXL" list -b cxl_test -m ${cand} | jq -r '.[].bus'`
> + cand_device=$($CXL list -m $cand | jq -r '.[].host')
> +
> + if [ "$cand_serial" == "$MOCK_DC_SHARABLE_SERIAL" ]; then
> + if [ -z "$sharable_mem" ]; then
> + sharable_mem="$cand"
> + sharable_decoder="$cand_decoder"
> + sharable_bus="$cand_bus"
> + sharable_device="$cand_device"
> + sharable_dra_size="$cand_dra"
> + fi
> + else
> + if [ -z "$mem" ]; then
> + mem="$cand"
> + decoder="$cand_decoder"
> + bus="$cand_bus"
> + device="$cand_device"
> + dra_size="$cand_dra"
> + fi
> + fi
> +
> + if [ -n "$mem" ] && [ -n "$sharable_mem" ]; then
> + break
> + fi
> +done
> +
> +echo "TEST: non-sharable bus:${bus} decoder:${decoder} mem:${mem} device:${device} size:${dra_size}"
> +echo "TEST: sharable bus:${sharable_bus} decoder:${sharable_decoder} mem:${sharable_mem} device:${sharable_device} size:${sharable_dra_size}"
> +
> +if [ "$decoder" == "" ] || [ "$device" == "" ] || [ "$dra_size" == "" ]; then
> + echo "No non-sharable mem device/decoder found with DCD support"
> + exit 77
> +fi
> +
> +if [ "$sharable_mem" == "" ]; then
> + echo "No sharable mem device found (mock did not stamp MOCK_DC_SHARABLE_SERIAL)"
> + exit 77
> +fi
> +
> +# testing pre existing extents must be called first as the extents were created
> +# by cxl-test being loaded
> +test_pre_existing_extents
> +test_remove_extent_under_dax_device
> +test_create_dax_dev_spanning_two_extents
> +test_inject_tag_support
> +test_uuid_no_match
> +test_uuid_no_match_seed_intact
> +test_uuid_aggregation
> +test_uuid_show
> +# These two run on the sharable memdev (serial $MOCK_DC_SHARABLE_SERIAL).
> +test_shared_extent_inject
> +test_seq_integrity_gap
> +test_cross_more_uniqueness
> +test_alignment_rejection
> +test_partial_extent_remove
> +test_multiple_extent_remove
> +test_destroy_region_without_extent_removal
> +test_destroy_with_extent_and_dax
> +test_dax_device_ops
> +test_reject_overlapping
> +test_two_regions
> +test_more_bit
> +test_driver_reload
> +test_event_reporting
> +
> +modprobe -r cxl_test
> +
> +check_dmesg "$LINENO"
> +
> +exit 0
> diff --git a/test/meson.build b/test/meson.build
> index e0e2193..cd06bf8 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -171,6 +171,7 @@ cxl_translate = find_program('cxl-translate.sh')
> cxl_elc = find_program('cxl-elc.sh')
> cxl_dax_hmem = find_program('cxl-dax-hmem.sh')
> cxl_region_replay = find_program('cxl-region-replay.sh')
> +cxl_dcd = find_program('cxl-dcd.sh')
>
> tests = [
> [ 'libndctl', libndctl, 'ndctl' ],
> @@ -207,6 +208,7 @@ tests = [
> [ 'cxl-elc.sh', cxl_elc, 'cxl' ],
> [ 'cxl-dax-hmem.sh', cxl_dax_hmem, 'cxl' ],
> [ 'cxl-region-replay.sh', cxl_region_replay, 'cxl' ],
> + [ 'cxl-dcd.sh', cxl_dcd, 'cxl' ],
> ]
>
> if get_option('destructive').enabled()