The previous commit fully-fleshed out validation for individual extents.
This commit completes tag-group validation.
Add two group-level gates to cxl_add_pending() that
cxl_validate_extent()'s per-extent view can't see:
- Sequence integrity (cxl_check_group_seq): well-formed iff. every
member is shared_extn_seq == 0 (non-shareable) or the sorted group
is exactly 1..n contiguous (shareable).
- Partition equality (cxl_check_group_partition): tagged allocations
cannot span DC partitions; a partition's CDAT DSMAS entry is the
unit at which shareable / writable / coherency attributes are
described. Skipped for the null UUID.
Each check drops the whole group on violation. Cross-chain uniqueness
of a tag lands in a subsequent commit alongside the host-wide tag
registry.
Based on patches by John Groves.
Signed-off-by: Ira Weiny <[email protected]>
Signed-off-by: John Groves <[email protected]>
Signed-off-by: Anisa Su <[email protected]>
---
Changes:
[anisa: split out as a separate validation step. Cross-chain
uniqueness moved to the dedicated "Enforce cross-region tag
uniqueness" commit so this one only adds — no add-then-replace.]
---
drivers/cxl/core/mbox.c | 117 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+)
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 421bd716a273..545c48c9c373 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1342,6 +1342,109 @@ static void extract_tag_group(struct list_head *pending,
}
}
+/*
+ * Validate shared_extn_seq across a tag group already sorted ascending.
+ *
+ * A tag group is well-formed iff either every member has
+ * shared_extn_seq == 0 (non-sharable allocation) or the sorted group is
+ * exactly 1, 2, ..., n (sharable). Anything else — mix, gap, duplicate,
+ * non-zero starting other than 1 — is a device firmware bug.
+ */
+static int cxl_check_group_seq(struct device *dev,
+ const uuid_t *tag,
+ const struct list_head *group)
+{
+ struct cxl_extent_list_node *pos;
+ u16 first, expected;
+
+ if (list_empty(group))
+ return 0;
+
+ pos = list_first_entry(group, struct cxl_extent_list_node, list);
+ first = le16_to_cpu(pos->extent->shared_extn_seq);
+
+ if (first == 0) {
+ list_for_each_entry(pos, group, list) {
+ if (le16_to_cpu(pos->extent->shared_extn_seq) != 0) {
+ dev_warn(dev,
+ "Tag %pUb: shared_extn_seq mixed
0/non-zero in one allocation (firmware bug)\n",
+ tag);
+ return -EINVAL;
+ }
+ }
+ return 0;
+ }
+
+ if (first != 1) {
+ dev_warn(dev,
+ "Tag %pUb: shared_extn_seq starts at %u, expected 1
(firmware bug)\n",
+ tag, first);
+ return -EINVAL;
+ }
+
+ expected = 1;
+ list_for_each_entry(pos, group, list) {
+ u16 s = le16_to_cpu(pos->extent->shared_extn_seq);
+
+ if (s != expected) {
+ dev_warn(dev,
+ "Tag %pUb: shared_extn_seq gap/dup: expected
%u got %u (firmware bug)\n",
+ tag, expected, s);
+ return -EINVAL;
+ }
+ expected++;
+ }
+ return 0;
+}
+
+/*
+ * For tagged groups, reject allocations that span DC partitions. A tag
+ * is an allocation identity; the partition's CDAT DSMAS entry is what
+ * tells the host which attributes (sharable, writable, coherency)
+ * apply. Untagged groups are skipped — the spec does not define a
+ * cross-chain identity for them.
+ */
+static int cxl_check_group_partition(struct cxl_memdev_state *mds,
+ const uuid_t *tag,
+ const struct list_head *group)
+{
+ struct device *dev = mds->cxlds.dev;
+ const struct cxl_dpa_partition *first_part = NULL;
+ u64 first_dpa = 0;
+ struct cxl_extent_list_node *pos;
+
+ if (uuid_is_null(tag) || list_empty(group))
+ return 0;
+
+ list_for_each_entry(pos, group, list) {
+ struct cxl_extent *extent = pos->extent;
+ struct range ext_range = (struct range) {
+ .start = le64_to_cpu(extent->start_dpa),
+ .end = le64_to_cpu(extent->start_dpa) +
+ le64_to_cpu(extent->length) - 1,
+ };
+ const struct cxl_dpa_partition *part;
+
+ part = cxl_extent_dc_partition(mds, extent, &ext_range);
+ if (!part)
+ return -ENXIO;
+
+ if (!first_part) {
+ first_part = part;
+ first_dpa = ext_range.start;
+ continue;
+ }
+
+ if (part != first_part) {
+ dev_warn(dev,
+ "Tag %pUb: extents span DC partitions
(DPA:%#llx and DPA:%#llx), firmware bug\n",
+ tag, first_dpa, ext_range.start);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
/*
* Drive the pending Add-Capacity records through cxl_add_extent(),
* grouped by tag. Per group: extract from pending, stable-sort by
@@ -1371,6 +1474,20 @@ static int cxl_add_pending(struct cxl_memdev_state *mds)
extract_tag_group(pending, &tag, &group);
list_sort(NULL, &group, extent_seq_compare);
+ /* Sequence-number integrity */
+ if (cxl_check_group_seq(dev, &tag, &group)) {
+ list_for_each_entry_safe(pos, tmp, &group, list)
+ delete_extent_node(pos);
+ continue;
+ }
+
+ /* Partition equality (skipped for null UUID) */
+ if (cxl_check_group_partition(mds, &tag, &group)) {
+ list_for_each_entry_safe(pos, tmp, &group, list)
+ delete_extent_node(pos);
+ continue;
+ }
+
/* Alignment gate — abort the group if any member fails */
bool aligned = true;
list_for_each_entry(pos, &group, list) {
--
2.43.0