From nobody Sat Feb 7 08:13:50 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1677519374; cv=none; d=zohomail.com; s=zohoarc; b=n/WrGYuRhIMAWxHbBzXGFJcB/tq2IVIzn/GJBKn3+bVy2NNXB8mvk0amlON1zXFc86w8eel7RFRVLPSnX0cHHX5np3IQfyvySYixmELpnt2Ruh/t3Cu/2A0p9a06+RAOev67OxZu8MPj4h9zwC84Dp3k4HCwqGni/2HoNIYg++s= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1677519374; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=UF3FNADu8h3z2XV4IzGQxrUv5Wytehf5KYMXjyTm6bs=; b=UbxLEMte70wqI1HGqUj27+XT0j7ZdpvrJwzmYFZf9qSDxvs8eOo/SMfw8TeBD1HQTdE+hno7b26s0CqT0eXaLMQPB2ZUx+CrZP/lercTLDE8tEIjNnK486L/Pzsnh8NY7uyUvyFf6v1wb3PLBWVEoSSb2ed3vnRcQ5HAOvjujzo= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1677519374242315.36602254653906; Mon, 27 Feb 2023 09:36:14 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pWhPl-0008PL-JK; Mon, 27 Feb 2023 12:35:53 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pWhPj-0008Oe-Qc for qemu-devel@nongnu.org; Mon, 27 Feb 2023 12:35:51 -0500 Received: from frasgout.his.huawei.com ([185.176.79.56]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pWhPh-0000YO-Hb for qemu-devel@nongnu.org; Mon, 27 Feb 2023 12:35:51 -0500 Received: from lhrpeml500005.china.huawei.com (unknown [172.18.147.200]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4PQSFn6CySz6J6KM; Tue, 28 Feb 2023 01:30:53 +0800 (CST) Received: from SecurePC-101-06.china.huawei.com (10.122.247.231) by lhrpeml500005.china.huawei.com (7.191.163.240) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Mon, 27 Feb 2023 17:35:47 +0000 To: , Michael Tsirkin , Fan Ni CC: , , Ira Weiny , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Dave Jiang Subject: [PATCH v3 3/7] hw/cxl/events: Wire up get/clear event mailbox commands Date: Mon, 27 Feb 2023 17:34:12 +0000 Message-ID: <20230227173416.7740-4-Jonathan.Cameron@huawei.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230227173416.7740-1-Jonathan.Cameron@huawei.com> References: <20230227173416.7740-1-Jonathan.Cameron@huawei.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.122.247.231] X-ClientProxiedBy: lhrpeml500001.china.huawei.com (7.191.163.213) To lhrpeml500005.china.huawei.com (7.191.163.240) X-CFilter-Loop: Reflected Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.176.79.56; envelope-from=jonathan.cameron@huawei.com; helo=frasgout.his.huawei.com X-Spam_score_int: -41 X-Spam_score: -4.2 X-Spam_bar: ---- X-Spam_report: (-4.2 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Jonathan Cameron From: Jonathan Cameron via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1677519375839100003 Content-Type: text/plain; charset="utf-8" From: Ira Weiny CXL testing is benefited from an artificial event log injection mechanism. Add an event log infrastructure to insert, get, and clear events from the various logs available on a device. Replace the stubbed out CXL Get/Clear Event mailbox commands with commands that operate on the new infrastructure. Signed-off-by: Ira Weiny Signed-off-by: Jonathan Cameron --- hw/cxl/cxl-events.c | 217 ++++++++++++++++++++++++++++++++++++ hw/cxl/cxl-mailbox-utils.c | 40 ++++++- hw/cxl/meson.build | 1 + hw/mem/cxl_type3.c | 1 + include/hw/cxl/cxl_device.h | 25 +++++ include/hw/cxl/cxl_events.h | 55 +++++++++ 6 files changed, 337 insertions(+), 2 deletions(-) diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c new file mode 100644 index 0000000000..5da1b76b97 --- /dev/null +++ b/hw/cxl/cxl-events.c @@ -0,0 +1,217 @@ +/* + * CXL Event processing + * + * Copyright(C) 2023 Intel Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2. See the + * COPYING file in the top-level directory. + */ + +#include + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qemu/typedefs.h" +#include "qemu/error-report.h" +#include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_events.h" + +/* Artificial limit on the number of events a log can hold */ +#define CXL_TEST_EVENT_OVERFLOW 8 + +static void reset_overflow(CXLEventLog *log) +{ + log->overflow_err_count =3D 0; + log->first_overflow_timestamp =3D 0; + log->last_overflow_timestamp =3D 0; +} + +void cxl_event_init(CXLDeviceState *cxlds) +{ + CXLEventLog *log; + int i; + + for (i =3D 0; i < CXL_EVENT_TYPE_MAX; i++) { + log =3D &cxlds->event_logs[i]; + log->next_handle =3D 1; + log->overflow_err_count =3D 0; + log->first_overflow_timestamp =3D 0; + log->last_overflow_timestamp =3D 0; + qemu_mutex_init(&log->lock); + QSIMPLEQ_INIT(&log->events); + } +} + +static CXLEvent *cxl_event_get_head(CXLEventLog *log) +{ + return QSIMPLEQ_FIRST(&log->events); +} + +static CXLEvent *cxl_event_get_next(CXLEvent *entry) +{ + return QSIMPLEQ_NEXT(entry, node); +} + +static int cxl_event_count(CXLEventLog *log) +{ + CXLEvent *event; + int rc =3D 0; + + QSIMPLEQ_FOREACH(event, &log->events, node) { + rc++; + } + + return rc; +} + +static bool cxl_event_empty(CXLEventLog *log) +{ + return QSIMPLEQ_EMPTY(&log->events); +} + +static void cxl_event_delete_head(CXLDeviceState *cxlds, + CXLEventLogType log_type, + CXLEventLog *log) +{ + CXLEvent *entry =3D cxl_event_get_head(log); + + reset_overflow(log); + QSIMPLEQ_REMOVE_HEAD(&log->events, node); + if (cxl_event_empty(log)) { + cxl_event_set_status(cxlds, log_type, false); + } + g_free(entry); +} + +/* + * return true if an interrupt should be generated as a result + * of inserting this event. + */ +bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, + CXLEventRecordRaw *event) +{ + uint64_t time; + CXLEventLog *log; + CXLEvent *entry; + + if (log_type >=3D CXL_EVENT_TYPE_MAX) { + return false; + } + + time =3D cxl_device_get_timestamp(cxlds); + + log =3D &cxlds->event_logs[log_type]; + + QEMU_LOCK_GUARD(&log->lock); + + if (cxl_event_count(log) >=3D CXL_TEST_EVENT_OVERFLOW) { + if (log->overflow_err_count =3D=3D 0) { + log->first_overflow_timestamp =3D time; + } + log->overflow_err_count++; + log->last_overflow_timestamp =3D time; + return false; + } + + entry =3D g_new0(CXLEvent, 1); + + memcpy(&entry->data, event, sizeof(*event)); + + entry->data.hdr.handle =3D cpu_to_le16(log->next_handle); + log->next_handle++; + /* 0 handle is never valid */ + if (log->next_handle =3D=3D 0) { + log->next_handle++; + } + entry->data.hdr.timestamp =3D cpu_to_le64(time); + + QSIMPLEQ_INSERT_TAIL(&log->events, entry, node); + cxl_event_set_status(cxlds, log_type, true); + + /* Count went from 0 to 1 */ + return cxl_event_count(log) =3D=3D 1; +} + +CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload= *pl, + uint8_t log_type, int max_recs, + uint16_t *len) +{ + CXLEventLog *log; + CXLEvent *entry; + uint16_t nr; + + if (log_type >=3D CXL_EVENT_TYPE_MAX) { + return CXL_MBOX_INVALID_INPUT; + } + + log =3D &cxlds->event_logs[log_type]; + + QEMU_LOCK_GUARD(&log->lock); + + entry =3D cxl_event_get_head(log); + for (nr =3D 0; entry && nr < max_recs; nr++) { + memcpy(&pl->records[nr], &entry->data, CXL_EVENT_RECORD_SIZE); + entry =3D cxl_event_get_next(entry); + } + + if (!cxl_event_empty(log)) { + pl->flags |=3D CXL_GET_EVENT_FLAG_MORE_RECORDS; + } + + if (log->overflow_err_count) { + pl->flags |=3D CXL_GET_EVENT_FLAG_OVERFLOW; + pl->overflow_err_count =3D cpu_to_le16(log->overflow_err_count); + pl->first_overflow_timestamp =3D cpu_to_le64(log->first_overflow_t= imestamp); + pl->last_overflow_timestamp =3D cpu_to_le64(log->last_overflow_tim= estamp); + } + + pl->record_count =3D cpu_to_le16(nr); + *len =3D CXL_EVENT_PAYLOAD_HDR_SIZE + (CXL_EVENT_RECORD_SIZE * nr); + + return CXL_MBOX_SUCCESS; +} + +CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, CXLClearEventPay= load *pl) +{ + CXLEventLog *log; + uint8_t log_type; + CXLEvent *entry; + int nr; + + log_type =3D pl->event_log; + + if (log_type >=3D CXL_EVENT_TYPE_MAX) { + return CXL_MBOX_INVALID_INPUT; + } + + log =3D &cxlds->event_logs[log_type]; + + QEMU_LOCK_GUARD(&log->lock); + /* + * Must itterate the queue twice. + * "The device shall verify the event record handles specified in the = input + * payload are in temporal order. If the device detects an older event + * record that will not be cleared when Clear Event Records is execute= d, + * the device shall return the Invalid Handle return code and shall not + * clear any of the specified event records." + * -- CXL 3.0 8.2.9.2.3 + */ + entry =3D cxl_event_get_head(log); + for (nr =3D 0; entry && nr < pl->nr_recs; nr++) { + uint16_t handle =3D pl->handle[nr]; + + /* NOTE: Both handles are little endian. */ + if (handle =3D=3D 0 || entry->data.hdr.handle !=3D handle) { + return CXL_MBOX_INVALID_INPUT; + } + entry =3D cxl_event_get_next(entry); + } + + entry =3D cxl_event_get_head(log); + for (nr =3D 0; entry && nr < pl->nr_recs; nr++) { + cxl_event_delete_head(cxlds, log_type, log); + entry =3D cxl_event_get_head(log); + } + + return CXL_MBOX_SUCCESS; +} diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index c6e02d1480..5437a70ce8 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -9,6 +9,7 @@ =20 #include "qemu/osdep.h" #include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_events.h" #include "hw/pci/pci.h" #include "qemu/cutils.h" #include "qemu/log.h" @@ -95,11 +96,46 @@ struct cxl_cmd { return CXL_MBOX_SUCCESS; \ } =20 -DEFINE_MAILBOX_HANDLER_ZEROED(events_get_records, 0x20); -DEFINE_MAILBOX_HANDLER_NOP(events_clear_records); DEFINE_MAILBOX_HANDLER_ZEROED(events_get_interrupt_policy, 4); DEFINE_MAILBOX_HANDLER_NOP(events_set_interrupt_policy); =20 +static CXLRetCode cmd_events_get_records(struct cxl_cmd *cmd, + CXLDeviceState *cxlds, + uint16_t *len) +{ + CXLGetEventPayload *pl; + uint8_t log_type; + int max_recs; + + if (cmd->in < sizeof(log_type)) { + return CXL_MBOX_INVALID_INPUT; + } + + log_type =3D *((uint8_t *)cmd->payload); + + pl =3D (CXLGetEventPayload *)cmd->payload; + memset(pl, 0, sizeof(*pl)); + + max_recs =3D (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / + CXL_EVENT_RECORD_SIZE; + if (max_recs > 0xFFFF) { + max_recs =3D 0xFFFF; + } + + return cxl_event_get_records(cxlds, pl, log_type, max_recs, len); +} + +static CXLRetCode cmd_events_clear_records(struct cxl_cmd *cmd, + CXLDeviceState *cxlds, + uint16_t *len) +{ + CXLClearEventPayload *pl; + + pl =3D (CXLClearEventPayload *)cmd->payload; + *len =3D 0; + return cxl_event_clear_records(cxlds, pl); +} + /* 8.2.9.2.1 */ static CXLRetCode cmd_firmware_update_get_info(struct cxl_cmd *cmd, CXLDeviceState *cxl_dstate, diff --git a/hw/cxl/meson.build b/hw/cxl/meson.build index cfa95ffd40..71059972d4 100644 --- a/hw/cxl/meson.build +++ b/hw/cxl/meson.build @@ -5,6 +5,7 @@ softmmu_ss.add(when: 'CONFIG_CXL', 'cxl-mailbox-utils.c', 'cxl-host.c', 'cxl-cdat.c', + 'cxl-events.c', ), if_false: files( 'cxl-host-stubs.c', diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 44ffc7d9b0..a8fc2e5774 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -697,6 +697,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **err= p) goto err_release_cdat; } =20 + cxl_event_init(&ct3d->cxl_dstate); return; =20 err_release_cdat: diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 9f8ee85f8a..d3aec1bc0e 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -111,6 +111,20 @@ typedef enum { CXL_MBOX_MAX =3D 0x17 } CXLRetCode; =20 +typedef struct CXLEvent { + CXLEventRecordRaw data; + QSIMPLEQ_ENTRY(CXLEvent) node; +} CXLEvent; + +typedef struct CXLEventLog { + uint16_t next_handle; + uint16_t overflow_err_count; + uint64_t first_overflow_timestamp; + uint64_t last_overflow_timestamp; + QemuMutex lock; + QSIMPLEQ_HEAD(, CXLEvent) events; +} CXLEventLog; + typedef struct cxl_device_state { MemoryRegion device_registers; =20 @@ -161,6 +175,8 @@ typedef struct cxl_device_state { uint64_t mem_size; uint64_t pmem_size; uint64_t vmem_size; + + CXLEventLog event_logs[CXL_EVENT_TYPE_MAX]; } CXLDeviceState; =20 /* Initialize the register block for a device */ @@ -353,6 +369,15 @@ MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_= addr, uint64_t data, =20 uint64_t cxl_device_get_timestamp(CXLDeviceState *cxlds); =20 +void cxl_event_init(CXLDeviceState *cxlds); +bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, + CXLEventRecordRaw *event); +CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload= *pl, + uint8_t log_type, int max_recs, + uint16_t *len); +CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, + CXLClearEventPayload *pl); + void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d); =20 #endif diff --git a/include/hw/cxl/cxl_events.h b/include/hw/cxl/cxl_events.h index aeb3b0590e..d4aaa894f1 100644 --- a/include/hw/cxl/cxl_events.h +++ b/include/hw/cxl/cxl_events.h @@ -10,6 +10,8 @@ #ifndef CXL_EVENTS_H #define CXL_EVENTS_H =20 +#include "qemu/uuid.h" + /* * CXL rev 3.0 section 8.2.9.2.2; Table 8-49 * @@ -25,4 +27,57 @@ typedef enum CXLEventLogType { CXL_EVENT_TYPE_MAX } CXLEventLogType; =20 +/* + * Common Event Record Format + * CXL rev 3.0 section 8.2.9.2.1; Table 8-42 + */ +#define CXL_EVENT_REC_HDR_RES_LEN 0xf +typedef struct CXLEventRecordHdr { + QemuUUID id; + uint8_t length; + uint8_t flags[3]; + uint16_t handle; + uint16_t related_handle; + uint64_t timestamp; + uint8_t maint_op_class; + uint8_t reserved[CXL_EVENT_REC_HDR_RES_LEN]; +} QEMU_PACKED CXLEventRecordHdr; + +#define CXL_EVENT_RECORD_DATA_LENGTH 0x50 +typedef struct CXLEventRecordRaw { + CXLEventRecordHdr hdr; + uint8_t data[CXL_EVENT_RECORD_DATA_LENGTH]; +} QEMU_PACKED CXLEventRecordRaw; +#define CXL_EVENT_RECORD_SIZE (sizeof(CXLEventRecordRaw)) + +/* + * Get Event Records output payload + * CXL rev 3.0 section 8.2.9.2.2; Table 8-50 + */ +#define CXL_GET_EVENT_FLAG_OVERFLOW BIT(0) +#define CXL_GET_EVENT_FLAG_MORE_RECORDS BIT(1) +typedef struct CXLGetEventPayload { + uint8_t flags; + uint8_t reserved1; + uint16_t overflow_err_count; + uint64_t first_overflow_timestamp; + uint64_t last_overflow_timestamp; + uint16_t record_count; + uint8_t reserved2[0xa]; + CXLEventRecordRaw records[]; +} QEMU_PACKED CXLGetEventPayload; +#define CXL_EVENT_PAYLOAD_HDR_SIZE (sizeof(CXLGetEventPayload)) + +/* + * Clear Event Records input payload + * CXL rev 3.0 section 8.2.9.2.3; Table 8-51 + */ +typedef struct CXLClearEventPayload { + uint8_t event_log; /* CXLEventLogType */ + uint8_t clear_flags; + uint8_t nr_recs; + uint8_t reserved[3]; + uint16_t handle[]; +} CXLClearEventPayload; + #endif /* CXL_EVENTS_H */ --=20 2.37.2