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

cxl/test: Add generic mock events

Facilitate testing basic Get/Clear Event functionality by creating
multiple logs and generic events with made up UUID's.

Data is completely made up with data patterns which should be easy to
spot in trace output.

A single sysfs entry resets the event data and triggers collecting the
events for testing.

Test traces are easy to obtain with a small script such as this:

#!/bin/bash -x

devices=`find /sys/devices/platform -name cxl_mem*`

# Turn on tracing
echo "" > /sys/kernel/tracing/trace
echo 1 > /sys/kernel/tracing/events/cxl/enable
echo 1 > /sys/kernel/tracing/tracing_on

# Generate fake interrupt
for device in $devices; do
echo 1 > $device/event_trigger
done

# Turn off tracing and report events
echo 0 > /sys/kernel/tracing/tracing_on
cat /sys/kernel/tracing/trace

Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
Link: https://lore.kernel.org/r/20221216-cxl-ev-log-v7-6-2316a5c8f7d8@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

authored by

Ira Weiny and committed by
Dan Williams
d1dca858 95b49479

+233 -2
+1 -1
tools/testing/cxl/test/Kbuild
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 - ccflags-y := -I$(srctree)/drivers/cxl/ 2 + ccflags-y := -I$(srctree)/drivers/cxl/ -I$(srctree)/drivers/cxl/core 3 3 4 4 obj-m += cxl_test.o 5 5 obj-m += cxl_mock.o
+232 -1
tools/testing/cxl/test/mem.c
··· 9 9 #include <linux/bits.h> 10 10 #include <cxlmem.h> 11 11 12 + #include "trace.h" 13 + 12 14 #define LSA_SIZE SZ_128K 13 15 #define DEV_SIZE SZ_2G 14 16 #define EFFECT(x) (1U << x) ··· 69 67 70 68 #define PASS_TRY_LIMIT 3 71 69 70 + #define CXL_TEST_EVENT_CNT_MAX 15 71 + 72 + /* Set a number of events to return at a time for simulation. */ 73 + #define CXL_TEST_EVENT_CNT 3 74 + 75 + struct mock_event_log { 76 + u16 clear_idx; 77 + u16 cur_idx; 78 + u16 nr_events; 79 + struct cxl_event_record_raw *events[CXL_TEST_EVENT_CNT_MAX]; 80 + }; 81 + 82 + struct mock_event_store { 83 + struct cxl_dev_state *cxlds; 84 + struct mock_event_log mock_logs[CXL_EVENT_TYPE_MAX]; 85 + u32 ev_status; 86 + }; 87 + 72 88 struct cxl_mockmem_data { 73 89 void *lsa; 74 90 u32 security_state; ··· 94 74 u8 master_pass[NVDIMM_PASSPHRASE_LEN]; 95 75 int user_limit; 96 76 int master_limit; 97 - 77 + struct mock_event_store mes; 78 + u8 event_buf[SZ_4K]; 98 79 }; 80 + 81 + static struct mock_event_log *event_find_log(struct device *dev, int log_type) 82 + { 83 + struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 84 + 85 + if (log_type >= CXL_EVENT_TYPE_MAX) 86 + return NULL; 87 + return &mdata->mes.mock_logs[log_type]; 88 + } 89 + 90 + static struct cxl_event_record_raw *event_get_current(struct mock_event_log *log) 91 + { 92 + return log->events[log->cur_idx]; 93 + } 94 + 95 + static void event_reset_log(struct mock_event_log *log) 96 + { 97 + log->cur_idx = 0; 98 + log->clear_idx = 0; 99 + } 100 + 101 + /* Handle can never be 0 use 1 based indexing for handle */ 102 + static u16 event_get_clear_handle(struct mock_event_log *log) 103 + { 104 + return log->clear_idx + 1; 105 + } 106 + 107 + /* Handle can never be 0 use 1 based indexing for handle */ 108 + static __le16 event_get_cur_event_handle(struct mock_event_log *log) 109 + { 110 + u16 cur_handle = log->cur_idx + 1; 111 + 112 + return cpu_to_le16(cur_handle); 113 + } 114 + 115 + static bool event_log_empty(struct mock_event_log *log) 116 + { 117 + return log->cur_idx == log->nr_events; 118 + } 119 + 120 + static void mes_add_event(struct mock_event_store *mes, 121 + enum cxl_event_log_type log_type, 122 + struct cxl_event_record_raw *event) 123 + { 124 + struct mock_event_log *log; 125 + 126 + if (WARN_ON(log_type >= CXL_EVENT_TYPE_MAX)) 127 + return; 128 + 129 + log = &mes->mock_logs[log_type]; 130 + if (WARN_ON(log->nr_events >= CXL_TEST_EVENT_CNT_MAX)) 131 + return; 132 + 133 + log->events[log->nr_events] = event; 134 + log->nr_events++; 135 + } 136 + 137 + static int mock_get_event(struct cxl_dev_state *cxlds, 138 + struct cxl_mbox_cmd *cmd) 139 + { 140 + struct cxl_get_event_payload *pl; 141 + struct mock_event_log *log; 142 + u8 log_type; 143 + int i; 144 + 145 + if (cmd->size_in != sizeof(log_type)) 146 + return -EINVAL; 147 + 148 + if (cmd->size_out < struct_size(pl, records, CXL_TEST_EVENT_CNT)) 149 + return -EINVAL; 150 + 151 + log_type = *((u8 *)cmd->payload_in); 152 + if (log_type >= CXL_EVENT_TYPE_MAX) 153 + return -EINVAL; 154 + 155 + memset(cmd->payload_out, 0, cmd->size_out); 156 + 157 + log = event_find_log(cxlds->dev, log_type); 158 + if (!log || event_log_empty(log)) 159 + return 0; 160 + 161 + pl = cmd->payload_out; 162 + 163 + for (i = 0; i < CXL_TEST_EVENT_CNT && !event_log_empty(log); i++) { 164 + memcpy(&pl->records[i], event_get_current(log), 165 + sizeof(pl->records[i])); 166 + pl->records[i].hdr.handle = event_get_cur_event_handle(log); 167 + log->cur_idx++; 168 + } 169 + 170 + pl->record_count = cpu_to_le16(i); 171 + if (!event_log_empty(log)) 172 + pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS; 173 + 174 + return 0; 175 + } 176 + 177 + static int mock_clear_event(struct cxl_dev_state *cxlds, 178 + struct cxl_mbox_cmd *cmd) 179 + { 180 + struct cxl_mbox_clear_event_payload *pl = cmd->payload_in; 181 + struct mock_event_log *log; 182 + u8 log_type = pl->event_log; 183 + u16 handle; 184 + int nr; 185 + 186 + if (log_type >= CXL_EVENT_TYPE_MAX) 187 + return -EINVAL; 188 + 189 + log = event_find_log(cxlds->dev, log_type); 190 + if (!log) 191 + return 0; /* No mock data in this log */ 192 + 193 + /* 194 + * This check is technically not invalid per the specification AFAICS. 195 + * (The host could 'guess' handles and clear them in order). 196 + * However, this is not good behavior for the host so test it. 197 + */ 198 + if (log->clear_idx + pl->nr_recs > log->cur_idx) { 199 + dev_err(cxlds->dev, 200 + "Attempting to clear more events than returned!\n"); 201 + return -EINVAL; 202 + } 203 + 204 + /* Check handle order prior to clearing events */ 205 + for (nr = 0, handle = event_get_clear_handle(log); 206 + nr < pl->nr_recs; 207 + nr++, handle++) { 208 + if (handle != le16_to_cpu(pl->handles[nr])) { 209 + dev_err(cxlds->dev, "Clearing events out of order\n"); 210 + return -EINVAL; 211 + } 212 + } 213 + 214 + /* Clear events */ 215 + log->clear_idx += pl->nr_recs; 216 + return 0; 217 + } 218 + 219 + static void cxl_mock_event_trigger(struct device *dev) 220 + { 221 + struct cxl_mockmem_data *mdata = dev_get_drvdata(dev); 222 + struct mock_event_store *mes = &mdata->mes; 223 + int i; 224 + 225 + for (i = CXL_EVENT_TYPE_INFO; i < CXL_EVENT_TYPE_MAX; i++) { 226 + struct mock_event_log *log; 227 + 228 + log = event_find_log(dev, i); 229 + if (log) 230 + event_reset_log(log); 231 + } 232 + 233 + cxl_mem_get_event_records(mes->cxlds, mes->ev_status); 234 + } 235 + 236 + struct cxl_event_record_raw maint_needed = { 237 + .hdr = { 238 + .id = UUID_INIT(0xBA5EBA11, 0xABCD, 0xEFEB, 239 + 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 240 + .length = sizeof(struct cxl_event_record_raw), 241 + .flags[0] = CXL_EVENT_RECORD_FLAG_MAINT_NEEDED, 242 + /* .handle = Set dynamically */ 243 + .related_handle = cpu_to_le16(0xa5b6), 244 + }, 245 + .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 246 + }; 247 + 248 + struct cxl_event_record_raw hardware_replace = { 249 + .hdr = { 250 + .id = UUID_INIT(0xABCDEFEB, 0xBA11, 0xBA5E, 251 + 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0xa5, 0x5a, 0xa5), 252 + .length = sizeof(struct cxl_event_record_raw), 253 + .flags[0] = CXL_EVENT_RECORD_FLAG_HW_REPLACE, 254 + /* .handle = Set dynamically */ 255 + .related_handle = cpu_to_le16(0xb6a5), 256 + }, 257 + .data = { 0xDE, 0xAD, 0xBE, 0xEF }, 258 + }; 259 + 260 + static void cxl_mock_add_event_logs(struct mock_event_store *mes) 261 + { 262 + mes_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed); 263 + mes->ev_status |= CXLDEV_EVENT_STATUS_INFO; 264 + 265 + mes_add_event(mes, CXL_EVENT_TYPE_FATAL, &hardware_replace); 266 + mes->ev_status |= CXLDEV_EVENT_STATUS_FATAL; 267 + } 99 268 100 269 static int mock_gsl(struct cxl_mbox_cmd *cmd) 101 270 { ··· 791 582 case CXL_MBOX_OP_GET_PARTITION_INFO: 792 583 rc = mock_partition_info(cxlds, cmd); 793 584 break; 585 + case CXL_MBOX_OP_GET_EVENT_RECORD: 586 + rc = mock_get_event(cxlds, cmd); 587 + break; 588 + case CXL_MBOX_OP_CLEAR_EVENT_RECORD: 589 + rc = mock_clear_event(cxlds, cmd); 590 + break; 794 591 case CXL_MBOX_OP_SET_LSA: 795 592 rc = mock_set_lsa(cxlds, cmd); 796 593 break; ··· 843 628 return !!id->driver_data; 844 629 } 845 630 631 + static ssize_t event_trigger_store(struct device *dev, 632 + struct device_attribute *attr, 633 + const char *buf, size_t count) 634 + { 635 + cxl_mock_event_trigger(dev); 636 + return count; 637 + } 638 + static DEVICE_ATTR_WO(event_trigger); 639 + 846 640 static int cxl_mock_mem_probe(struct platform_device *pdev) 847 641 { 848 642 struct device *dev = &pdev->dev; ··· 879 655 cxlds->serial = pdev->id; 880 656 cxlds->mbox_send = cxl_mock_mbox_send; 881 657 cxlds->payload_size = SZ_4K; 658 + cxlds->event.buf = (struct cxl_get_event_payload *) mdata->event_buf; 882 659 if (is_rcd(pdev)) { 883 660 cxlds->rcd = true; 884 661 cxlds->component_reg_phys = CXL_RESOURCE_NONE; ··· 897 672 if (rc) 898 673 return rc; 899 674 675 + mdata->mes.cxlds = cxlds; 676 + cxl_mock_add_event_logs(&mdata->mes); 677 + 900 678 cxlmd = devm_cxl_add_memdev(cxlds); 901 679 if (IS_ERR(cxlmd)) 902 680 return PTR_ERR(cxlmd); 681 + 682 + cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL); 903 683 904 684 return 0; 905 685 } ··· 944 714 945 715 static struct attribute *cxl_mock_mem_attrs[] = { 946 716 &dev_attr_security_lock.attr, 717 + &dev_attr_event_trigger.attr, 947 718 NULL 948 719 }; 949 720 ATTRIBUTE_GROUPS(cxl_mock_mem);