Target filtering silently drops unknown names and collapses overlapping
matches. cxl create-region can therefore build a smaller region than the
target list requested, or report only a ways mismatch.

Validate the target list against the resolved memdevs. Reject unmatched
targets and memdevs claimed by more than one target filter, while allowing
one filter to select multiple memdevs.

Fixes: 3d6cd829ec08 ("cxl/region: Use cxl_filter_walk() to gather create-region 
targets")
Assisted-by: LLM
Signed-off-by: Alison Schofield <[email protected]>
---
 cxl/region.c              | 65 +++++++++++++++++++++++++++++++++++++++
 test/cxl-create-region.sh | 45 +++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/cxl/region.c b/cxl/region.c
index 43c228f2f999..2bb3b90940b0 100644
--- a/cxl/region.c
+++ b/cxl/region.c
@@ -222,6 +222,66 @@ static int memdev_sort(const void *a, const void *b)
        return a_pos - b_pos;
 }
 
+/*
+ * Reject unmatched target names and overlapping target filters.
+ * A single filter may match multiple memdevs.
+ */
+static bool validate_target_list(struct json_object *jmemdevs, const char *csv)
+{
+       int i, nr_memdevs = json_object_array_length(jmemdevs);
+       const char *arg, *sep = which_sep(csv);
+       struct json_object *jobj;
+       struct cxl_memdev *memdev;
+       bool valid = true;
+       char *list, *save;
+       int *claims;
+
+       claims = calloc(nr_memdevs, sizeof(*claims));
+       if (!claims)
+               return false;
+
+       list = strdup(csv);
+       if (!list) {
+               free(claims);
+               return false;
+       }
+
+       for (arg = strtok_r(list, sep, &save); arg;
+            arg = strtok_r(NULL, sep, &save)) {
+               int matched = 0;
+
+               for (i = 0; i < nr_memdevs; i++) {
+                       jobj = json_object_array_get_idx(jmemdevs, i);
+                       memdev = json_object_get_userdata(jobj);
+                       if (!util_cxl_memdev_filter(memdev, arg, NULL))
+                               continue;
+                       claims[i]++;
+                       matched++;
+               }
+
+               if (!matched) {
+                       log_err(&rl, "no active memdev matches target '%s'\n",
+                               arg);
+                       valid = false;
+               }
+       }
+
+       for (i = 0; i < nr_memdevs; i++) {
+               if (claims[i] < 2)
+                       continue;
+               jobj = json_object_array_get_idx(jmemdevs, i);
+               memdev = json_object_get_userdata(jobj);
+               log_err(&rl, "%s is named %d times in the target list\n",
+                       cxl_memdev_get_devname(memdev), claims[i]);
+               valid = false;
+       }
+
+       free(list);
+       free(claims);
+
+       return valid;
+}
+
 static struct json_object *collect_memdevs(struct cxl_ctx *ctx,
                                           const char *decoder, int *count,
                                           const char **mems)
@@ -251,6 +311,11 @@ static struct json_object *collect_memdevs(struct cxl_ctx 
*ctx,
        }
 
        if (csv) {
+               if (!validate_target_list(jmemdevs, csv)) {
+                       json_object_put(jmemdevs);
+                       jmemdevs = NULL;
+                       goto out;
+               }
                sort_context.csv = csv,
                json_object_array_sort(jmemdevs, memdev_sort);
        }
diff --git a/test/cxl-create-region.sh b/test/cxl-create-region.sh
index 6fc845c7d382..6ffe36b852a2 100644
--- a/test/cxl-create-region.sh
+++ b/test/cxl-create-region.sh
@@ -296,6 +296,50 @@ create_csv_targets()
        fi
 }
 
+list_regions()
+{
+       $CXL list -b cxl_test -R | jq -r ".[]? | .region" | sort | tr "\n" " "
+}
+
+# Verify failure occurs without creating a region and reports @expect.
+create_expect_fail()
+{
+       expect="$1"
+       shift
+
+       regions_before="$(list_regions)"
+
+       if out=$($CXL create-region -b cxl_test "$@" 2>&1); then
+               echo "create-region $*: expected failure, created a region"
+               err "$LINENO"
+       fi
+
+       if ! grep -Fq -- "$expect" <<< "$out"; then
+               echo "create-region $*: expected '$expect', got:"
+               echo "$out"
+               err "$LINENO"
+       fi
+
+       if [[ "$(list_regions)" != "$regions_before" ]]; then
+               echo "create-region $*: refused, but left a region behind"
+               err "$LINENO"
+       fi
+}
+
+refuse_bad_target_list()
+{
+       if ! setup_interleave_shape; then
+               echo "skipping bad target list refusals"
+               return
+       fi
+
+       create_expect_fail "$pos0 is named 2 times in the target list" \
+               -d "$x2_decoder" -m "$pos0,$pos0"
+
+       create_expect_fail "no active memdev matches target 'nomemdev'" \
+               -d "$x2_decoder" -m "$pos0,nomemdev"
+}
+
 # test region creation on devices behind a single-port host-bridge
 create_single
 
@@ -310,6 +354,7 @@ for mem in ${mems[@]}; do
 done
 
 create_csv_targets
+refuse_bad_target_list
 
 # Repeat x1/x2/x3/x4 region creation with XOR interleave arithmetic.
 load_cxl_test interleave_arithmetic=1
-- 
2.37.3


Reply via email to