Introduce SCMI Telemetry support
Subsystems (6)
Cristian Marussi
reviewerv15 cover Introduce SCMI Telemetry support
Hi all,
[TLDR Summary]
[V15 highlights]
- general cleanup and hardening driven by reviews
- improved TDCF access routines to minimize lost samples under load
- completed ABI docs
[V16 TODO]
- address any remaining V15 outstanding Sashiko/reviewers issues
The upcoming SCMI v4.0 specification [0] introduces a new SCMI protocol dedicated to System Telemetry.
In a nutshell, the SCMI Telemetry protocol allows an agent to discover at runtime the set of Telemetry Data Events (DEs) available on a specific platform and provides the means to configure the set of DEs that a user is interested into, while reading them back using the collection method that is deeemed more suitable for the usecase at hand. (...amongst the various possible collection methods allowed by SCMI specification)
Without delving into the gory details of the whole SCMI Telemetry protocol let's just say that the SCMI platform/server firmware advertises a number of Telemetry Data Events, each one identified by a 32bit unique ID, and an SCMI agent/client, like Linux, can discover them and read back at will the associated data value in a number of ways.
Data collection is mainly intended to happen on demand via shared memory areas exposed by the platform firmware, discovered dynamically via SCMI Telemetry and accessed by Linux on-demand, but some DE can also be reported via SCMI Notifications asynchronous messages or via direct dedicated FastChannels (another kind of SCMI memory based access): all of this underlying mechanism is anyway hidden to the user since it is mediated by the kernel driver which will return the proper data value when queried.
Anyway, the set of well-known architected DE IDs defined by the spec is limited to a dozen IDs, which means that the vast majority of DE IDs are customizable per-platform: as a consequence, though, the same ID, say '0x1234', could represent completely different things on different systems.
Precise definitions and semantic of such custom Data Event IDs are out of the scope of the SCMI Telemetry specification and of this implementation:
they are supposed to be provided using some kind of JSON-like description file that will have to be consumed by a userspace tool which would be finally in charge of making sense of the set of available DEs.
IOW, in turn, this means that even though the DEs enumerated via SCMI come with some sort of topological and qualitative description provided by the protocol (like unit of measurements, name, topology info etc), kernel-wise we CANNOT be completely sure of "what is what" without being fed-back some sort of information about the DEs by the afore mentioned userspace tool.
For these reasons, currently this series does NOT attempt to register any of these DEs with any of the usual in-kernel subsystems (like HWMON, IIO, PERF etc), simply because we cannot be sure which DE is suitable, or even desirable, for a given subsystem. This also means there are NO in-kernel users of these Telemetry data events as of now.
So, while we do not exclude, for the future, to feed/register some of the discovered DEs to/with some of the above mentioned Kernel subsystems, as of now we have ONLY modeled a custom userspace API to make SCMI Telemetry available to userspace tools.
With V5 we adopted a pure chardev/IOCTL ABI approach, refining the IOCTL based interface already present with the previous FS-based ABI.
INTERFACES
For each discovered SCMI Instance a character device named tlm_<N> is created under /dev/scmi/ subtree.
The IOCTL interface described at 'include/uapi/linux/scmi.h' is made available to enumerate and configure Telemetry resources: Telemetry data can be collected using a few different IOCTls, depending on the required granularity.
Alternatively it is possible to obtain a list of the file descriptors referencing directly the underlying SCMI Telemetry SHMTI memory areas and implement in user space an SCMI Telemetry parser accessing directly the SHMTI, while staying in compliance with the SCMI TDCF format.
NOTE THAT from v3 onwards the firmware interface level NOW supports ONLY the latest SCMI v4.0 specification [0].
Based on V7.4-rc2, tested on an emulated setup and AGI HW.
This series is available also at [1].
If you still reading...any feedback welcome :P
Thanks,
Cristian
---
v14 --> v15
- rebased on v7.4-rc2
- [STLM]
+ add lazy enumeration
+ add des/info cmdline options
+ add missing SIGTERM handling
- [SYS/TLM]
+ harden/simplify batch helpers
+ fix shmti mmap size check to include offset
+ simplify session_unsubscribe cleanup on exit/unbind
+ added missing kref.h include
+ expose TLM EVENTS capabilities ONLY IF CONFIG_EVENTFD
+ mmap: improved check to avoid WRITE/EXEC mapping
+ reject non zero vm_pgoff
+ drop usage of guard(srcu) to avoid races with put_device
- [TLM]
+ avoid genration counter reset on TLM RESET
+ add an overflow check on NOTIF payload lines parsing
+ convert to Linux errno before building notification report
+ use better startm/endm initial markers in tdcf_de_payld_get
+ trigger resource enumeration also on de_data_read
+ skip unitialized FCs on reads and return -EPROTO
+ check FC size on enumeration (big enough to hold TS ?)
+ introduce localized single DE snapshot on single DE data read
+ widen tde->mtx locking in msg_payld_process
+ refactor msg_payld_process
+ unlink BLK_TS on DE disable
+ update linked BLK_TS on enable
+ update TDE location ONLY after successfull linked lines processing
+ trigger resource enumeration on state_get
+ trigger resource enumeration on colletion configure only for groups
+ widen tde->mtx locking section on state_set
+ refactor UUID/BLK_TS lines processing in enable
+ introduce BLK_TS localized snapshot during enable
+ check line type at de_offs
+ sanitize requested mode on collection conf
+ use snapshots to collect UUIDs during full scans
+ stop tracking UUIDs lines singularly (useless overhead)
+ use UUIDs line hashing for lookup
+ use snapshots to track and collect BLK_TS during full scan bulk reads
+ account for tstamped TDE during discovery
+ correct SHMTI overflow off-by-one
+ rework TDE cache hit/miss logic
+ introduce snapshots on full scan reads
+ validate DE location also on SCAN_UPDATE
+ fix TDCF prologue/epilogue endianity struct definitions
+ rework SHMTI initialization and allocation
+ add snapshot suppport data structures
+ added alignment checks on SHMTII length size
+ fixed error string formatting
+ warn if less than expected SHMTIs are enumerated
+ improve memory area sanity checks
+ added missing initialization of intrvs->grp_id
+ bailout from group initialization when NO DEs were found
+ better handling of error path on groups init
+ drop usage of devres for FC iomapping
+ trigger full enumeration of DE lookups
+ new IS_TDE_TS_ macro helpers
+ add a dedicated scmi_telemetry_free_tdes_add helper
+ dropping needless __must_check
+ flag as fully enumerated only when ALL declared des were found
+ reworked error path in scmi_allocate_pending_event_handler
+ hardened core iterator parsing of multi-part reply
v13 --> v14
- rebased on v7.3-rc7
- [STLM]
+ fix mmmap/munmap calls
+ better NULL checks on failed enumerations
+ harden command parsing and return errors
- [SYS/TLM]
+ avoid double free on probe err path
+ grab ctx and check if dead when mmapping an anon_fd
+ move away from dead_mtx in favour of dead_srcu and extend srcu'ed section in ioctl handler
- [TLM]
+ renamed some fields to more strictly adhere to spec nomenclature
+ split v13 Basic Telemetry into patches [7-11]
+ split V13 Parse SHMTI into patches [12-14]
+ add a barrier to acquire rinfo->num_des on reset
+ avoid lock inversion on config_mtx
+ report scmi_telemetry_line_data_parse errors
+ add traces for CACHED_DE_UPDATED
+ using a less regular time interval for TDCF scan retries
+ move BLK_ST_CHANGED check when bts_payld is really known
+ check lso LINE_TYPE when processing BLK_TS on enable
+ grab individual tde->mtx while updating group state
+ clear returned sid/offset/uid on error path
+ do NOT mutate global config when configuring groups
+ call state_update on do_xfer success even if response process fails
+ fixed AB-BA dealock on blkts_link
+ relocate xa_destroy for xa_lines after uuids cleanup
+ report errors on scmi_telemetry_line_data_parse()
+ fix group unroll loop on groups_init failure
+ remove goto from de_desc_parse since clashes with cleanup guards
+ reworked the helpers used to register protocol notifiers so as to effectively work during the protocol initialization phase when locks are enabled
v12 --> v13
- rebased on v7.3-rc5
- [STLM]
+ misc fixes on alloc/free
+ add single DE dump capability to get info/cfg/data
+ added SHMTI/TDCF decoder (by LLM)
+ fix mmap len/offset call to account for bigger pages and offset
+ fixed SPDX header
- [SYS/TLM]
+ pass back E2BIG from copy_struct_from_user to userspace
+ remove a few redundant copy_from_user
+ fix filedescriptor lifecycle: drop kref in favour of embedded cdev and use cdev_device_add. Rely on devm_protocol_get for proto_ops
+ refactor EVENT session unsubscribe and check for aliveness
+ use *alloc_objs helpers
- [TLM]
+ reworked resourecs cleanup logic
+ use *alloc_objs where possible
+ reworked tde->mtx locks acquisition to improve synchronization between configuration actions and concurrent readers
+ refactored STATE_SET helpers to keep DE and GROUP handling distinct
+ handle PARTIAL_ERRORs
+ reworked logic around TDE<-->BLK_TS link/unlink
+ review policy around errors while scanning the TDCF: now retries assuming a torn read
+ harden nearest_line_type scanner
+ harden freeing path on RCU UUIDs
+ better DATA LINE size-validation while parsing
+ reset active lines trackers on scan retries
+ remove dubious macro TO_CPU64
+ rename DE_CONFIGURE defines
+ remove useless READ_ONCE
v11 --> v12
- rebased on v7.3-rc3
- [STLM]
+ renamed command helpers
+ reviewed internal/external errcodes usage
+ fixed dump_shmti read loop
+ fixed shmti_check end address calculation
+ fixed batch read boundary checks
+ fixed group indexing
- [SYS/TLM]
+ added RCU protection to UUIDs
+ reworked eventfd handling: per-session tracking with lifetime tied to chardev fds
+ dropped needless copy_from_user on batch.states
- [UAPI]: document new eventfd lifecycle
- [TLM]:
+ fix debugfs shmti dump file to use the SID as name
+ reviewed generation counter update logic
+ fixed TS_RATE determination at boot-on
+ updating cache within TDE lock when processing msg_payld
+ using config_mtx to sync with ongoing configurations
+ reporting transient DATA_INVALID lines as -EINVAL
+ added proper locking around TDE access on state_get
+ added a config_mtx to serialize configuration accesses between collection configure and state_set
+ refactored state_cache_update
+ reject zero as MATCHSEQ
+ reworked locking around line_get/put
+ reworked locking around blkts_read
+ cleanup active line refs even when bailing out of a scan in error
+ added hashed locks for fine grain locking around blkts/uuid line creation
+ added RCU protection on UUIDs access
+ dropped counted-by in smhtis
+ reworked barriers around res_get
+ added smp_store_release on TDE registration
v10 --> v11
- rebased on v7.3-rc1
- added initial RFC for KSelfTest
- [STLM]: add more 0-sized query check for new IOCTLs supported
- [SYS/TLM]: make devices 664
- [SYS/TLMM]: add new 0-sized query support for IOCTLs:
GET_INTRVS / BULK / SIMPLE
- [UAPI]: more docs and clarification on upper-bounds
- [UAPI]: more docs and clarification BATCH IOCTLs parameter usage
- [TLM]: move events cleanup into resource_free
- [TLM]: fix non-conformancy on CONFIG_GET when groups NON configurable
- [TLM]: use SCAN_UPDATE also on BOOT-ON initial scan (NO discovery)
- [TLM]: avoid 'naked' ti->rinfo accesses
- [TLM]: link notif DE to primary UUID only when first seen
- [TLM]: protect TDE updates on reset
- [TLM]: review tde->mtx locking
- [TLM]: review line.mtx locking
- [TLM]: refactor reply processing code on DE enable/disable
- [TLM]: add new mutex to protect des cache updates
- [TLM]: add more overflow and boundary check on payload parsing
- [TLM]: fix UUID/BLKTS line refcounting to avoid leaks on free
- [TLM]: cleanup exit path on resource enumeration
- [TLM]: account for ktime_t notification field when checking boundary
v9 --> v10
- rebased on v7.2-rc7
- fixed trivial conflict issue when applying patch 5
v8 --> v9
- [SYS/TLM] reworked ANON fds processing to delay fd_install to the very last moment when failure is no more possible
- [SYS/TLM] reworked cdev and data lifecycle to cleanly handle driver unbind while chardev fds are open, avoiding any devres allocation
- [SYS/TLM] added sensible upper-bounds checks on allocations
- [SYS/TLM] used kvmalloc and friends all over when allocation sizes originates from userspace or FW
- [SYS/TLM] reworked BATCH helpers to support batch.items=0
- [SYS/TLM] reworked ALL the _LIST IOCTLs to use scmi_tlm_batch container and related helpers
- [SYS/TLM] support 0-items IOCTL idiom
- [ABI] dropped custom container structure in favour of scmi_tlm_batch for all of the _LIST IOCVTLs
- [TLM] use kvzalloc when size is potentially important
- [TLM] avoid NULL deref on DBG
- [TLM] removed initial state lookup on RESET
- [TLM] fix notification payload boundary and wraps checks
- [TLM] added check on unexpected shorter reply on simpl reads
- [TLM] use non-LE accessors when appropriate for payloads
- [TLM] converted a few macros to inlines
- [TLM] fix DE offsest boundary check logic
- [TLM] bail out if SHMTI not 32-bit aligned
- [TLM] warn if SHMTI not 64-bit aligned
- [DRV] added non-devres protocol operations helpers
v7 --> v8
- Rebased on v7.2-rc6
- [SYS/TLM] refatcor batched IOCTLs handling
- [SYS/TLM] use array_size to check for overflows all over
- [SYS/TLM] add upper bounds checks for batched IOCTL
- [ABI] fix wrong strcut in SCMI_TLM_BATCH_READ
- [ABI] move a few IOCTLs to be _IOW only
- [TLM] reworked locking around TDCF lines tracking
- [TLM] add proper barriers to TDE/UUID binding process
- [TLM] update tde->sid on SCAN_UPDATE too
- [TLM] check wrap-arounds on shmti_de_offset manipulation
- [TLM] remove .all_disable telemetry_operation since unused
- [TLM] add WARN on if a valid SHMTI ID is reported o
enabling a FastChannel/DE
- [TLM] add more safeguards and checks to SHMTI_OVERFLOWS
- [TLM] fix UUIDs copy and allocation
- [TLM] fix UUID reference store and cleanup
- [TLM] add proper msg boundary parsing checks on DE_DESCRIPTION multipart
- [TLM] add proper cleanup/rollback on groups_init err_path
- [TLM] update num_des_tstamp ONLY after a DE is successfully initialized
- [TLM] proper cleanup tde during tde_put (preserving info/mutex bits)
- [TLM] add membarriers in enumerate resources
- [TLM] add a check and a warn on SHMTI phys_addr alignment
- [TLM] use proper FTRACE helpers for variable strings
v6 --> v7
- [ABI] IOCTL EVENTS support: GENERATION COUNTER (stlm monitor)
- [ABI] expose DE tracking: UUID/SHMTI/OFFS
- [ABI] IOCTL BATCHED DE_CFG
- [ABI] added per-DE IOCTL BATCH status
- [ABI] new ABI feats flags
- [ABI] improved description in Doxygen docs
- [ABI] added reserved space to grow
- [STLM] added new ABI feats support (generation, UUIDs, location...)
- [SYS/TLM] fixed module_init error path
- [SYS/TLM] fixed interval DISCRETE flags reporting
- [SYS/TLM] check open FMODE before executing a config change
- [SYS/TLM] fixing DEs data cleanup on RESET
- [SYS/TLM] added _RAW helpers to handle non-MMIO TDCF-like accesses (like Notification payload)
- [SYS/TLM] added UUID rescan logic at DE enable, when DE/UUID association
- [TLM] reworked UUID internal handling (endianity) and use uuid_t type
- [TLM] added generic Telemetry protocol support for events subscription unknown
- residual sparse fixes
v5 --> v6
- rebased on v7.2-rc4
- fixed a lot of Sashiko complains
- fixed ABI issues reported by review (Fayssal)
- added IOCTL TLM_RESET
- added ABI versioning
- added compat_ioctl support
- use a new IOCTL magic name
- better handling of UUID endianity
- reworked all bounds checks in UAPI implementation
- reject oversized SHMTI mmap request
- refined 'stlm' testing tool to be more interactive and to exercise the full spectrum of IOCTLs ('stlm -h' is your manual for now :P)
- fully reworked per-protocol notification handling
- dropped the last bit of human readable data attached to the chardev .read
v4 --> v5
- rebased on v7.2-rc1
- dropped FileSystem based driver
- introduced a new simple chardev SCMI driver using Telemetry
- reworked/reviewed the v4 IOCTLs based UAPI
- UAPI: better struct alignment and comments
- UAI: Removed flexible array members
- UAPI: make SCMI Telemetry protocol stack completely independent from uapi defs
- UAPI: new ioctls support to enable RAW mmap direct access to SCMI SHMTI areas from userspace
- added new ABI Documentation
- added new SCMI core facility to lookup the current SCMI instance ID
v3 --> v4
- rebased on v7.1-rc7
- updatded doc to detail Concurrency model
- bail out on FW_BUG errors
- make all_des_enable/all_des_tstamp_enable entry readable
- refactored access to TDE values
- refactored common accessors for tlm_priv (FIX WARN on kfree)
- make all files by default world readable and user writable (if needed)
- added uid/god/umask mount options (and docs)
- added generation counter to aid spotting config changes (and docs)
- added DebugFS configurable support to debug/dump SHMTI areas (and docs)
- hide FS entries when NOT supported (like des_simple_sample_read)
- fixed output format of des/<NNN>/value to -> <TS> <VALUE>
- renamed top-dir by_components to by-components
- add a .remove method to SCMI System Telemetry Driver
- use kzalloc_obj
V2 --> V3
- rebased on v7.0-rc5
- ported the firmware interface to SCMI v4.0 BETA
- split the SCMI protocol layer in a lot of small patches
- completd filesystem and ABI documentation
- renamed components subtree to by_components
- fixed uninitialized var in scmi_telemetry_de_subdir_symlink
- renamd tstamp_exp to tstamp_rate
- swap logic in scmi_telemetry_initial_state_lookup
- use memcpy_from_le32 where required
- changed a dfew dev_err into Telemetry traces
- define and use new helper scmi_telemetry_de_unlink
- simplify a few assignments with ternary ops
- added a missing __mmust_check on the internal SCMI API
- reworked and clarified de_data_read returned errno:
ENODATA vs EINVAL vs ENODEV/ENOENT
- removed some risky/unneeded devres allocations
- various checkpatch fixes
- reworked and clarified usage of traces in Telemetry
- added the missing DT binding for protocol 0x1B
- split out unrelated change around notification from patch adding support for protocol internal notifier
- more comments
V1 --> V2
- rebased on v6.19-rc3
- harden TDCF shared memory areas accesses by using proper accessors
- reworked protocol resources lifecycle to allow lazy enumeration
- using NEW FS mount API
- reworked FS inode allocation to use a std kmem_cache
- fixed a few IOCTLs support routine to support lazy enumeration
- added (RFC) a new FS lazy mount option to support lazily population of some subtrees of the FS (des/ groups/ components/)
- reworked implementation of components/ alternative FS view to use symlinks instead of hardlinks
- added a basic simple (RFC) testing tool to exercise UAPI ioctls interface
- hardened Telmetry protocol and driver to support partial out-of-spec FW lacking some cmds (best effort)
- reworked probing races handling
- reviewed behaviour on unmount/unload
- added support for Boot_ON Telemetry by supporting SCMI Telemetry cmds:
+ DE_ENABLED_LIST
+ CONFIG_GET
- added FS and ABI docs
RFC --> V1
---
- moved from SysFS/chardev to a full fledged FS
- added support for SCMI Telemetry BLK timestamps
[0]: https://developer.arm.com/documentation/den0056/f/?lang=en
[1]: https://git.kernel.org/pub/scm/linux/kernel/git/cris/linux.git/log/?h=scmi_telemetry_ng_V15
Cristian Marussi (34):
firmware: arm_scmi: Harden iterators state parsing
firmware: arm_scmi: Add new SCMIv4.0 error codes definitions
firmware: arm_scmi: Introduce a dedicated SCMI Errors header
firmware: arm_scmi: Allow registration of unknown-size events/reports
firmware: arm_scmi: Refactor __scmi_event_handler_get_ops helper
firmware: arm_scmi: Introduce protocol instance notifiers
dt-bindings: firmware: arm,scmi: Add support for telemetry protocol
include: trace: Add Telemetry trace events
firmware: arm_scmi: Add Telemetry basic attributes enumeration
firmware: arm_scmi: Add Telemetry resources enumeration
firmware: arm_scmi: Add Telemetry groups initialization
firmware: arm_scmi: Add Telemetry update intervals enumeration
firmware: arm_scmi: Add Telemetry SHMTI areas enumeration
firmware: arm_scmi: Add Telemetry TDCF areas parsing support
firmware: arm_scmi: Add Telemetry TDCF parsing for BLK_TS lines
firmware: arm_scmi: Add Telemetry TDCF parsing for UUID lines
firmware: arm_scmi: Add Telemetry configuration operations
firmware: arm_scmi: Add Telemetry DataEvent read capabilities
firmware: arm_scmi: Add Telemetry reset logic
firmware: arm_scmi: Add Telemetry notification support
firmware: arm_scmi: Add Telemetry boot-on support
firmware: arm-scmi: Add Telemetry generic event support
firmware: arm_scmi: Add Telemetry generation counter event
firmware: arm_scmi: Add common per-protocol debugfs support
firmware: arm_scmi: Add Telemetry debugfs SHMTI dump support
firmware: arm_scmi: Add Telemetry debugfs ABI documentation
firmware: arm_scmi: Expose per-instance identifier
firmware: arm_scmi: Add un-managed methods to get/put protocols
operations
uapi: Add ARM SCMI Telemetry definitions
firmware: arm_scmi: Add System Telemetry driver
firmware: arm_scmi: Enable autoloading for System Telemetry driver
docs: ioctl-number: Add SCMI Ioctls
Documentation: Add SCMI System Telemetry documentation [RFC] tools/testing/scmi: Add SCMI Telemetry testing tool
Philip Radford (1):
[RFC] kselftest/arm64: Add SCMI Telemetry UAPI compliance testcases
Documentation/ABI/testing/debugfs-scmi | 22 + .../bindings/firmware/arm,scmi.yaml | 8 + Documentation/userspace-api/index.rst | 1 + .../userspace-api/ioctl/ioctl-number.rst | 1 + Documentation/userspace-api/stlm.rst | 590 ++ MAINTAINERS | 2 + drivers/firmware/arm_scmi/Kconfig | 24 + drivers/firmware/arm_scmi/Makefile | 3 +- drivers/firmware/arm_scmi/bus.c | 1 + drivers/firmware/arm_scmi/common.h | 65 +- drivers/firmware/arm_scmi/driver.c | 209 +- drivers/firmware/arm_scmi/errors.h | 56 + drivers/firmware/arm_scmi/notify.c | 256 +- drivers/firmware/arm_scmi/notify.h | 8 +- drivers/firmware/arm_scmi/protocols.h | 23 + .../firmware/arm_scmi/scmi_system_telemetry.c | 1788 ++++++ drivers/firmware/arm_scmi/telemetry.c | 5196 +++++++++++++++++ include/linux/scmi_protocol.h | 269 +- include/trace/events/scmi.h | 45 + include/uapi/linux/scmi.h | 531 ++ tools/testing/scmi/Makefile | 25 + tools/testing/scmi/stlm.c | 1692 ++++++ tools/testing/scmi/stlm_decode.c | 252 + tools/testing/scmi/stlm_decode.h | 7 + tools/testing/selftests/arm64/Makefile | 2 +- tools/testing/selftests/arm64/scmi/Makefile | 12 + tools/testing/selftests/arm64/scmi/config | 2 + .../arm64/scmi/test_scmi_telemetry_uapi.c | 1810 ++++++ 28 files changed, 12801 insertions(+), 99 deletions(-) create mode 100644 Documentation/userspace-api/stlm.rst create mode 100644 drivers/firmware/arm_scmi/errors.h create mode 100644 drivers/firmware/arm_scmi/scmi_system_telemetry.c create mode 100644 drivers/firmware/arm_scmi/telemetry.c create mode 100644 include/uapi/linux/scmi.h create mode 100644 tools/testing/scmi/Makefile create mode 100644 tools/testing/scmi/stlm.c create mode 100644 tools/testing/scmi/stlm_decode.c create mode 100644 tools/testing/scmi/stlm_decode.h create mode 100644 tools/testing/selftests/arm64/scmi/Makefile create mode 100644 tools/testing/selftests/arm64/scmi/config create mode 100644 tools/testing/selftests/arm64/scmi/test_scmi_telemetry_uapi.c
Signature · 1 line
2.54.0
Cristian Marussi
reviewerPATCH v15 1/35 firmware: arm_scmi: Harden iterators state parsing
Add checks in the core multi-part iterators support to detect generally malformed and incosistent multi-part replies.
--- drivers/firmware/arm_scmi/driver.c | 10 ++++++++++ drivers/firmware/arm_scmi/protocols.h | 6 ++++++ 2 files changed, 16 insertions(+)
drivers/firmware/arm_scmi/driver.c +10 -0
@@ -1873,11 +1873,21 @@ static int __scmi_iterator_run(void *iter, unsigned int *start, unsigned int *enif (ret)return ret; +if (i->t->rx.len <SCMI_MULTIPART_REPLY_MIN_SZ ) { +dev_err (ph->dev,"Invalid multi-part reply.\n" ); +return -EINVAL ; + } + st->rx_len = i->t->rx.len; ret = iops->update_state (st, i->resp, i->priv);if (ret)return ret; +if (!st->num_returned && st->num_remaining) { +dev_err (ph->dev,"Inconsistent multi-part reply.\n" ); +return -EINVAL ; + } +if (st->num_returned > st->max_resources - st->desc_index) { dev_err(ph->dev, "No. of resources can't exceed %d\n",
drivers/firmware/arm_scmi/protocols.h +6 -0
@@ -33,6 +33,12 @@#define MSG_SUPPORTS_FASTCHANNEL (x) ((x) & BIT(0)) +/* +* Common minimum size currently required by all the existing standard and +* vendor protocols. +*/ +#define SCMI_MULTIPART_REPLY_MIN_SZ (sizeof(u32)) +enum scmi_common_cmd {PROTOCOL_VERSION =0x0 ,PROTOCOL_ATTRIBUTES =0x1 ,
Signature · 1 line
2.54.0
Re: [PATCH v15 01/35] firmware: arm_scmi: Harden iterators state parsing
Quoted 2 lines · On Mon, 9 Nov 2026 09:11:47 +0000 Cristian Marussi <[email protected]>…
Add checks in the core multi-part iterators support to detect generally malformed and incosistent multi-part replies.
Spell check patch descriptions. inconsistent.
Otherwise seems fine but I'm too lazy to check all existing protocols for that minimum size!
Quoted 50 lines
Signed-off-by Cristian Marussi <[email protected]>--- drivers/firmware/arm_scmi/driver.c | 10 ++++++++++ drivers/firmware/arm_scmi/protocols.h | 6 ++++++ 2 files changed, 16 insertions(+)drivers/firmware/arm_scmi/driver.c +10
-0@@ -1873,11 +1873,21 @@ static int __scmi_iterator_run(void *iter, unsigned int *start, unsigned int *enif (ret)return ret; +if (i->t->rx.len <SCMI_MULTIPART_REPLY_MIN_SZ ) { +dev_err (ph->dev,"Invalid multi-part reply.\n" ); +return -EINVAL ; + } + st->rx_len = i->t->rx.len; ret = iops->update_state (st, i->resp, i->priv);if (ret)return ret; +if (!st->num_returned && st->num_remaining) { +dev_err (ph->dev,"Inconsistent multi-part reply.\n" ); +return -EINVAL ; + } +if (st->num_returned > st->max_resources - st->desc_index) { dev_err(ph->dev, "No. of resources can't exceed %d\n",drivers/firmware/arm_scmi/protocols.h +6
-0@@ -33,6 +33,12 @@#define MSG_SUPPORTS_FASTCHANNEL (x) ((x) & BIT(0)) +/* +* Common minimum size currently required by all the existing standard and +* vendor protocols. +*/ +#define SCMI_MULTIPART_REPLY_MIN_SZ (sizeof(u32)) +enum scmi_common_cmd {PROTOCOL_VERSION =0x0 ,PROTOCOL_ATTRIBUTES =0x1 ,
Cristian Marussi
reviewerRe: [PATCH v15 01/35] firmware: arm_scmi: Harden iterators state parsing
Quoted 7 lines · Jonathan Cameron
Quoted 2 lines · On Mon, 9 Nov 2026 09:11:47 +0000 Cristian Marussi <[email protected]>…
Add checks in the core multi-part iterators support to detect generally malformed and incosistent multi-part replies.
Spell check patch descriptions. inconsistent.
Ah...checkpatch let me down another time
Quoted 4 lines
Otherwise seems fine but I'm too lazy to check all existing protocols for that minimum size!
Indeed I was too lazy to add a dedicated parameter to all the call-sites so I verified that any possible standard/vendor protocol has that minimum size as of now, since in fcat it is the minimum viable payload needed to hold num_returned/num_remanining...if any future spec will change this we'll evolve accordingly/...for now it is the simplest approach (and kills a lot of Sashiko complaints scattered all over :P)
Thanks,
Cristian
Cristian Marussi
reviewerPATCH v15 2/35 firmware: arm_scmi: Add new SCMIv4.0 error codes definitions
In reply to Cristian MarussiSCMIv4.0 introduces a couple of new possible protocol error codes: add the needed definitions and mappings to Linux error values.
Note that SCMI_PARTIAL_ERROR has to be handled specially and as such it needs to be distinguishable but, as of now, all of the available sensible error codes are already mapped to some other pre-existent SCMI error condition, so we settled on -EBADMSG which is far from ideal, but at least kind of captures the nature of the underlying issue.
---
v12 --> v13
- use EBADMSG to map SCMI_PARTIAL_ERROR
v4 --> v5
- map SCMI_ERR_IN_USE to EBUSY as natural
--- drivers/firmware/arm_scmi/common.h | 4 ++++ 1 file changed, 4 insertions(+)
drivers/firmware/arm_scmi/common.h +4 -0
@@ -49,6 +49,8 @@ enum scmi_error_codes {SCMI_ERR_GENERIC =-8 ,/* Generic Error */ SCMI_ERR_HARDWARE =-9 ,/* Hardware Error */ SCMI_ERR_PROTOCOL =-10 ,/* Protocol Error */ +SCMI_ERR_IN_USE =-11 ,/* In Use Error */ + SCMI_ERR_PARTIAL = -12, /* Partial Error */ }; static const int scmi_linux_errmap[] = {
@@ -64,6 +66,8 @@ static const int scmi_linux_errmap[] = { -EIO ,/* SCMI_ERR_GENERIC */ -EREMOTEIO ,/* SCMI_ERR_HARDWARE */ -EPROTO ,/* SCMI_ERR_PROTOCOL */ + -EBUSY ,/* SCMI_ERR_IN_USE */ + -EBADMSG, /* SCMI_ERR_PARTIAL */ };static inline int scmi_to_linux_errno (int errno)
Signature · 1 line
2.54.0
Cristian Marussi
reviewerPATCH v15 3/35 firmware: arm_scmi: Introduce a dedicated SCMI Errors header
In reply to Cristian MarussiAdd a new dedicated header and move all of the errors definitions and mapping helpers into it and out of common.h so that SCMI errors definitions alone can be easily included in protocol files without all of the unrelated additional stuff contained in common.h
--- drivers/firmware/arm_scmi/common.h | 43 +---------------------- drivers/firmware/arm_scmi/errors.h | 56 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 drivers/firmware/arm_scmi/errors.h
drivers/firmware/arm_scmi/common.h +1 -42
@@ -26,6 +26,7 @@#include <linux/unaligned.h> +#include "errors.h" #include "protocols.h" #include "notify.h"
@@ -37,48 +38,6 @@#define SCMI_TRANSPORT_DEVNAME_PREFIX "__scmi_transport_device" -enum scmi_error_codes { -SCMI_SUCCESS =0 ,/* Success */ -SCMI_ERR_SUPPORT =-1 ,/* Not supported */ -SCMI_ERR_PARAMS =-2 ,/* Invalid Parameters */ -SCMI_ERR_ACCESS =-3 ,/* Invalid access/permission denied */ -SCMI_ERR_ENTRY =-4 ,/* Not found */ -SCMI_ERR_RANGE =-5 ,/* Value out of range */ -SCMI_ERR_BUSY =-6 ,/* Device busy */ -SCMI_ERR_COMMS =-7 ,/* Communication Error */ -SCMI_ERR_GENERIC =-8 ,/* Generic Error */ -SCMI_ERR_HARDWARE =-9 ,/* Hardware Error */ -SCMI_ERR_PROTOCOL =-10 ,/* Protocol Error */ -SCMI_ERR_IN_USE =-11 ,/* In Use Error */ -SCMI_ERR_PARTIAL =-12 ,/* Partial Error */ -}; - -static const int scmi_linux_errmap[] = { -/* better than switch case as long as return value is continuous */ -0 ,/* SCMI_SUCCESS */ - -EOPNOTSUPP ,/* SCMI_ERR_SUPPORT */ - -EINVAL ,/* SCMI_ERR_PARAM */ - -EACCES ,/* SCMI_ERR_ACCESS */ - -ENOENT ,/* SCMI_ERR_ENTRY */ - -ERANGE ,/* SCMI_ERR_RANGE */ - -EBUSY ,/* SCMI_ERR_BUSY */ - -ECOMM ,/* SCMI_ERR_COMMS */ - -EIO ,/* SCMI_ERR_GENERIC */ - -EREMOTEIO ,/* SCMI_ERR_HARDWARE */ - -EPROTO ,/* SCMI_ERR_PROTOCOL */ - -EBUSY ,/* SCMI_ERR_IN_USE */ - -EBADMSG ,/* SCMI_ERR_PARTIAL */ -}; - -static inline int scmi_to_linux_errno (int errno) -{ -int err_idx = -errno; - -if (err_idx >=SCMI_SUCCESS && err_idx <ARRAY_SIZE (scmi_linux_errmap)) -return scmi_linux_errmap[err_idx]; -return -EIO ; -} -#define MSG_ID_MASK GENMASK(7, 0)#define MSG_XTRACT_ID (hdr) FIELD_GET(MSG_ID_MASK, (hdr))#define MSG_TYPE_MASK GENMASK(9, 8)
drivers/firmware/arm_scmi/errors.h new file +56 -0
56 lines not on this page · raw message
Signature · 1 line
2.54.0
Re: [PATCH v15 03/35] firmware: arm_scmi: Introduce a dedicated SCMI Errors header
Quoted 4 lines · On Mon, 9 Nov 2026 09:11:49 +0000 Cristian Marussi <[email protected]>…
Add a new dedicated header and move all of the errors definitions and mapping helpers into it and out of
common.hso that SCMI errors definitions alone can be easily included in protocol files without all of the unrelated additional stuff contained incommon.h
In ideal world I'd move first and add the new codes later. (so swap this and previous patch).
Trivial thing but reduce the patch size a tiny bit!
Other than that, seems like there are enough of these for a separate header to perhaps make sense.
Quoted 131 lines
Signed-off-by Cristian Marussi <[email protected]>--- drivers/firmware/arm_scmi/common.h | 43 +---------------------- drivers/firmware/arm_scmi/errors.h | 56 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 drivers/firmware/arm_scmi/errors.hdrivers/firmware/arm_scmi/common.h +1
-42@@ -26,6 +26,7 @@#include <linux/unaligned.h> +#include "errors.h" #include "protocols.h" #include "notify.h" @@ -37,48 +38,6 @@#define SCMI_TRANSPORT_DEVNAME_PREFIX "__scmi_transport_device" -enum scmi_error_codes { -SCMI_SUCCESS =0 ,/* Success */ -SCMI_ERR_SUPPORT =-1 ,/* Not supported */ -SCMI_ERR_PARAMS =-2 ,/* Invalid Parameters */ -SCMI_ERR_ACCESS =-3 ,/* Invalid access/permission denied */ -SCMI_ERR_ENTRY =-4 ,/* Not found */ -SCMI_ERR_RANGE =-5 ,/* Value out of range */ -SCMI_ERR_BUSY =-6 ,/* Device busy */ -SCMI_ERR_COMMS =-7 ,/* Communication Error */ -SCMI_ERR_GENERIC =-8 ,/* Generic Error */ -SCMI_ERR_HARDWARE =-9 ,/* Hardware Error */ -SCMI_ERR_PROTOCOL =-10 ,/* Protocol Error */ -SCMI_ERR_IN_USE =-11 ,/* In Use Error */ -SCMI_ERR_PARTIAL =-12 ,/* Partial Error */ -}; - -static const int scmi_linux_errmap[] = { -/* better than switch case as long as return value is continuous */ -0 ,/* SCMI_SUCCESS */ - -EOPNOTSUPP ,/* SCMI_ERR_SUPPORT */ - -EINVAL ,/* SCMI_ERR_PARAM */ - -EACCES ,/* SCMI_ERR_ACCESS */ - -ENOENT ,/* SCMI_ERR_ENTRY */ - -ERANGE ,/* SCMI_ERR_RANGE */ - -EBUSY ,/* SCMI_ERR_BUSY */ - -ECOMM ,/* SCMI_ERR_COMMS */ - -EIO ,/* SCMI_ERR_GENERIC */ - -EREMOTEIO ,/* SCMI_ERR_HARDWARE */ - -EPROTO ,/* SCMI_ERR_PROTOCOL */ - -EBUSY ,/* SCMI_ERR_IN_USE */ - -EBADMSG ,/* SCMI_ERR_PARTIAL */ -}; - -static inline int scmi_to_linux_errno (int errno) -{ -int err_idx = -errno; - -if (err_idx >=SCMI_SUCCESS && err_idx <ARRAY_SIZE (scmi_linux_errmap)) -return scmi_linux_errmap[err_idx]; -return -EIO ; -} -#define MSG_ID_MASK GENMASK(7, 0)#define MSG_XTRACT_ID (hdr) FIELD_GET(MSG_ID_MASK, (hdr))#define MSG_TYPE_MASK GENMASK(9, 8)drivers/firmware/arm_scmi/errors.h new file +56
-0@@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* System Control and Management Interface (SCMI) Message Protocol +* header containing SCMI errors definitions and Linux mapping. +* +* Copyright (C) 2018-2026 ARM Ltd. +*/ +#ifndef _SCMI_ERRORS_H +#define _SCMI_ERRORS_H + +#include <linux/errno.h> +#include <linux/array_size.h> + +enum scmi_error_codes { +SCMI_SUCCESS =0 ,/* Success */ +SCMI_ERR_SUPPORT =-1 ,/* Not supported */ +SCMI_ERR_PARAMS =-2 ,/* Invalid Parameters */ +SCMI_ERR_ACCESS =-3 ,/* Invalid access/permission denied */ +SCMI_ERR_ENTRY =-4 ,/* Not found */ +SCMI_ERR_RANGE =-5 ,/* Value out of range */ +SCMI_ERR_BUSY =-6 ,/* Device busy */ +SCMI_ERR_COMMS =-7 ,/* Communication Error */ +SCMI_ERR_GENERIC =-8 ,/* Generic Error */ +SCMI_ERR_HARDWARE =-9 ,/* Hardware Error */ +SCMI_ERR_PROTOCOL =-10 ,/* Protocol Error */ +SCMI_ERR_IN_USE =-11 ,/* In Use Error */ +SCMI_ERR_PARTIAL =-12 ,/* Partial Error */ +}; + +static const int scmi_linux_errmap[] = { +/* better than switch case as long as return value is continuous */ +0 ,/* SCMI_SUCCESS */ + -EOPNOTSUPP ,/* SCMI_ERR_SUPPORT */ + -EINVAL ,/* SCMI_ERR_PARAM */ + -EACCES ,/* SCMI_ERR_ACCESS */ + -ENOENT ,/* SCMI_ERR_ENTRY */ + -ERANGE ,/* SCMI_ERR_RANGE */ + -EBUSY ,/* SCMI_ERR_BUSY */ + -ECOMM ,/* SCMI_ERR_COMMS */ + -EIO ,/* SCMI_ERR_GENERIC */ + -EREMOTEIO ,/* SCMI_ERR_HARDWARE */ + -EPROTO ,/* SCMI_ERR_PROTOCOL */ + -EBUSY ,/* SCMI_ERR_IN_USE */ + -EBADMSG ,/* SCMI_ERR_PARTIAL */ +}; + +static inline int scmi_to_linux_errno (int errno) +{ +int err_idx = -errno; + +if (err_idx >=SCMI_SUCCESS && err_idx <ARRAY_SIZE (scmi_linux_errmap)) +return scmi_linux_errmap[err_idx]; +return -EIO ; +} + +#endif /* _SCMI_ERRORS_H */
Cristian Marussi
reviewerRe: [PATCH v15 03/35] firmware: arm_scmi: Introduce a dedicated SCMI Errors header
Quoted 11 lines · Jonathan Cameron
Quoted 4 lines · On Mon, 9 Nov 2026 09:11:49 +0000 Cristian Marussi <[email protected]>…
Add a new dedicated header and move all of the errors definitions and mapping helpers into it and out of
common.hso that SCMI errors definitions alone can be easily included in protocol files without all of the unrelated additional stuff contained incommon.hIn ideal world I'd move first and add the new codes later. (so swap this and previous patch).
Ok but logically the new separated header is needed by the fact that the new added error codes has to be checked in-protocol and including common.h in protocol unit bring in a lot of unneeded stuff.
Quoted 6 lines
Trivial thing but reduce the patch size a tiny bit!
Other than that, seems like there are enough of these for a separate header to perhaps make sense.
Reviewed-by Jonathan Cameron <[email protected]>
Thanks,
Cristian
Cristian Marussi
reviewerPATCH v15 4/35 firmware: arm_scmi: Allow registration of unknown-size events/reports
In reply to Cristian MarussiAllow protocols to register events with build-time unknown sizes: such events can be declared zero-sized and let the core SCMI stack perform the needed safe-net boundary checks based on the configured transport size.
---
v14 --> v15
- better commenting
v12 --> v13
- improve variable naming
v10 --> v11
- account for ktime_t field when checking boundary
v2 --> v3
- split out of previous patch on protocol notifier
- use max() instead of max_t()
--- drivers/firmware/arm_scmi/notify.c | 28 +++++++++++++++++++++++----- drivers/firmware/arm_scmi/notify.h | 8 ++++++-- 2 files changed, 29 insertions(+), 7 deletions(-)
drivers/firmware/arm_scmi/notify.c +23 -5
@@ -595,7 +595,16 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,if (!r_evt)return -EINVAL ; -if (len > r_evt->evt->max_payld_sz) { +/* +* Events with a zero max_payld_sz are sized to be of the maximum +* size allowed by the transport: no need to be size-checked here +* since the transport layer would have already dropped such +* over-sized messages. +* Note that, though, while this protects from buffer overruns, it is +* no more possible at this layer of the stack to detect zero-sized +* events corrupted by truncation. +*/ +if (r_evt->evt->max_payld_sz && len > r_evt->evt->max_payld_sz) {dev_err (handle->dev,"discard badly sized message\n" );return -EINVAL ; }
@@ -751,7 +760,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,const struct scmi_protocol_handle *ph, const struct scmi_protocol_events *ee) { - int i; + int i, max_msg_sz, default_report_sz;unsigned int num_sources;size_t payld_sz =0 ;struct scmi_registered_events_desc *pd;
@@ -766,6 +775,9 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,if (!ni)return -ENOMEM ; + max_msg_sz = ph->hops->get_max_msg_size (ph); + default_report_sz = max_msg_sz +sizeof (ktime_t); +/* num_sources cannot be <= 0 */ if (ee->num_sources) { num_sources = ee->num_sources;
@@ -778,8 +790,13 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, } evt = ee->evts; -for (i =0 ; i < ee->num_events; i++) - payld_sz =max_t (size_t, payld_sz, evt[i].max_payld_sz); +for (i =0 ; i < ee->num_events; i++) { +if (evt[i].max_payld_sz ==0 ) { + payld_sz = max_msg_sz; +break ; + } + payld_sz =max (payld_sz, evt[i].max_payld_sz); + } payld_sz +=sizeof (struct scmi_event_header ); pd = scmi_allocate_registered_events_desc(ni, proto_id, ee->queue_sz,
@@ -805,7 +822,8 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,mutex_init (&r_evt->sources_mtx); r_evt->report =devm_kzalloc (ni->handle->dev, - evt->max_report_sz,GFP_KERNEL ); + evt->max_report_sz ?: default_report_sz, +GFP_KERNEL );if (!r_evt->report)return -ENOMEM ;
drivers/firmware/arm_scmi/notify.h +6 -2
@@ -18,8 +18,12 @@/** * struct scmi_event - Describes an event to be supported * @id: Event ID -* @max_payld_sz: Max possible size for the payload of a notification message -* @max_report_sz: Max possible size for the report of a notification message +* @max_payld_sz: Max possible size for the payload of a notification message. +* Set to zero to use the maximum message size allowed by the +* transport. +* @max_report_sz: Max possible size for the report of a notification message. +* Set to zero to use the maximum message size allowed by the +* transport plus the additional ktime_t timestamp. * * Each SCMI protocol, during its initialization phase, can describe the events * it wishes to support in a few struct scmi_event and pass them to the core
Signature · 1 line
2.54.0
Re: [PATCH v15 04/35] firmware: arm_scmi: Allow registration of unknown-size events/reports
Quoted 9 lines · On Mon, 9 Nov 2026 09:11:50 +0000 Cristian Marussi <[email protected]>…
Allow protocols to register events with build-time unknown sizes: such events can be declared zero-sized and let the core SCMI stack perform the needed safe-net boundary checks based on the configured transport size.
Reviewed-by Jonathan Cameron <[email protected]>Signed-off-by Cristian Marussi <[email protected]>---
v14 --> v15
- better commenting
A few bits of feedback on the comments.
Quoted 17 lines
v12 --> v13
- improve variable naming
v10 --> v11
- account forktime_tfield when checking boundary
v2 --> v3
- split out of previous patch on protocol notifier
- usemax()instead ofmax_t()--- drivers/firmware/arm_scmi/notify.c | 28 +++++++++++++++++++++++----- drivers/firmware/arm_scmi/notify.h | 8 ++++++-- 2 files changed, 29 insertions(+), 7 deletions(-)drivers/firmware/arm_scmi/notify.c +0
-0
drivers/firmware/arm_scmi/notify.c int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, · line 602
Quoted 9 lines
drivers/firmware/arm_scmi/notify.c +5
-1@@ -595,7 +595,16 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,if (!r_evt)return -EINVAL ; -if (len > r_evt->evt->max_payld_sz) { +/* +* Events with a zero max_payld_sz are sized to be of the maximum +* size allowed by the transport: no need to be size-checked here +* since the transport layer would have already dropped such +* over-sized messages.
'such' here to me refers to the first bit. That is, those that are " sized to be the maxim size allowed by the transport.
I'd just drop 'such' and have
* since the transport layer would have already dropped over-sized
* messages.
as that avoids this potential confusion.
drivers/firmware/arm_scmi/notify.c int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, · line 603
Quoted 1 line
drivers/firmware/arm_scmi/notify.c +1
-0+* Note that, though, while this protects from buffer overruns, it is
Drop 'though'. Doesn't add anything and leads to odd flow to the sentence
drivers/firmware/arm_scmi/notify.c int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, · line 604
Quoted 1 line
drivers/firmware/arm_scmi/notify.c +1
-0+* no more possible at this layer of the stack to detect zero-sized
'not possible at this layer ..'
The 'no more' refers to code that isn't here if someone reads this code once the patch is merged. So if you want to refer to what changed, keep it in the patch description not a comment.
Quoted 6 lines
drivers/firmware/arm_scmi/notify.c +3
-0+* events corrupted by truncation. +*/ +if (r_evt->evt->max_payld_sz && len > r_evt->evt->max_payld_sz) {dev_err (handle->dev,"discard badly sized message\n" );return -EINVAL ; }
Cristian Marussi
reviewerRe: [PATCH v15 04/35] firmware: arm_scmi: Allow registration of unknown-size events/reports
Quoted 55 lines · Jonathan Cameron
Quoted 9 lines · On Mon, 9 Nov 2026 09:11:50 +0000 Cristian Marussi <[email protected]>…
Allow protocols to register events with build-time unknown sizes: such events can be declared zero-sized and let the core SCMI stack perform the needed safe-net boundary checks based on the configured transport size.
Reviewed-by Jonathan Cameron <[email protected]>Signed-off-by Cristian Marussi <[email protected]>---
v14 --> v15
- better commentingA few bits of feedback on the comments.
Quoted 17 lines
v12 --> v13
- improve variable naming
v10 --> v11
- account forktime_tfield when checking boundary
v2 --> v3
- split out of previous patch on protocol notifier
- usemax()instead ofmax_t()--- drivers/firmware/arm_scmi/notify.c | 28 +++++++++++++++++++++++----- drivers/firmware/arm_scmi/notify.h | 8 ++++++-- 2 files changed, 29 insertions(+), 7 deletions(-)drivers/firmware/arm_scmi/notify.c +0
-0drivers/firmware/arm_scmi/notify.c int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, · line 602
Quoted 9 lines
drivers/firmware/arm_scmi/notify.c +5
-1@@ -595,7 +595,16 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,if (!r_evt)return -EINVAL ; -if (len > r_evt->evt->max_payld_sz) { +/* +* Events with a zero max_payld_sz are sized to be of the maximum +* size allowed by the transport: no need to be size-checked here +* since the transport layer would have already dropped such +* over-sized messages. 'such' here to me refers to the first bit. That is, those that are " sized to be the maxim size allowed by the transport.
I'd just drop 'such' and have
* since the transport layer would have already dropped over-sized
* messages.as that avoids this potential confusion.
drivers/firmware/arm_scmi/notify.c int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, · line 603
Quoted 1 line
drivers/firmware/arm_scmi/notify.c +1
-0+* Note that, though, while this protects from buffer overruns, it is Drop 'though'. Doesn't add anything and leads to odd flow to the sentence
Quoted 8 lines
drivers/firmware/arm_scmi/notify.c int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, · line 604
Quoted 1 line
drivers/firmware/arm_scmi/notify.c +1
-0+* no more possible at this layer of the stack to detect zero-sized 'not possible at this layer ..'
The 'no more' refers to code that isn't here if someone reads this code once the patch is merged. So if you want to refer to what changed, keep it in the patch description not a comment.
Thanks...it seems like the more I try to write clear and meaningful sentences in English the more I mess up :D
Cristian
Cristian Marussi
reviewerPATCH v15 5/35 firmware: arm_scmi: Refactor __scmi_event_handler_get_ops helper
In reply to Cristian MarussiRefactor the __scmi_event_handler_get_ops notification helper to simplify its logic and make use of guards for locking.
Event handler allocation is split out of registration in a dedicated helper so as to allow for future reuse.
Mostly no functional change, in the sense that while the functionality remained the same, a richer set of errors is returned where before was always a -EINVAL.
---
v14 --> v15
- reworked error path in scmi_allocate_pending_event_handler
- improve commit message
--- drivers/firmware/arm_scmi/notify.c | 74 +++++++++++++++++++----------- 1 file changed, 47 insertions(+), 27 deletions(-)
drivers/firmware/arm_scmi/notify.c +47 -27
@@ -891,7 +891,6 @@ void scmi_deregister_protocol_events(const struct scmi_handle *handle,/** * scmi_allocate_event_handler() - Allocate Event handler -* @ni: A reference to the notification instance to use * @evt_key: 32bit key uniquely bind to the event identified by the tuple * (proto_id, evt_id, src_id) *
@@ -901,21 +900,33 @@ void scmi_deregister_protocol_events(const struct scmi_handle *handle,* associated to this handler descriptor (hndl->r_evt == NULL), so the handler * is initialized as pending. * -* Context: Assumes to be called with @pending_mtx already acquired. * Return: the freshly allocated structure on Success */ -static struct scmi_event_handler * -scmi_allocate_event_handler (struct scmi_notify_instance *ni,u32 evt_key) +static struct scmi_event_handler *scmi_allocate_event_handler (u32 evt_key) {struct scmi_event_handler *hndl; hndl =kzalloc_obj (*hndl);if (!hndl)return NULL ; + hndl->key = evt_key;BLOCKING_INIT_NOTIFIER_HEAD (&hndl->chain);refcount_set (&hndl->users,1 ); -/* New handlers are created pending */ + +return hndl; +} + +static struct scmi_event_handler * +scmi_allocate_pending_event_handler (struct scmi_notify_instance *ni,u32 evt_key) +{ +struct scmi_event_handler *hndl; + +lockdep_assert_held (&ni->pending_mtx); + hndl =scmi_allocate_event_handler (evt_key); +if (!hndl) +return NULL ; +hash_add (ni->pending_events_handlers, &hndl->hash, hndl->key);return hndl;
@@ -1085,7 +1096,8 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni, u32 evt_key, bool create) {struct scmi_registered_event *r_evt; -struct scmi_event_handler *hndl =NULL ; +struct scmi_event_handler *hndl; +int ret; r_evt =SCMI_GET_REVT (ni,KEY_XTRACT_PROTO_ID (evt_key),KEY_XTRACT_EVT_ID (evt_key));
@@ -1093,39 +1105,47 @@ __scmi_event_handler_get_ops(struct scmi_notify_instance *ni,if (r_evt && r_evt->not_supported_by_platform)return ERR_PTR (-EOPNOTSUPP ); -mutex_lock (&ni->pending_mtx); +guard (mutex)(&ni->pending_mtx);/* Search registered events at first ... if possible at all */ if (r_evt) { -mutex_lock (&r_evt->proto->registered_mtx); +guard (mutex)(&r_evt->proto->registered_mtx); hndl =KEY_FIND (r_evt->proto->registered_events_handlers, hndl, evt_key); -if (hndl) +if (hndl) {refcount_inc (&hndl->users); -mutex_unlock (&r_evt->proto->registered_mtx); +return hndl; + } }/* ...then amongst pending. */ -if (!hndl) { - hndl =KEY_FIND (ni->pending_events_handlers, hndl, evt_key); -if (hndl) -refcount_inc (&hndl->users); + hndl =KEY_FIND (ni->pending_events_handlers, hndl, evt_key); +if (hndl) { +refcount_inc (&hndl->users); +return hndl; } -/* Create if still not found and required */ -if (!hndl && create) { - hndl =scmi_allocate_event_handler (ni, evt_key); -if (hndl &&scmi_register_event_handler (ni, hndl)) { -dev_dbg (ni->handle->dev, -"purging UNKNOWN handler - key:%X\n" , - hndl->key); -/* this hndl can be only a pending one */ -scmi_put_handler_unlocked (ni, hndl); - hndl =ERR_PTR (-EINVAL ); - } +if (!create) +return ERR_PTR (-ENODEV ); + +/* +* Create if still not found and required, new handlers are +* created pending and transitioned to registered when bound to +* an event. +*/ + hndl =scmi_allocate_pending_event_handler (ni, evt_key); +if (!hndl) +return ERR_PTR (-ENOMEM ); + + ret =scmi_register_event_handler (ni, hndl); +if (ret) { +dev_dbg (ni->handle->dev, +"purging UNKNOWN handler - key:%X\n" , hndl->key); +/* this hndl can be only a pending one */ +scmi_put_handler_unlocked (ni, hndl); +return ERR_PTR (ret); } -mutex_unlock (&ni->pending_mtx); -return hndl ?:ERR_PTR (-ENODEV ); +return hndl; } static struct scmi_event_handler *
Signature · 1 line
2.54.0
Cristian Marussi
reviewerPATCH v15 6/35 firmware: arm_scmi: Introduce protocol instance notifiers
In reply to Cristian MarussiSCMI Protocol notifications are typically used by SCMI drivers to detect and react to particular conditions: this was the assumption and the classic usage scenario upon which the SCMI notification framework was built.
Some protocols like Telemetry, though, could be interested in enabling and registering for such notifications: in such a scenario each initialized protocol instance could want to register a common protocol notifier.
In order to reuse the existing SCMI Notifications machinery, allow protocols themselves to register for their own notifications and provide their own notifier callbacks.
Each protocol, at the end of its initialization phase, can now provide one unique per-protocol-instance notifier block, which will be registered, by the core SCMI stack, on the related notification chain: such notifier will be automatically removed during the protocol de-initialization phase.
---
v13 --> v14
- reworked the helpers used to register protocol notifiers so as to possibly worked during the protocol initialization phase when locks are enabled
v12 --> v13
- refactored a few IFs
v10 -->v11
- added a few dev_dbg
v8 --> v9
- Added membarrier comment
v7 --> v8
- Reworked logic around protocol notifier registration to address possible races as pointed out by Sashiko
- Reviewed commit message
v5 -->v6
- move protocol notifier registration out of proto_init
v2-->v3
- split out unrelated changes on event sizing
v1-->v2
- Fixed multiline comment format
--- drivers/firmware/arm_scmi/common.h | 24 ++++ drivers/firmware/arm_scmi/driver.c | 115 +++++++++++++++---- drivers/firmware/arm_scmi/notify.c | 154 ++++++++++++++++++++++++++ drivers/firmware/arm_scmi/protocols.h | 10 ++ 4 files changed, 282 insertions(+), 21 deletions(-)
drivers/firmware/arm_scmi/common.h +24 -0
@@ -19,6 +19,7 @@#include <linux/list.h> #include <linux/module.h> #include <linux/property.h> +#include <linux/notifier.h> #include <linux/refcount.h> #include <linux/scmi_protocol.h> #include <linux/spinlock.h>
@@ -122,7 +123,9 @@ struct scmi_device *scmi_device_create(struct fwnode_handle *fwnode,void scmi_device_destroy (struct device *parent,int protocol,const char *name);int scmi_protocol_acquire (const struct scmi_handle *handle,u8 protocol_id); +int scmi_protocol_acquire_unlocked (const struct scmi_handle *handle,u8 protocol_id);void scmi_protocol_release (const struct scmi_handle *handle,u8 protocol_id); +void scmi_protocol_release_unlocked (const struct scmi_handle *handle,u8 protocol_id);/* SCMI Transport */ /**
@@ -693,8 +696,29 @@ static struct platform_driver __drv = { \#define DEFINE_SCMI_ACPI_TRANSPORT_DRIVER (__tag, __drv, __desc, __match, __core_ops)\ __DEFINE_SCMI_TRANSPORT_DRIVER(__tag, __drv, __desc, NULL, __match, __core_ops) +struct scmi_protocol_notifier { +u8 proto_id; +u8 evt_id; +const u32 *src_id; +struct notifier_block *nb; +bool registered; +}; +void scmi_notification_instance_data_set (const struct scmi_handle *handle,void *priv);void *scmi_notification_instance_data_get (const struct scmi_handle *handle); + +int scmi_protocol_notifier_register (const struct scmi_handle *handle, +struct scmi_protocol_notifier *pno); +int scmi_protocol_notifier_unregister (const struct scmi_handle *handle, +struct scmi_protocol_notifier *pno); + +static inline bool +scmi_protocol_notifier_registered (struct scmi_protocol_notifier *pno) +{ +/* Ensure registered is visible */ +return smp_load_acquire (&pno->registered); +} +int scmi_inflight_count (const struct scmi_handle *handle);#endif /* _SCMI_COMMON_H */
drivers/firmware/arm_scmi/driver.c +94 -21
178 lines not on this page · raw message
drivers/firmware/arm_scmi/notify.c +154 -0
172 lines not on this page · raw message
drivers/firmware/arm_scmi/protocols.h +10 -0
@@ -175,6 +175,13 @@ struct scmi_proto_helpers_ops;* can be used by the protocol implementation to generate SCMI messages. * @set_priv: A method to set protocol private data for this instance. * @get_priv: A method to get protocol private data previously set. +* @instance_notifier_register: A method to register interest for specific +* notifications from within a protocol +* implementation unit: ONLY one instance notifier +* can be registered per-protocol. +* The related notifier block will be registered +* right after the protocol is initialized and +* automatically removed on protocol release. * * This structure represents a protocol initialized against specific SCMI * instance and it will be used as follows:
@@ -194,6 +201,9 @@ struct scmi_protocol_handle {const struct scmi_proto_helpers_ops *hops;int (*set_priv)(const struct scmi_protocol_handle *ph,void *priv);void *(*get_priv)(const struct scmi_protocol_handle *ph); +int (*instance_notifier_register)(const struct scmi_protocol_handle *ph, +u8 evt_id,const u32 *src_id, +struct notifier_block *nb); };/**
Signature · 1 line
2.54.0
Re: [PATCH v15 06/35] firmware: arm_scmi: Introduce protocol instance notifiers
Quoted 12 lines · On Mon, 9 Nov 2026 09:11:52 +0000 Cristian Marussi <[email protected]>…
SCMI Protocol notifications are typically used by SCMI drivers to detect and react to particular conditions: this was the assumption and the classic usage scenario upon which the SCMI notification framework was built.
Some protocols like Telemetry, though, could be interested in enabling and registering for such notifications: in such a scenario each initialized protocol instance could want to register a common protocol notifier.
In order to reuse the existing SCMI Notifications machinery, allow protocols themselves to register for their own notifications and provide their own notifier callbacks.
Quoted 6 lines
Each protocol, at the end of its initialization phase, can now provide one unique per-protocol-instance notifier block, which will be registered, by the core SCMI stack, on the related notification chain: such notifier will be automatically removed during the protocol de-initialization phase.
Signed-off-by Cristian Marussi <[email protected]>
Hi Cristian,
Perhaps it is more a reflection of Monday afternoon than anything else, but this took a lot more work to understand than I'd expect for something so conceptually simple.
The recursive nature of the unwind code seems overly complex given it only ever recurses once (so the gain is minor). To my eyes that just makes it harder to reason about for minimal advantage in amount of code.
A suggestion inline that perhaps a bit of refactoring will allow a simpler linear flow.
Quoted 46 lines
---
v13 --> v14
- reworked the helpers used to register protocol notifiers so as to possibly worked during the protocol initialization phase when locks are enabled
v12 --> v13
- refactored a few IFs
v10 -->v11
- added a fewdev_dbg
v8 --> v9
- Added membarrier comment
v7 --> v8
- Reworked logic around protocol notifier registration to address possible races as pointed out by Sashiko
- Reviewed commit message
v5 -->v6
- move protocol notifier registration out ofproto_init
v2-->v3
- split out unrelated changes on event sizing
v1-->v2
- Fixed multiline comment format--- drivers/firmware/arm_scmi/common.h | 24 ++++ drivers/firmware/arm_scmi/driver.c | 115 +++++++++++++++---- drivers/firmware/arm_scmi/notify.c | 154 ++++++++++++++++++++++++++ drivers/firmware/arm_scmi/protocols.h | 10 ++ 4 files changed, 282 insertions(+), 21 deletions(-)static const struct
scmi_xfer_opsxfer_ops= {drivers/firmware/arm_scmi/driver.c +1
-1.xfer_get_init = xfer_get_init, .reset_rx_to_maxsz = reset_rx_to_maxsz,@@ -2297,6 +2318,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info, pi->ph.hops = &helpers_ops; pi->ph.set_priv = scmi_set_protocol_priv; pi->ph.get_priv = scmi_get_protocol_priv; + pi->ph.instance_notifier_register = scmi_register_instance_notifier;refcount_set (&pi->users,1 );/* @@ -2358,12 +2380,12 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,static struct scmi_protocol_instance *__must_check scmi_get_protocol_instance (const struct scmi_handle *handle,u8 protocol_id) { -struct scmi_protocol_instance *pi;
drivers/firmware/arm_scmi/driver.c scmi_alloc_init_protocol_instance(struct scmi_info *info, · line 2390
Quoted 10 lines
drivers/firmware/arm_scmi/driver.c +3
-2struct scmi_info *info =handle_to_scmi_info (handle); +struct scmi_protocol_instance *pi; -mutex_lock (&info->protocols_mtx); - pi =idr_find (&info->protocols, protocol_id); +guard (mutex)(&info->protocols_mtx); + pi =idr_find (&info->protocols, protocol_id);if (pi) {refcount_inc (&pi->users);
return pi; }
Then you can drop the else.
Maybe this gets more complex in later patches but looking just at this, it seems worth doing that to reduce the indent of what follows.
Quoted 181 lines
drivers/firmware/arm_scmi/driver.c +54
-16}else {@@ -2371,16 +2393,44 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)/* Fails if protocol not registered on bus */ proto =scmi_protocol_get (protocol_id, &info->version); -if (proto) - pi =scmi_alloc_init_protocol_instance (info, proto); -else - pi =ERR_PTR (-EPROBE_DEFER ); +if (!proto) +return ERR_PTR (-EPROBE_DEFER ); + + pi =scmi_alloc_init_protocol_instance (info, proto); +if (IS_ERR (pi)) +return pi; + +/* +* A unique protocol notifier, when present, is registered +* after the protocol itself and all of its supported events +* have been successfully initialized. +*/ +if (smp_load_acquire (&pi->pno.nb) && +scmi_protocol_notifier_register (pi->handle, &pi->pno)) +dev_warn (handle->dev, +"Failed to register protocol [0x%X] notifier\n" , + proto->id); } -mutex_unlock (&info->protocols_mtx);return pi; } +int scmi_protocol_acquire_unlocked (const struct scmi_handle *handle,u8 protocol_id) +{ +struct scmi_info *info =handle_to_scmi_info (handle); +struct scmi_protocol_instance *pi; + +lockdep_assert_held (&info->protocols_mtx); + + pi =idr_find (&info->protocols, protocol_id); +if (WARN_ON (!pi)) +return -ENODEV ; + +refcount_inc (&pi->users); + +return 0 ; +} +/** * scmi_protocol_acquire - Protocol acquire * @handle: A reference to the SCMI platform instance. @@ -2396,23 +2446,32 @@ int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id)return PTR_ERR_OR_ZERO (scmi_get_protocol_instance (handle, protocol_id)); } -/** -* scmi_protocol_release - Protocol de-initialization helper. -* @handle: A reference to the SCMI platform instance. -* @protocol_id: The protocol being requested. -* -* Remove one user for the specified protocol and triggers de-initialization -* and resources de-allocation once the last user has gone. -*/ -void scmi_protocol_release (const struct scmi_handle *handle,u8 protocol_id) +void scmi_protocol_release_unlocked (const struct scmi_handle *handle, +u8 protocol_id) {struct scmi_info *info =handle_to_scmi_info (handle);struct scmi_protocol_instance *pi; -mutex_lock (&info->protocols_mtx); +lockdep_assert_held (&info->protocols_mtx); + pi =idr_find (&info->protocols, protocol_id);if (WARN_ON (!pi)) - goto out; +return ; + +/* +* If a protocol notifier was registered and this is the last instance +* releasing the protocol, un-register the notifier: note that the +* notifier itself counts as one user, as for any other regular notifier, +* so if a protocol notifier was registered and there are only 2 users +* active we can derive that this is the last protocol instance +* de-registering. +*/ +if (scmi_protocol_notifier_registered (&pi->pno) && +refcount_read (&pi->users) ==2 && +scmi_protocol_notifier_unregister (pi->handle, &pi->pno)) +dev_err (handle->dev, +"Failed to release protocol [0x%X] notifier\n" , + protocol_id);if (refcount_dec_and_test (&pi->users)) {void *gid = pi->gid;@@ -2431,9 +2490,23 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)dev_dbg (handle->dev,"De-Initialized protocol: 0x%X\n" , protocol_id); } +}drivers/firmware/arm_scmi/notify.c +62
-0@@ -1061,6 +1061,44 @@ static int scmi_register_event_handler(struct scmi_notify_instance *ni,return ret; +/** * __scmi_event_handler_get_ops() - Utility to get or create an event handler * @ni: A reference to the notification instance to use @@ -1438,6 +1476,67 @@ static int scmi_notifier_register(const struct scmi_handle *handle,return ret; } +/** +* scmi_protocol_notifier_register - Register the protocol notifier +* @handle: The handle identifying the platform instance against which the +* callback is registered +* @pno: The protocol notifier descriptor +* +* A protocol can optionally register a single protocol notifier callback +* during its initialization phase: the notifier_block will be the registered +* using the usual common SCMI notification framework. +* +* This custom wrapper function is used to register such a notifier since, by +* construction, when a protocol notifier is registered: +* - it is the first notifier to be registered +* - the related protocol and its events have been already initialized so this +* notifier will never need to transition from the pending events list +* +* Return: 0 on Success +*/ +int scmi_protocol_notifier_register (const struct scmi_handle *handle, +struct scmi_protocol_notifier *pno) +{ +struct scmi_event_handler *hndl; +struct scmi_notify_instance *ni; +u32 evt_key; +int ret; + + ni =scmi_notification_instance_data_get (handle); +if (!ni) +return -ENODEV ; + +/* Ensure registered was visible */ +if (smp_load_acquire (&pno->registered)) +return -EBUSY ; + + evt_key =MAKE_HASH_KEY (pno->proto_id, pno->evt_id, + pno->src_id ? *pno->src_id :SRC_ID_MASK ); + +/* This protocol handler notifier cannot exist already at this point */ + hndl =scmi_create_registered_handler (ni, evt_key); +if (IS_ERR (hndl)) +return PTR_ERR (hndl); + +blocking_notifier_chain_register (&hndl->chain, pno->nb); + ret =scmi_event_handler_enable_events (hndl); +if (ret) { +/* When enable fails throw the handler away */ +scmi_put_handler_unlocked (ni, hndl); +/* Drop protocol refcount acquired after creation */ +scmi_protocol_release_unlocked (handle, pno->proto_id); +return ret; + } + +/* Make sure registration is visible */ +smp_store_release (&pno->registered, true); + +dev_dbg (handle->dev, +"Registered SCMI protocol [0x%X] notifier\n" , pno->proto_id); + +return 0 ; +} +/** * scmi_notifier_unregister() - Unregister a notifier_block for an event * @handle: The handle identifying the platform instance against which the
drivers/firmware/arm_scmi/notify.c static int scmi_notifier_unregister(const struct scmi_handle *handle, · line 1601
Quoted 8 lines
drivers/firmware/arm_scmi/notify.c +5
-0@@ -1495,6 +1594,61 @@ static int scmi_notifier_unregister(const struct scmi_handle *handle,return 0 ; } +int scmi_protocol_notifier_unregister (const struct scmi_handle *handle, +struct scmi_protocol_notifier *pno) +{ +struct scmi_registered_event *r_evt; +struct scmi_event_handler *hndl;
It is a little confusing to have handle and hndl. Perhaps name this one ev_handler or something like that the difference is more obvious?
Quoted 16 lines
drivers/firmware/arm_scmi/notify.c +16
-0+struct scmi_notify_instance *ni; +u32 evt_key; +bool freed; + + ni =scmi_notification_instance_data_get (handle); +if (!ni) +return -ENODEV ; + + evt_key =MAKE_HASH_KEY (pno->proto_id, pno->evt_id, + pno->src_id ? *pno->src_id :SRC_ID_MASK ); + + r_evt =SCMI_GET_REVT (ni,KEY_XTRACT_PROTO_ID (evt_key), +KEY_XTRACT_EVT_ID (evt_key)); + +if (WARN_ON (!r_evt)) +return -ENODEV ;
drivers/firmware/arm_scmi/notify.c static int scmi_notifier_unregister(const struct scmi_handle *handle, · line 1627
Quoted 10 lines
drivers/firmware/arm_scmi/notify.c +10
-0+ +/* Make sure de-registration is visible BEFORE calling unregister */ +smp_store_release (&pno->registered, false); + + scoped_guard(mutex, &r_evt->proto->registered_mtx) { + hndl =KEY_FIND (r_evt->proto->registered_events_handlers, + hndl, evt_key); +if (!hndl) { +/* Visibly revert registered state on failure */ +smp_store_release (&pno->registered, true);
I assume any race in the window where it doesn't look registered doesn't matter because there should be no path in which the key isn't found? Perhaps add something to say that.
Quoted 7 lines
drivers/firmware/arm_scmi/notify.c +7
-0+return -ENOMEM ; + } + +blocking_notifier_chain_unregister (&hndl->chain, pno->nb); + + freed =scmi_put_handler_unlocked (ni, hndl); +/* This must cause the handler to be freed at this point */
drivers/firmware/arm_scmi/notify.c static int scmi_notifier_unregister(const struct scmi_handle *handle, · line 1644
Quoted 10 lines
drivers/firmware/arm_scmi/notify.c +10
-0+WARN_ON (!freed); + } + +/* +* Releasing the protocol for this notifier only here AFTER unlocking +* registered_mtx, since releasing the protocol itself can trigger its +* de-initialization (ie. including r_evt and registered_mtx) +*/ +if (freed) +scmi_protocol_release_unlocked (handle, pno->proto_id);
This recursion makes this code hard to follow.
I'm not 100% sure, but feels like you could add a tiny bit more code into scm_protocol_release() to first check if there is a self registered notifier and drop the recursive bit.
Something like the following.
void scmi_protocol_do_release(struct scmi_info *info, struct scmi_protocol_instance *pi)
{
void *gid = pi->gid;
if (pi->proto->events)
scmi_deregister_protocol_events(handle, protocol_id);
if (pi->proto->instance_deinit)
pi->proto->instance_deinit(&pi->ph);
idr_remove(&info->protocols, protocol_id);
scmi_protocol_put(pi->proto);
devres_release_group(handle->dev, gid);
dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n", protocol_id);
}int scmi_protocol_notifier_unregister(const struct scmi_handle *handle,
struct scmi_protocol_notifier *pno)
{
struct scmi_registered_event *r_evt;
struct scmi_event_handler *hndl;
struct scmi_notify_instance *ni;
u32 evt_key;
bool freed;
ni = scmi_notification_instance_data_get(handle);
if (!ni)
return -ENODEV;
evt_key = MAKE_HASH_KEY(pno->proto_id, pno->evt_id,
pno->src_id ? *pno->src_id : SRC_ID_MASK);
r_evt = SCMI_GET_REVT(ni, KEY_XTRACT_PROTO_ID(evt_key),
KEY_XTRACT_EVT_ID(evt_key));
if (WARN_ON(!r_evt))
return -ENODEV;
/* Make sure de-registration is visible BEFORE calling unregister */
smp_store_release(&pno->registered, false);
scoped_guard(mutex, &r_evt->proto->registered_mtx) {
hndl = KEY_FIND(r_evt->proto->registered_events_handlers,
hndl, evt_key);
if (!hndl) {
/* Visibly revert registered state on failure */
smp_store_release(&pno->registered, true);
return -ENOMEM;
}
blocking_notifier_chain_unregister(&hndl->chain, pno->nb);
freed = scmi_put_handler_unlocked(ni, hndl);
/* This must cause the handler to be freed at this point */
WARN_ON(!freed);
}
/*
* Releasing the protocol for this notifier only here AFTER unlocking
* registered_mtx, since releasing the protocol itself can trigger its
* de-initialization (ie. including r_evt and registered_mtx)
*/// if (freed)
// scmi_protocol_release_unlocked(handle, pno->proto_id);
dev_dbg(handle->dev, "Unregistered SCMI protocol [0x%X] notifier\n", pno->proto_id); return 0; }
void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
{
struct scmi_info *info = handle_to_scmi_info(handle);
struct scmi_protocol_instance *pi;
guard(mutex)(&info->protocols_mtx);
pi = idr_find(&info->protocols, protocol_id);
if (WARN_ON(!pi))
return;
// Simple case, it was the last one - which inherently means no
// self notifier bit.
if (refcount_dec_and_test(&pi->users)) {
scmi_protocol_do_release(info, pi);
return;
}
// The self notifier bit.
if (scmi_protocol_notifier_registered(&pi->pno) &&
refcount_read(&pi->users) == 1 &&
refcount_dec_and_test(&pi->users)) {
//last bit can be an unconditional dec.
int ret = scmi_protocol_notifier_unregister(pi->handle, &pi->pn);
if (ret)
dev_err();
scmi_protocol_do_release(info, pi);
}
}Quoted 6 lines
drivers/firmware/arm_scmi/notify.c +6
-0+ +dev_dbg (handle->dev, +"Unregistered SCMI protocol [0x%X] notifier\n" , pno->proto_id); + +return 0 ; +}
Cristian Marussi
reviewerPATCH v15 7/35 dt-bindings: firmware: arm,scmi: Add support for telemetry protocol
In reply to Cristian MarussiAdd new SCMI v4.0 Telemetry protocol bindings definitions.
---
v3 --> v4
- changed protocol number to lowercase 1b
- fixed misplaced block for protocol 0x1b
--- Documentation/devicetree/bindings/firmware/arm,scmi.yaml | 8 ++++++++ 1 file changed, 8 insertions(+)
Documentation/devicetree/bindings/firmware/arm,scmi.yaml +8 -0
@@ -324,6 +324,14 @@ properties: required: - reg + protocol@1b: + $ref: '#/$defs/protocol-node' + unevaluatedProperties: false + + properties: + reg: + const: 0x1b + unevaluatedProperties: false $defs:
Signature · 1 line
2.54.0
Cristian Marussi
reviewerPATCH v15 8/35 include: trace: Add Telemetry trace events
In reply to Cristian MarussiAdd custom traces to report Telemetry accesses and collection events.
---
v12 --> v13
- better commit message
- use const char where required
v7 --> v8
- use proper FTRACE helpers for variable strings
--- include/trace/events/scmi.h | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+)
include/trace/events/scmi.h +45 -0
@@ -178,6 +178,51 @@ TRACE_EVENT(scmi_msg_dump,__get_str (tag), __entry->msg_id, __entry->seq, __entry->status, __print_hex_str(__get_dynamic_array(cmd), __entry->len)) ); + +TRACE_EVENT (scmi_tlm_access, + TP_PROTO(u64 de_id, const char *tag, u64 startm, u64 endm), +TP_ARGS (de_id, tag, startm, endm), + +TP_STRUCT__entry ( + __field(u64, de_id) + __string(tag, tag) + __field(u64, startm) +__field (u64, endm) + ), + +TP_fast_assign ( + __entry->de_id = de_id; + __assign_str(tag); + __entry->startm = startm; + __entry->endm = endm; + ), + +TP_printk ("[%s] de_id=0x%llX - startm=%016llX endm=%016llX" , +__get_str (tag), __entry->de_id, __entry->startm, __entry->endm) +); + +TRACE_EVENT (scmi_tlm_collect, + TP_PROTO(u64 ts, u64 de_id, u64 value, const char *tag), +TP_ARGS (ts, de_id, value, tag), + +TP_STRUCT__entry ( + __field(u64, ts) + __field(u64, de_id) + __field(u64, value) +__string (tag, tag) + ), + +TP_fast_assign ( + __entry->ts = ts; + __entry->de_id = de_id; + __entry->value = value; + __assign_str(tag); + ), + +TP_printk ("[%s] ts=%llu de_id=0x%04llX value=%016llu" , +__get_str (tag), __entry->ts, __entry->de_id, __entry->value) +); +#endif /* _TRACE_SCMI_H */ /* This part must be outside protection */
Signature · 1 line
2.54.0
Cristian Marussi
reviewerPATCH v15 9/35 firmware: arm_scmi: Add Telemetry basic attributes enumeration
In reply to Cristian MarussiAdd SCMIv4.0 Telemetry basic support for minimal initialization and protocol attributes enumeration: expose telemetry instance descriptor via a simple information retrieval protocol operation.
---
v14 --> v15
- add include uuid.h into scmi_protocol.h
- dropped needless __must_check
v13 --> v14
- split original basic Telemetry patch, reducing this patch to the basics of protocol attributes enumeration only
- fix group unroll loop on groups_init failure
- remove goto from de_desc_parse
- renamed some fields to more strictly adhere to spec nomenclature
v12 --> v13
- headers includes re-order
- rework cleanup path for instance allocations
- relocate tde->mtx guards higher up and add lockdep assertions
- add more _RAW macros
- rename DE_CONFIGURE defines
- use *alloc_objs
- remove TO_CPU64 and useless READ_ONCEs
v11 --> v12
- dropped counted-by in smhtis
- reworked barriers around res_get
- added smp_store_release on TDE registration
v10 -->v11
- relocated a few parsing macros from another patch
- cleanup exit path on resource enumeration
- remove 'naked' ti->rinfo accesses
- walk xa_des to free DEs on removal
v8 --> v9
- Fixes for some Sashiko issues on v8
- use counted_by_lle where appropriate
- consider header in boudanry check in DE_DESCRIPTION iterators
- bail out if SHMTI areas are NOT 32bit aligned
- warn if SHMTI areas are NOT 64bit aligned
- convert SCMI_TDE_VAR_SZ macro to an inline function
- make some enum definition explicit
v7 --> v8
- Fixes for some Sashiko issues on v7
- add proper msg boundary parsing checks on DE_DESCRIPTION multipart
- add proper cleanup/rollback on groups_init err_path
- update num_des_tstamp ONLY after a DE is successfully initialized
- proper cleanup tde during tde_put (preserving info/mutex bits)
- add membarriers in enumerate resources
- add a check and a warn on SHMTI phys_addr alignment
- reword a few dev_errs
- use tde_free_put on resoucers_free
v6 --> v7
- use uuid_t for proper Telemetry UUID DE_IMPL_VERSION endianity
v5 --> v6
- define uuid_line dwords as BE
- fixed Sashiko reviews where applicable:
https://sashiko.dev/#/message/20260703123601.381275-8-cristian.marussi%40arm.com
- fix missing le32 and bound check in variable payload parsing
- fix TDE duplicatin logic
- fix intervals cleanup - fixes also KMEMLEAK report
- implement proper freeinfg for group resources
- proper SHMTI iterations
- removed useless/stale static in scmi_telemetry_tde_get()
v4 --> v5
- fix de->grp_id initialization for DEs not belonging to a group
- removed UAPI structures dependency
- expose discovered SHMTIs areas in scmi_protocol.h
- fixes sparse warnings
v3 -->v4
- bail-out on FW_BUG errors
- count timestamp capable DE to track enables
v2 --> v3
- split from monolithic Telemetry patch
- fix checkpatch macros complaints
- fix ACCESS_PRIVATE usage
- add a few comments on allocation/enumeration lifetime
- use interval.num_intervals
- removed needless cleanup handler usage
- simply return from scmi_telemetry_de_lookup()
- fixed composing_des name length to 08X
--- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/driver.c | 2 + drivers/firmware/arm_scmi/protocols.h | 1 + drivers/firmware/arm_scmi/telemetry.c | 149 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 33 +++++- 5 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 drivers/firmware/arm_scmi/telemetry.c
drivers/firmware/arm_scmi/Makefile +1 -1
@@ -8,7 +8,7 @@ scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o scmi-protocols-y := base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o -scmi-protocols-y += pinctrl.o +scmi-protocols-y += pinctrl.o telemetry.o scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y) obj-$(CONFIG_ARM_SCMI_PROTOCOL) += transports/
drivers/firmware/arm_scmi/driver.c +2 -0
@@ -3716,6 +3716,7 @@ static int __init scmi_driver_init(void)scmi_system_register ();scmi_powercap_register ();scmi_pinctrl_register (); +scmi_telemetry_register ();return platform_driver_register (&scmi_driver); }
@@ -3734,6 +3735,7 @@ static void __exit scmi_driver_exit(void)scmi_system_unregister ();scmi_powercap_unregister ();scmi_pinctrl_unregister (); +scmi_telemetry_unregister ();platform_driver_unregister (&scmi_driver);
drivers/firmware/arm_scmi/protocols.h +1 -0
@@ -410,5 +410,6 @@ DECLARE_SCMI_REGISTER_UNREGISTER(sensors);DECLARE_SCMI_REGISTER_UNREGISTER (voltage);DECLARE_SCMI_REGISTER_UNREGISTER (system);DECLARE_SCMI_REGISTER_UNREGISTER (powercap); +DECLARE_SCMI_REGISTER_UNREGISTER (telemetry);#endif /* _SCMI_PROTOCOLS_H */
drivers/firmware/arm_scmi/telemetry.c new file +149 -0
149 lines not on this page · raw message
include/linux/scmi_protocol.h +32 -1
@@ -2,17 +2,19 @@/* * SCMI Message Protocol driver header * -* Copyright (C) 2018-2021 ARM Ltd. +* Copyright (C) 2018-2026 ARM Ltd. */ #ifndef _LINUX_SCMI_PROTOCOL_H#define _LINUX_SCMI_PROTOCOL_H#include <linux/bitfield.h> +#include <linux/bitops.h> #include <linux/device-id/scmi.h> #include <linux/device.h> #include <linux/notifier.h> #include <linux/types.h> +#include <linux/uuid.h> #define SCMI_MAX_STR_SIZE 64#define SCMI_SHORT_NAME_MAX_SIZE 16
@@ -828,6 +830,35 @@ struct scmi_pinctrl_proto_ops {int (*pin_free)(const struct scmi_protocol_handle *ph,u32 pin); }; +struct scmi_telemetry_base_info { +unsigned int version; +uuid_t primary_revision; +unsigned int num_des; +unsigned int num_groups; +unsigned int num_intervals; +unsigned int num_shmtis; +}; + +struct scmi_telemetry_info { +bool single_read_support; +bool continuous_update_support; +bool per_group_config_support; +bool reset_support; +bool fc_support; +struct scmi_telemetry_base_info base; +}; + +/** +* struct scmi_telemetry_proto_ops - represents the various operations provided +* by SCMI Telemetry Protocol +* +* @info_get: get the general Telemetry information. +*/ +struct scmi_telemetry_proto_ops { +const struct scmi_telemetry_info *(*info_get) + (const struct scmi_protocol_handle *ph); +}; +/** * struct scmi_notify_ops - represents notifications' operations provided by * SCMI core
Signature · 1 line
2.54.0
Re: [PATCH v15 09/35] firmware: arm_scmi: Add Telemetry basic attributes enumeration
Quoted 5 lines · On Mon, 9 Nov 2026 09:11:55 +0000 Cristian Marussi <[email protected]>…
Add SCMIv4.0 Telemetry basic support for minimal initialization and protocol attributes enumeration: expose telemetry instance descriptor via a simple information retrieval protocol operation.
Signed-off-by Cristian Marussi <[email protected]>
A few minor things inline.
Thanks,
Jonathan
Quoted 17 lines
drivers/firmware/arm_scmi/protocols.h +1
-0@@ -410,5 +410,6 @@ DECLARE_SCMI_REGISTER_UNREGISTER(sensors);DECLARE_SCMI_REGISTER_UNREGISTER (voltage);DECLARE_SCMI_REGISTER_UNREGISTER (system);DECLARE_SCMI_REGISTER_UNREGISTER (powercap); +DECLARE_SCMI_REGISTER_UNREGISTER (telemetry);#endif /* _SCMI_PROTOCOLS_H */ drivers/firmware/arm_scmi/telemetry.c new file +0
-0
drivers/firmware/arm_scmi/telemetry.c line 8
Quoted 8 lines
drivers/firmware/arm_scmi/telemetry.c new file +8
-0@@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* System Control and Management Interface (SCMI) Telemetry Protocol +* +* Copyright (C) 2026 ARM Ltd. +*/ + +#include <linux/atomic.h>
I'm not immediately spotting this being used. Perhaps take another look at whether some of these should wait for later patches where their usecases come in.
Quoted 24 lines
drivers/firmware/arm_scmi/telemetry.c new file +24
-0+#include <linux/bitfield.h> +#include <linux/compiler_types.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/sprintf.h> +#include <linux/string.h> +#include <linux/uuid.h> + +#include "protocols.h" +#include "notify.h" + +/* Updated only after ALL the mandatory features for that version are merged */ +#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 + +#define SCMI_TLM_DE_IMPL_NUM_DWORDS 4 + +/* An enum listing the protocol specific commands definitions */ +enum scmi_telemetry_protocol_cmd { +TELEMETRY_LIST_SHMTI =0x3 , +TELEMETRY_DE_DESCRIPTION =0x4 ,
drivers/firmware/arm_scmi/telemetry.c line 42
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c new file +10
-0+TELEMETRY_LIST_UPDATE_INTERVALS =0x5 , +TELEMETRY_DE_CONFIGURE =0x6 , +TELEMETRY_DE_ENABLED_LIST =0x7 , +TELEMETRY_CONFIG_SET =0x8 , +TELEMETRY_READING_COMPLETE =TELEMETRY_CONFIG_SET , +TELEMETRY_CONFIG_GET =0x9 , + TELEMETRY_RESET = 0xA, +}; + +struct scmi_msg_resp_telemetry_protocol_attributes {
I like clear names as much as anyone, but probably wouldn't lose much by abbreviating a little?
struct scmi_msg_rsp_telemetry_prot_attr
perhaps?
drivers/firmware/arm_scmi/telemetry.c line 51
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c new file +9
-0+__le32 de_num; +__le32 groups_num; +__le32 de_implem_rev_dword[SCMI_TLM_DE_IMPL_NUM_DWORDS ]; +__le32 attributes_1; +#define SUPPORTS_SINGLE_READ (x) (le32_get_bits((x), BIT(31))) +#define SUPPORTS_CONTINUOS_UPDATE (x) (le32_get_bits((x), BIT(30))) +#define SUPPORTS_PER_GROUP_CONFIG (x) (le32_get_bits((x), BIT(18))) +#define SUPPORTS_RESET (x) (le32_get_bits((x), BIT(17))) +#define SUPPORTS_FC (x) (le32_get_bits((x), BIT(16)))
I guess a local style thing but my normal inclination would be not hide what is going on here and instead define
ATTRS1_SUPPORTS_FC BIT(16)
and use the le32_get_bits() inline. These helpers have lost the connection with attributes_1.
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c new file +6
-0+__le32 default_blk_ts_rate; +}; + +struct telemetry_info { +bool streaming_mode; +unsigned int num_shmti;
drivers/firmware/arm_scmi/telemetry.c line 67
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c new file +10
-0+unsigned int num_des_tstamp; +unsigned int default_blk_ts_rate; +const struct scmi_protocol_handle *ph; +struct scmi_telemetry_info info; +}; + +static int +scmi_telemetry_protocol_attributes_get (struct telemetry_info *ti) +{ +struct scmi_msg_resp_telemetry_protocol_attributes *resp;
It feels like this is in the function scope rather than the if (!ret) mostly because it has a really long type name. See above.
drivers/firmware/arm_scmi/telemetry.c line 77
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c new file +10
-0+const struct scmi_protocol_handle *ph = ti->ph; +struct scmi_xfer *t; +int ret; + + ret = ph->xops->xfer_get_init (ph,PROTOCOL_ATTRIBUTES ,0 , +sizeof (*resp), &t); +if (ret) +return ret; + + resp = t->rx.buf;
I think this would ideally be in the if (!ret) as well which makes more sense if the declaration is in there.
drivers/firmware/arm_scmi/telemetry.c line 79
Quoted 2 lines
drivers/firmware/arm_scmi/telemetry.c new file +2
-0+ ret = ph->xops->do_xfer (ph, t); +if (!ret) {
Maybe given long lines and indent, this is worth a helper function.
Quoted 5 lines
drivers/firmware/arm_scmi/telemetry.c new file +5
-0+__le32 attr = resp->attributes_1; +__be32 de_impl_version[SCMI_TLM_DE_IMPL_NUM_DWORDS ]; + + ti->info.base.num_des =le32_to_cpu (resp->de_num); + ti->info.base.num_groups =le32_to_cpu (resp->groups_num);
drivers/firmware/arm_scmi/telemetry.c line 94
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c new file +10
-0+for (int i =0 ; i <SCMI_TLM_DE_IMPL_NUM_DWORDS ; i++) + de_impl_version[i] = +cpu_to_be32 (le32_to_cpu (resp->de_implem_rev_dword[i])); +import_uuid (&ti->info.base.primary_revision, (const __u8 *)&de_impl_version); + ti->info.single_read_support =SUPPORTS_SINGLE_READ (attr); + ti->info.continuous_update_support =SUPPORTS_CONTINUOS_UPDATE (attr); + ti->info.per_group_config_support =SUPPORTS_PER_GROUP_CONFIG (attr); + ti->info.reset_support =SUPPORTS_RESET (attr); + ti->info.fc_support =SUPPORTS_FC (attr); + ti->num_shmti =le32_get_bits (attr,GENMASK (15 ,0 ));
I think that mask belongs up with the structure definition as people will want to check the whole thing in one place.
Quoted 15 lines
drivers/firmware/arm_scmi/telemetry.c new file +7
-0+ ti->default_blk_ts_rate =le32_to_cpu (resp->default_blk_ts_rate); + } + + ph->xops->xfer_put (ph, t); + +return ret; +}include/linux/scmi_protocol.h +0
-0@@ -2,17 +2,19 @@/* * SCMI Message Protocol driver header
include/linux/scmi_protocol.h line 12
Quoted 10 lines
include/linux/scmi_protocol.h +2
-1* -* Copyright (C) 2018-2021 ARM Ltd. +* Copyright (C) 2018-2026 ARM Ltd. */ #ifndef _LINUX_SCMI_PROTOCOL_H#define _LINUX_SCMI_PROTOCOL_H#include <linux/bitfield.h> +#include <linux/bitops.h>
Not seeing use of that in this patch within the header. Perhaps it should be included in a later patch or in the c file?
Quoted 29 lines
include/linux/scmi_protocol.h +18
-0#include <linux/device-id/scmi.h> #include <linux/device.h> #include <linux/notifier.h> #include <linux/types.h> +#include <linux/uuid.h> #define SCMI_MAX_STR_SIZE 64#define SCMI_SHORT_NAME_MAX_SIZE 16@@ -828,6 +830,35 @@ struct scmi_pinctrl_proto_ops {int (*pin_free)(const struct scmi_protocol_handle *ph,u32 pin); }; +struct scmi_telemetry_base_info { +unsigned int version; +uuid_t primary_revision; +unsigned int num_des; +unsigned int num_groups; +unsigned int num_intervals; +unsigned int num_shmtis; +}; + +struct scmi_telemetry_info { +bool single_read_support; +bool continuous_update_support; +bool per_group_config_support; +bool reset_support; +bool fc_support; +struct scmi_telemetry_base_info base; +};
Cristian Marussi
reviewerPATCH v15 10/35 firmware: arm_scmi: Add Telemetry resources enumeration
In reply to Cristian MarussiAdd basic Telemetry allocation and initialization routines, introducing and wiring-up the logic needed to enumerate all the SCMI Telemetry resources optionally using a lazy on-demand scheme: this approach factors in the flexibility needed to minimize the amount of initialization work based on the effective access pattern used by the upper layer ABI.
---
v14 --> v15
- drop usage of devres for FC iomapping
- trigger full enumeration of DE lookups
- new IS_TDE_TS_ macro helpers
- add a dedicated scmi_telemetry_free_tdes_add helper
- better commenting
- dropping needless __must_check
- flag as fully enumerated only when ALL declared des were found
- drop redundant iounmap
- check FC size on enumeration
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains the basic instance allocations and the DEs enumeration process
- renamed some fields to more strictly adhere to spec nomenclature
--- drivers/firmware/arm_scmi/telemetry.c | 724 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 37 ++ 2 files changed, 761 insertions(+)
drivers/firmware/arm_scmi/telemetry.c +724 -0
@@ -8,15 +8,19 @@#include <linux/atomic.h> #include <linux/bitfield.h> #include <linux/compiler_types.h> +#include <linux/completion.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/io.h> #include <linux/limits.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/slab.h> #include <linux/sprintf.h> #include <linux/string.h> #include <linux/uuid.h> +#include <linux/xarray.h> #include "protocols.h" #include "notify.h"
@@ -52,13 +56,214 @@ struct scmi_msg_resp_telemetry_protocol_attributes {__le32 default_blk_ts_rate; }; +struct de_desc_fastchannel { +__le32 addr_low; +__le32 addr_high; +__le32 size; +}; + +struct scmi_de_desc { +__le32 de_id; +__le32 group_id; +__le32 de_data_sz; +__le32 de_attributes_1; +#define IS_NAME_SUPPORTED (d) (le32_get_bits((d)->de_attributes_1, BIT(31))) +#define IS_FC_SUPPORTED (d) (le32_get_bits((d)->de_attributes_1, BIT(30))) +#define GET_DE_TYPE (d) (le32_get_bits((d)->de_attributes_1, GENMASK(29, 22))) +#define IS_PERSISTENT (d) (le32_get_bits((d)->de_attributes_1, BIT(21))) +#define GET_DE_UNIT_EXP (d) \ + ({ \ + __u32 __signed_exp = \ + le32_get_bits((d)->de_attributes_1, GENMASK(20, 13)); \ + \ + sign_extend32(__signed_exp, 7); \ + }) +#define GET_DE_UNIT (d) (le32_get_bits((d)->de_attributes_1, GENMASK(12, 5))) +#define TSTAMP_SUPPORT (d) (le32_get_bits((d)->de_attributes_1, GENMASK(1, 0))) +__le32 de_attributes_2; +#define GET_DE_INSTA_ID (d) (le32_get_bits((d)->de_attributes_2, GENMASK(31, 24))) +#define GET_COMPO_INSTA_ID (d) (le32_get_bits((d)->de_attributes_2, GENMASK(23, 8))) +#define GET_COMPO_TYPE (d) (le32_get_bits((d)->de_attributes_2, GENMASK(7, 0))) +__le32 de_attributes_3; +}; + +struct scmi_msg_resp_telemetry_de_description { +__le32 num_desc;/* This does NOT count desc[] by the spec */ +struct scmi_de_desc desc[]; +}; + +#define IS_TDE_TSTAMP_TYPE (__t, __ttype) ((__t)->ts_type == (__ttype)) +#define IS_TDE_TS_NONE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_NONE)) +#define IS_TDE_TS_LINE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_LINE)) +#define IS_TDE_TS_BLK (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_BLK)) + +#define TDE_FC_SIZE (_t) \ +({ \ + struct telemetry_de *__t = (_t); \ + size_t fc_size; \ + \ + fc_size = __t->de.info->data_sz; \ + fc_size += __t->de.tstamp_support ? sizeof(u32) * 2 : 0; \ + (fc_size); \ +}) + +enum timestamps { +TSTAMP_NONE =0 , +TSTAMP_LINE =1 , +TSTAMP_BLK =2 , +}; + +#define SHMTI_ID_INVALID GENMASK(31, 0) + +struct telemetry_de { +enum timestamps ts_type; +u32 ts_rate; +bool enumerated; +void __iomem *base; +u32 sid; +u32 offset; +/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */ +u32 fc_size; +struct list_head item; +struct scmi_telemetry_de de; +/* Protect last_val/ts/magic accesses - MUST BE KEPT LAST */ +struct mutex mtx; +}; + +#define to_tde (d) container_of(d, struct telemetry_de, de) +struct telemetry_info {bool streaming_mode;unsigned int num_shmti;unsigned int num_des_tstamp;unsigned int default_blk_ts_rate;const struct scmi_protocol_handle *ph; +struct telemetry_de *tdes; +struct xarray xa_des; +/* Mutex to protect access to @free_des */ +struct mutex free_mtx; +struct list_head free_des; +struct list_head fcs_des;struct scmi_telemetry_info info; +atomic_t rinfo_initializing; +struct completion rinfo_initdone; +struct scmi_telemetry_res_info *rinfo; +struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); +}; + +static struct scmi_telemetry_res_info * +__scmi_telemetry_resources_get (struct telemetry_info *ti); + +static struct telemetry_de * +scmi_telemetry_free_tde_get (struct telemetry_info *ti) +{ +struct telemetry_de *tde; + +guard (mutex)(&ti->free_mtx); + + tde = list_first_entry_or_null(&ti->free_des, struct telemetry_de, item); +if (!tde) +return tde; + +list_del (&tde->item); + +return tde; +} + +static inline void scmi_telemetry_free_tdes_add (struct telemetry_info *ti, +struct telemetry_de *tde) +{ +guard (mutex)(&ti->free_mtx); +list_add_tail (&tde->item, &ti->free_des); +} + +static void scmi_telemetry_free_tde_put (struct telemetry_info *ti, +struct telemetry_de *tde) +{ +struct scmi_telemetry_de_info *info; + +lockdep_assert_held (&tde->mtx); + +/* Unmap any valid FC */ +if (tde->de.fc_support && tde->base) +iounmap (tde->base); + +/* Save clear and restore */ + info = tde->de.info; +memset (info,0 ,sizeof (*info)); +memset (tde,0 , offsetof(struct telemetry_de , mtx)); + tde->de.info = info; + +scmi_telemetry_free_tdes_add (ti, tde); +} + +static struct telemetry_de *scmi_telemetry_tde_lookup (struct telemetry_info *ti, +unsigned int de_id) +{ +struct scmi_telemetry_de *de; + + de =xa_load (&ti->xa_des, de_id); +if (!de) +return NULL ; + +return to_tde (de); +} + +static struct telemetry_de *scmi_telemetry_tde_get (struct telemetry_info *ti, +unsigned int de_id) +{ +struct telemetry_de *tde; + + tde =scmi_telemetry_free_tde_get (ti); +if (!tde) { +dev_err (ti->ph->dev,"Cannot get TDE for ID:0x%08X\n" , de_id); +return ERR_PTR (-ENOSPC ); + } + + tde->sid =SHMTI_ID_INVALID ; + +return tde; +} + +static int scmi_telemetry_tde_register (struct telemetry_info *ti, +struct telemetry_de *tde) +{ +struct scmi_telemetry_res_info *rinfo; +int ret; + +lockdep_assert_held (&tde->mtx); + +/* Get rinfo without triggering a recursive enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + +if (rinfo->num_des >= ti->info.base.num_des) { + ret = -ENOSPC ; + goto err; + } + +/* Store DE pointer by de_id ... */ + ret =xa_insert (&ti->xa_des, tde->de.info->id, &tde->de,GFP_KERNEL ); +if (ret) + goto err; + +/* ... and in the general array */ + rinfo->des[rinfo->num_des] = &tde->de; +/* Make sure the freshly registered DE is visible before the index update */ +smp_store_release (&rinfo->num_des, rinfo->num_des +1 ); + +return 0 ; + +err: +dev_err (ti->ph->dev,"Cannot register TDE for ID:0x%08X - ret:%d\n" , + tde->de.info->id, ret); + +return ret; +} + +struct scmi_tlm_de_priv { +struct telemetry_info *ti; +void *next; +size_t rx_len; }; static int
@@ -100,6 +305,282 @@ scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti)return ret; } +static void iter_tlm_prepare_message (void *message, +unsigned int desc_index,const void *priv) +{ +put_unaligned_le32 (desc_index, message); +} + +static int iter_de_descr_update_state (struct scmi_iterator_state *st, +const void *response,void *priv) +{ +const struct scmi_msg_resp_telemetry_de_description *r = response; +struct scmi_tlm_de_priv *p = priv; + + st->num_returned =le32_get_bits (r->num_desc,GENMASK (15 ,0 )); + st->num_remaining =le32_get_bits (r->num_desc,GENMASK (31 ,16 )); + +if (st->rx_len < (sizeof (*r) +sizeof (r->desc[0 ]) * st->num_returned)) +return -EINVAL ; + +/* Initialized to first descriptor and initial payload size */ + p->next = (void *)r->desc; + p->rx_len = st->rx_len -sizeof (*r); + +return 0 ; +} + +static inline size_t scmi_telemetry_tde_var_size_calc (struct telemetry_de *tde) +{ +size_t sz =0 ; + + sz +=IS_TDE_TS_LINE (tde) ?sizeof (__le32) :0 ; + sz += tde->de.fc_support ?sizeof (struct de_desc_fastchannel ) :0 ; + sz += tde->de.name_support ?SCMI_SHORT_NAME_MAX_SIZE :0 ; + +return sz; +} + +static inline int +scmi_telemetry_area_check (struct device *dev,const char *tag, +u32 low,u32 high,u32 size, +phys_addr_t *phys_addr,size_t *len) +{ +u64 addr = ((u64 )high <<32 ) | low; +phys_addr_t pa = (phys_addr_t )addr; +unsigned long end_phys; + +/* Does the returned pointer fits architetural size ? */ +if ((u64 )pa != addr) { +dev_err (dev,FW_BUG "%s phys_addr (0x%llX) pointer too big.\n" , + tag, pa); +return -EOVERFLOW ; + } + +if (!IS_ALIGNED (pa,4 )) { +dev_err (dev,FW_BUG "%s phys_addr (0x%llX) misaligned.\n" , + tag, pa); +return -EINVAL ; + } + +if (!IS_ALIGNED (size,4 )) { +dev_err (dev,FW_BUG "%s area length (%u) misaligned\n" , + tag, size); +return -EINVAL ; + } + +if (check_add_overflow (pa, size, &end_phys)) { +dev_err (dev,FW_BUG "%s area (0x%llX/%u) wrap-around.\n" , + tag, pa, size); +return -EOVERFLOW ; + } + + *phys_addr = pa; + *len = size; + +return 0 ; +} + +static int scmi_telemetry_de_descriptor_parse (struct telemetry_info *ti, +struct telemetry_de *tde, +void **next,size_t *rx_len) +{ +const struct scmi_de_desc *desc = *next; +size_t payld_sz =sizeof (*desc); + +lockdep_assert_held (&tde->mtx); + +if (*rx_len < payld_sz) +return -ENOSPC ; + + tde->de.info->id =le32_to_cpu (desc->de_id); + tde->de.info->grp_id =le32_to_cpu (desc->group_id); + tde->de.info->data_sz =le32_to_cpu (desc->de_data_sz); + tde->de.info->type =GET_DE_TYPE (desc); + tde->de.info->unit =GET_DE_UNIT (desc); + tde->de.info->unit_exp =GET_DE_UNIT_EXP (desc); + tde->de.info->instance_id =GET_DE_INSTA_ID (desc); + tde->de.info->compo_instance_id =GET_COMPO_INSTA_ID (desc); + tde->de.info->compo_type =GET_COMPO_TYPE (desc); + tde->de.info->persistent =IS_PERSISTENT (desc); + tde->ts_type =TSTAMP_SUPPORT (desc); + tde->de.tstamp_support = !!tde->ts_type; + tde->de.fc_support =IS_FC_SUPPORTED (desc); + tde->de.name_support =IS_NAME_SUPPORTED (desc); +/* Update DE_DESCRIPTOR size for the next iteration */ + *next +=sizeof (*desc); + + payld_sz +=scmi_telemetry_tde_var_size_calc (tde); +if (*rx_len < payld_sz) +return -ENOSPC ; + +if (IS_TDE_TS_LINE (tde)) { +__le32 *line_ts_rate = *next; + + tde->de.info->ts_rate =le32_to_cpu ((*line_ts_rate)); + +/* Variably sized depending on TS support */ + *next +=sizeof (*line_ts_rate); + }else if (tde->ts_type ==TSTAMP_BLK ) { +/* Setup default BLK TS value at first */ + tde->de.info->ts_rate = ti->default_blk_ts_rate; + } + +if (tde->de.fc_support) { +struct de_desc_fastchannel *dfc; +phys_addr_t phys_addr; +void __iomem *addr; +size_t size; +int ret; + + dfc = *next; + + ret =scmi_telemetry_area_check (ti->ph->dev,"FC" , +le32_to_cpu (dfc->addr_low), +le32_to_cpu (dfc->addr_high), +le32_to_cpu (dfc->size), + &phys_addr, &size); +if (ret) +return ret; + +/* Does it fit ? */ +if (size <TDE_FC_SIZE (tde)) { +dev_err (ti->ph->dev, +FW_BUG "TDE FC size too small (%zu)\n" , size); +return -EPROTO ; + } + + addr =ioremap (phys_addr, size); +if (!addr) +return -EADDRNOTAVAIL ; + + tde->base = addr; + tde->offset =0 ; + tde->fc_size = size; + +/* Variably sized depending on FC support */ + *next +=sizeof (*dfc); + } + +if (tde->de.name_support) { +const char *de_name = *next; + +strscpy (tde->de.info->name, de_name,SCMI_SHORT_NAME_MAX_SIZE ); +/* Variably sized depending on name support */ + *next +=SCMI_SHORT_NAME_MAX_SIZE ; + } + + *rx_len -= payld_sz; + +return 0 ; +} + +static int iter_de_descr_process_response (const struct scmi_protocol_handle *ph, +const void *response, +struct scmi_iterator_state *st, +void *priv) +{ +struct scmi_tlm_de_priv *p = priv; +struct telemetry_info *ti = p->ti; +const struct scmi_de_desc *desc = p->next; +struct telemetry_de *tde; +bool discovered = false; +unsigned int de_id; +int ret; + +/* Is at least reading de_id safe */ +if (p->rx_len <sizeof (desc->de_id)) +return -ENOSPC ; + + de_id =le32_to_cpu (desc->de_id); +/* +* Check if this DE was already been discovered by other means, +* such as during boot if Telemetry was detected as already enabled +* and the list of enabled DEs was enumerated separately. +*/ + tde =scmi_telemetry_tde_lookup (ti, de_id); +if (!tde) { +/* Create a new one */ + tde =scmi_telemetry_tde_get (ti, de_id); +if (IS_ERR (tde)) +return PTR_ERR (tde); + + discovered = true; + } + +guard (mutex)(&tde->mtx); +if (tde->enumerated) { +/* Cannot be a duplicate of a DE already created by enumeration */ +dev_err (ph->dev, +"Discovered INVALID DE with DUPLICATED ID:0x%08X\n" , + de_id); +return -EINVAL ; + } + + ret =scmi_telemetry_de_descriptor_parse (ti, tde, &p->next, &p->rx_len); +if (ret) { +dev_err (ph->dev,"Malformed DE Descriptor - ret:%d\n" , ret); +/* DE not enumerated at this point were created in this call */ +if (discovered) +scmi_telemetry_free_tde_put (ti, tde); +return ret; + } + +/* Now fully enumerated */ + tde->enumerated = true; + +/* Register if it was not already ... */ +if (discovered) { + ret =scmi_telemetry_tde_register (ti, tde); +if (ret) { +/* DE not enumerated at this point were created in this call */ +scmi_telemetry_free_tde_put (ti, tde); +return ret; + } + } + +/* Count timestamped DEs */ + ti->num_des_tstamp += !!tde->de.tstamp_support; + +/* Add to FastChannels list */ +if (tde->de.fc_support) +list_add (&tde->item, &ti->fcs_des); + +return 0 ; +} + +static int scmi_telemetry_de_descriptors_get (struct telemetry_info *ti) +{ +const struct scmi_protocol_handle *ph = ti->ph; + +struct scmi_iterator_ops ops = { + .prepare_message = iter_tlm_prepare_message, + .update_state = iter_de_descr_update_state, + .process_response = iter_de_descr_process_response, + }; +struct scmi_tlm_de_priv tpriv = { + .ti = ti, + .next =NULL , + }; +void *iter; +int ret; + +if (!ti->info.base.num_des) +return 0 ; + + iter = ph->hops->iter_response_init (ph, &ops, ti->info.base.num_des, +TELEMETRY_DE_DESCRIPTION , +sizeof (u32), &tpriv); +if (IS_ERR (iter)) +return PTR_ERR (iter); + + ret = ph->hops->iter_response_run (iter); +if (ret) +return ret; + +return 0 ; +} +static const struct scmi_telemetry_info *scmi_telemetry_info_get (const struct scmi_protocol_handle *ph) {
@@ -108,10 +589,247 @@ scmi_telemetry_info_get(const struct scmi_protocol_handle *ph)return &ti->info; } +static const struct scmi_telemetry_res_info * +scmi_telemetry_resources_get (const struct scmi_protocol_handle *ph) +{ +struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); +struct telemetry_info *ti = ph->get_priv (ph); + +/* Ensure the latest installed res_get helper is visible */ + res_get =smp_load_acquire (&ti->res_get); + +return res_get (ti); +} + +static const struct scmi_telemetry_de * +scmi_telemetry_de_lookup (const struct scmi_protocol_handle *ph,u32 id) +{ +struct telemetry_info *ti = ph->get_priv (ph); + +/* Trigger a full proper enumeration */ +scmi_telemetry_resources_get (ph); + +return xa_load (&ti->xa_des, id); +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, + .de_lookup = scmi_telemetry_de_lookup, + .res_get = scmi_telemetry_resources_get, }; +/** +* scmi_telemetry_resources_alloc - Resources allocation +* @ti: A reference to the telemetry info descriptor for this instance +* +* This allocates and initializes dedicated resources for the maximum possible +* number of needed telemetry resources, based on information gathered from +* the initial enumeration: these allocations represent an upper bound on +* the number of discoverable telemetry resources and they will be later +* populated during late deferred further discovery phases. +* +* Note that some of the allocations down below are designed to keep some +* class of data packed together since it eases retrieval from the ABI layer. +* +* Return: 0 on Success, errno otherwise +*/ +static int scmi_telemetry_resources_alloc (struct telemetry_info *ti) +{ +/* Array to hold pointers to discovered DEs */ + struct scmi_telemetry_de **des __free(kfree) = +kzalloc_objs (*des, ti->info.base.num_des); +if (!des) +return -ENOMEM ; + +/* The allocated DE descriptors */ + struct telemetry_de *tdes __free(kfree) = +kzalloc_objs (*tdes, ti->info.base.num_des); +if (!tdes) +return -ENOMEM ; + +/* Allocate a set of contiguous DE info descriptors. */ + struct scmi_telemetry_de_info *dei_store __free(kfree) = +kzalloc_objs (*dei_store, ti->info.base.num_des); +if (!dei_store) +return -ENOMEM ; + + struct scmi_telemetry_res_info *rinfo __free(kfree) = +kzalloc_obj (*rinfo); +if (!rinfo) +return -ENOMEM ; + +mutex_init (&ti->free_mtx); +INIT_LIST_HEAD (&ti->free_des); +for (int i =0 ; i < ti->info.base.num_des; i++) { +mutex_init (&tdes[i].mtx); +/* Bind contiguous DE info structures */ + tdes[i].de.info = &dei_store[i]; +scmi_telemetry_free_tdes_add (ti, &tdes[i]); + } + +INIT_LIST_HEAD (&ti->fcs_des); + + ti->tdes =no_free_ptr (tdes); + + rinfo->des =no_free_ptr (des); + rinfo->dei_store =no_free_ptr (dei_store); + +/* Ensure all of the above assignments are visible */ +smp_store_release (&ti->rinfo,no_free_ptr (rinfo)); + +return 0 ; +} + +static struct scmi_telemetry_res_info * +__scmi_telemetry_resources_get (struct telemetry_info *ti) +{ +/* Ensure rinfo descriptor is visible */ +return smp_load_acquire (&ti->rinfo); +} + +static void scmi_telemetry_resources_free (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; + +/* Get rinfo without triggering a recursive enumeration, cannot be NULL */ + rinfo =__scmi_telemetry_resources_get (ti); +/* Ensure rinfo is no more accessible upfront */ +smp_store_release (&ti->rinfo,NULL ); + +kfree (ti->tdes); + +kfree (rinfo->des); +kfree (rinfo->dei_store); + +kfree (rinfo); + +dev_dbg (ti->ph->dev,"SCMI Telemetry resources freed for instance\n" ); +} + +static void scmi_telemetry_tdes_cleanup (struct telemetry_info *ti) +{ +struct scmi_telemetry_de *de; +unsigned long idx; + +xa_for_each (&ti->xa_des, idx, de) { +struct telemetry_de *tde =to_tde (de); + +guard (mutex)(&tde->mtx); + +scmi_telemetry_free_tde_put (ti, tde); + } + +xa_destroy (&ti->xa_des); +} + +static void scmi_telemetry_instance_cleanup (void *arg) +{ +struct telemetry_info *ti = arg; + +scmi_telemetry_tdes_cleanup (ti); +scmi_telemetry_resources_free (ti); +} + +/** +* scmi_telemetry_resources_enumerate - Enumeration helper +* @ti: A reference to the telemetry info descriptor for this instance +* +* This helper is configured to be called once on the first enumeration +* attempt, when triggered by invoking ti->res_get() from somewhere else. +* +* Once run it substitues itself in ti->res_get() with the simple accessor +* __scmi_telemetry_resources_get, which returns a descriptor to the resources +* that were possibly discovered. +* +* Note that, while it attempts to fully enumerate Data Events and Groups, it +* does NOT fail when such enumerations fail, instead it simply gives up with +* the end result that only a partially populated, but consistent, resources +* descriptor will be returned; in such a case the incomplete descriptor will +* be marked as NOT fully_enumerated: this design enables the kernel to deal +* with badly implemented out-of-spec firmware support while keep on providing +* a minimal sane, albeit possibly incomplete, set of telemetry respources. +* +* Return: A reference to a fully or partially populated resources descriptor +*/ +static struct scmi_telemetry_res_info * +scmi_telemetry_resources_enumerate (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; +struct device *dev = ti->ph->dev; +int ret; + +/* +* Ensure the following initialization can be called only once +* from one thread of execution. +*/ +if (atomic_cmpxchg (&ti->rinfo_initializing,0 ,1 )) { +/* +* When initialization is already ongoing in another thread, +* just wait for its completion and return the fully or partially +* populated rinfo. +*/ +if (!completion_done (&ti->rinfo_initdone)) +wait_for_completion (&ti->rinfo_initdone); + +/* Ensure rinfo descriptor is visible */ +return smp_load_acquire (&ti->rinfo); + } + +/* Note that this code below can be run only once by one thread */ + ret =scmi_telemetry_de_descriptors_get (ti); +if (ret) { +dev_err (dev,FW_BUG "Cannot fully enumerate DEs resources. Degraded system.\n" ); + goto done; + } + +/* Get rinfo without triggering a recursive enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + rinfo->fully_enumerated = rinfo->num_des == ti->info.base.num_des; +done: +/* Disable initialization permanently */ +smp_store_release (&ti->res_get, __scmi_telemetry_resources_get); + +/* Unblock concurrent threads that have been stalled */ +complete_all (&ti->rinfo_initdone); + +/* Ensure local rinfo is visible before returning it */ +smp_mb (); +return ti->rinfo; +} + +/** +* scmi_telemetry_instance_init - Instance initializer +* @ti: A reference to the telemetry info descriptor for this instance +* +* Note that this allocates and initialize all the resources possibly needed +* and then setups the @scmi_telemetry_resources_enumerate helper as the +* default method for the first call to ti->res_get(): this mechanism enables +* the possibility of optionally implementing deferred enumeration policies +* which optionally delay the discovery phase and related SCMI message exchanges +* to a later point in time. +* +* Return: 0 on Success, errno otherwise +*/ +static int scmi_telemetry_instance_init (struct telemetry_info *ti) +{ +int ret; + + ret =scmi_telemetry_resources_alloc (ti); +if (ret) +return ret; + +xa_init (&ti->xa_des); + +/* Setup resources lazy initialization */ +atomic_set (&ti->rinfo_initializing,0 ); +init_completion (&ti->rinfo_initdone); +/* Ensure the new res_get() operation is visible after this point */ +smp_store_mb (ti->res_get, scmi_telemetry_resources_enumerate); + +return devm_add_action_or_reset (ti->ph->dev, + scmi_telemetry_instance_cleanup, ti); +} +static int scmi_telemetry_protocol_init (const struct scmi_protocol_handle *ph) {struct device *dev = ph->dev;
@@ -133,6 +851,12 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph)return ret; } + ret =scmi_telemetry_instance_init (ti); +if (ret) { +dev_err (dev,"Cannot initialize instance. Abort.\n" ); +return ret; + } + ti->info.base.version = ph->version;return ph->set_priv (ph, ti);
include/linux/scmi_protocol.h +37 -0
@@ -830,6 +830,37 @@ struct scmi_pinctrl_proto_ops {int (*pin_free)(const struct scmi_protocol_handle *ph,u32 pin); }; +struct scmi_telemetry_de_info { +unsigned int id; +unsigned int grp_id; +unsigned int data_sz; +unsigned int type; +unsigned int unit; +int unit_exp; +unsigned int ts_rate; +unsigned int instance_id; +unsigned int compo_instance_id; +unsigned int compo_type; +bool persistent; +char name[16 ]; +}; + +struct scmi_telemetry_de { +bool tstamp_support; +bool fc_support; +bool name_support; +bool enabled; +bool tstamp_enabled; +struct scmi_telemetry_de_info *info; +}; + +struct scmi_telemetry_res_info { +bool fully_enumerated; +unsigned int num_des; +struct scmi_telemetry_de **des; +struct scmi_telemetry_de_info *dei_store; +}; +struct scmi_telemetry_base_info {unsigned int version;uuid_t primary_revision;
@@ -853,10 +884,16 @@ struct scmi_telemetry_info {* by SCMI Telemetry Protocol * * @info_get: get the general Telemetry information. +* @de_lookup: get a specific DE descriptor from the DE id. +* @res_get: get a reference to the Telemetry resources descriptor. */ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get) (const struct scmi_protocol_handle *ph); +const struct scmi_telemetry_de __must_check *(*de_lookup) + (const struct scmi_protocol_handle *ph,u32 id); +const struct scmi_telemetry_res_info *(*res_get) + (const struct scmi_protocol_handle *ph); };/**
Signature · 1 line
2.54.0
Re: [PATCH v15 10/35] firmware: arm_scmi: Add Telemetry resources enumeration
Quoted 7 lines · On Mon, 9 Nov 2026 09:11:56 +0000 Cristian Marussi <[email protected]>…
Add basic Telemetry allocation and initialization routines, introducing and wiring-up the logic needed to enumerate all the SCMI Telemetry resources optionally using a lazy on-demand scheme: this approach factors in the flexibility needed to minimize the amount of initialization work based on the effective access pattern used by the upper layer ABI.
Signed-off-by Cristian Marussi <[email protected]>
Just some minor stuff inline.
Quoted 24 lines
---
v14 --> v15
- drop usage of devres for FC iomapping
- trigger full enumeration of DE lookups
- newIS_TDE_TS_macro helpers
- add a dedicatedscmi_telemetry_free_tdes_addhelper
- better commenting
- dropping needless__must_check
- flag as fully enumerated only when ALL declared des were found
- drop redundant iounmap
- check FC size on enumeration
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains the basic instance allocations and the DEs enumeration process
- renamed some fields to more strictly adhere to spec nomenclature--- drivers/firmware/arm_scmi/telemetry.c | 724 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 37 ++ 2 files changed, 761 insertions(+)drivers/firmware/arm_scmi/telemetry.c +0
-0
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_protocol_attributes { · line 106
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+ +#define TDE_FC_SIZE (_t) \ +({ \ + struct telemetry_de *__t = (_t); \ + size_t fc_size; \ + \ + fc_size = __t->de.info->data_sz; \ + fc_size += __t->de.tstamp_support ? sizeof(u32) * 2 : 0; \
Maybe add a comment on why two u32. I had to dig around for that.
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+ (fc_size); \ +}) + +enum timestamps { +TSTAMP_NONE =0 , +TSTAMP_LINE =1 , +TSTAMP_BLK =2 , +}; +
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_protocol_attributes { · line 125
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+#define SHMTI_ID_INVALID GENMASK(31, 0) + +struct telemetry_de { +enum timestamps ts_type; +u32 ts_rate; +bool enumerated; +void __iomem *base; +u32 sid; +u32 offset; +/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */
Why this comment? Seems a bit random.
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_protocol_attributes { · line 129
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+u32 fc_size; +struct list_head item; +struct scmi_telemetry_de de; +/* Protect last_val/ts/magic accesses - MUST BE KEPT LAST */
Why? Would be useful to have that as well as what must be the case.
Quoted 26 lines
drivers/firmware/arm_scmi/telemetry.c +24
-0+struct mutex mtx; +}; + +static int scmi_telemetry_tde_register (struct telemetry_info *ti, +struct telemetry_de *tde) +{ +struct scmi_telemetry_res_info *rinfo; +int ret; + +lockdep_assert_held (&tde->mtx); + +/* Get rinfo without triggering a recursive enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + +if (rinfo->num_des >= ti->info.base.num_des) { + ret = -ENOSPC ; + goto err; + } + +/* Store DE pointer by de_id ... */ + ret =xa_insert (&ti->xa_des, tde->de.info->id, &tde->de,GFP_KERNEL ); +if (ret) + goto err; +
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_protocol_attributes { · line 258
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+/* ... and in the general array */ + rinfo->des[rinfo->num_des] = &tde->de; +/* Make sure the freshly registered DE is visible before the index update */ +smp_store_release (&rinfo->num_des, rinfo->num_des +1 ); + +return 0 ; + +err: +dev_err (ti->ph->dev,"Cannot register TDE for ID:0x%08X - ret:%d\n" , + tde->de.info->id, ret);
I'd push that into the error paths above where you can give a more specific message + avoid the goto complexity by returning directly.
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+ +return ret; +} + +static int iter_de_descr_update_state (struct scmi_iterator_state *st, +const void *response,void *priv) +{ +const struct scmi_msg_resp_telemetry_de_description *r = response;
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 327
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+struct scmi_tlm_de_priv *p = priv; + + st->num_returned =le32_get_bits (r->num_desc,GENMASK (15 ,0 )); + st->num_remaining =le32_get_bits (r->num_desc,GENMASK (31 ,16 )); + +if (st->rx_len < (sizeof (*r) +sizeof (r->desc[0 ]) * st->num_returned)) +return -EINVAL ; + +/* Initialized to first descriptor and initial payload size */ + p->next = (void *)r->desc;
Any need for the cast? Looks to be a non const pointer being stored to a void * so implicit cast should be fine.
Quoted 12 lines
drivers/firmware/arm_scmi/telemetry.c +11
-0+ p->rx_len = st->rx_len -sizeof (*r); + +return 0 ; +} + +static int scmi_telemetry_de_descriptor_parse (struct telemetry_info *ti, +struct telemetry_de *tde, +void **next,size_t *rx_len) +{ +const struct scmi_de_desc *desc = *next; +size_t payld_sz =sizeof (*desc);
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 399
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ +lockdep_assert_held (&tde->mtx); + +if (*rx_len < payld_sz) +return -ENOSPC ; + + tde->de.info->id =le32_to_cpu (desc->de_id); + tde->de.info->grp_id =le32_to_cpu (desc->group_id); + tde->de.info->data_sz =le32_to_cpu (desc->de_data_sz); + tde->de.info->type =GET_DE_TYPE (desc);
I much prefer this approach of passing in the whole desc to the one in the previous patch where the __le32 attributes_1 was passed in.
It keeps all the 'which field' associations in one place in the code. So if you want to do these GET / SUPPORTS macros then on the whole resp structure is the better option (as done here!)
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+ tde->de.info->unit =GET_DE_UNIT (desc); + tde->de.info->unit_exp =GET_DE_UNIT_EXP (desc); + tde->de.info->instance_id =GET_DE_INSTA_ID (desc); + tde->de.info->compo_instance_id =GET_COMPO_INSTA_ID (desc);
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 413
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ tde->de.info->compo_type =GET_COMPO_TYPE (desc); + tde->de.info->persistent =IS_PERSISTENT (desc); + tde->ts_type =TSTAMP_SUPPORT (desc); + tde->de.tstamp_support = !!tde->ts_type; + tde->de.fc_support =IS_FC_SUPPORTED (desc); + tde->de.name_support =IS_NAME_SUPPORTED (desc); +/* Update DE_DESCRIPTOR size for the next iteration */ + *next +=sizeof (*desc); + + payld_sz +=scmi_telemetry_tde_var_size_calc (tde);
Might be easier to just check on element in the variable sized region? Keeps the size together with what we are looking for it in. Or is aim of doing this here to make sure there are no possible failure paths after the fast channel stuff so we don't have to unwind that? If so maybe a comment here would help reviewers understand that.
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 422
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+if (*rx_len < payld_sz) +return -ENOSPC ; + +if (IS_TDE_TS_LINE (tde)) { +__le32 *line_ts_rate = *next; + + tde->de.info->ts_rate =le32_to_cpu ((*line_ts_rate)); + +/* Variably sized depending on TS support */
As below. I'd say 'what' is variably sized, or drop the comment.
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+ *next +=sizeof (*line_ts_rate); + }else if (tde->ts_type ==TSTAMP_BLK ) { +/* Setup default BLK TS value at first */ + tde->de.info->ts_rate = ti->default_blk_ts_rate;
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 436
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ } + +if (tde->de.fc_support) { +struct de_desc_fastchannel *dfc; +phys_addr_t phys_addr; +void __iomem *addr; +size_t size; +int ret; + + dfc = *next;
Similar to the setting of desc at top of file, I'd do this at declaration
struct de_desc_fastchannel *dfc = *next;
Mainly for consistency rather that it really mattering either way!
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 442
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +6
-0+ + ret =scmi_telemetry_area_check (ti->ph->dev,"FC" , +le32_to_cpu (dfc->addr_low), +le32_to_cpu (dfc->addr_high), +le32_to_cpu (dfc->size), + &phys_addr, &size);
Building phys_addr inside a function called check is a bit unintuitive. I'd do it out here and pass it in as a parameter.
Similar with size (though that's just copied).
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+if (ret) +return ret; + +/* Does it fit ? */ +if (size <TDE_FC_SIZE (tde)) { +dev_err (ti->ph->dev, +FW_BUG "TDE FC size too small (%zu)\n" , size); +return -EPROTO ; + }
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 461
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ + addr =ioremap (phys_addr, size); +if (!addr) +return -EADDRNOTAVAIL ; + + tde->base = addr; + tde->offset =0 ; + tde->fc_size = size; + +/* Variably sized depending on FC support */
Not sure that comment adds much seeing as we are in the fc support if statement.
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 469
Quoted 8 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+ *next +=sizeof (*dfc); + } + +if (tde->de.name_support) { +const char *de_name = *next; + +strscpy (tde->de.info->name, de_name,SCMI_SHORT_NAME_MAX_SIZE ); +/* Variably sized depending on name support */
Similarly. This seems pretty obvious given where we are. Also not clear 'what' is variably sized from the comment.
Quoted 21 lines
drivers/firmware/arm_scmi/telemetry.c +21
-0+ *next +=SCMI_SHORT_NAME_MAX_SIZE ; + } + + *rx_len -= payld_sz; + +return 0 ; +} + +static int iter_de_descr_process_response (const struct scmi_protocol_handle *ph, +const void *response, +struct scmi_iterator_state *st, +void *priv) +{ +struct scmi_tlm_de_priv *p = priv; +struct telemetry_info *ti = p->ti; +const struct scmi_de_desc *desc = p->next; +struct telemetry_de *tde; +bool discovered = false; +unsigned int de_id; +int ret; +
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 500
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+/* Is at least reading de_id safe */ +if (p->rx_len <sizeof (desc->de_id)) +return -ENOSPC ; + + de_id =le32_to_cpu (desc->de_id); +/* +* Check if this DE was already been discovered by other means, +* such as during boot if Telemetry was detected as already enabled +* and the list of enabled DEs was enumerated separately. +*/
Is this something added in a later patch? So far I'm not seeing any means for this to happen and hence for the tde->enumerated check below to ever not trigger (unless we get a failure in the descriptor parsing).
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 504
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+ tde =scmi_telemetry_tde_lookup (ti, de_id); +if (!tde) { +/* Create a new one */ + tde =scmi_telemetry_tde_get (ti, de_id);
The naming here with _get made me think this would retrieve the instance if it was already registered. Instead it is getting a free on from the pool.. Maybe better called alloc? This naming is the reason the Create comment is currently necessary. Seems easy to resolve!
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+if (IS_ERR (tde)) +return PTR_ERR (tde); + + discovered = true; + } + +guard (mutex)(&tde->mtx); +if (tde->enumerated) { +/* Cannot be a duplicate of a DE already created by enumeration */
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 523
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+dev_err (ph->dev, +"Discovered INVALID DE with DUPLICATED ID:0x%08X\n" , + de_id); +return -EINVAL ; + } + + ret =scmi_telemetry_de_descriptor_parse (ti, tde, &p->next, &p->rx_len); +if (ret) { +dev_err (ph->dev,"Malformed DE Descriptor - ret:%d\n" , ret); +/* DE not enumerated at this point were created in this call */
If they were created in this call, why do we need to check 'discovered'? Comment seems a bit confusing.
Quoted 30 lines
drivers/firmware/arm_scmi/telemetry.c +30
-0+if (discovered) +scmi_telemetry_free_tde_put (ti, tde); +return ret; + } + +/* Now fully enumerated */ + tde->enumerated = true; + +/* Register if it was not already ... */ +if (discovered) { + ret =scmi_telemetry_tde_register (ti, tde); +if (ret) { +/* DE not enumerated at this point were created in this call */ +scmi_telemetry_free_tde_put (ti, tde); +return ret; + } + } + +/* Count timestamped DEs */ + ti->num_des_tstamp += !!tde->de.tstamp_support; + +/* Add to FastChannels list */ +if (tde->de.fc_support) +list_add (&tde->item, &ti->fcs_des); + +return 0 ; +} + +static int scmi_telemetry_de_descriptors_get (struct telemetry_info *ti) +{
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_protocol_attributes_get(struct telemetry_info *ti) · line 563
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+const struct scmi_protocol_handle *ph = ti->ph; + +struct scmi_iterator_ops ops = { + .prepare_message = iter_tlm_prepare_message, + .update_state = iter_de_descr_update_state, + .process_response = iter_de_descr_process_response, + }; +struct scmi_tlm_de_priv tpriv = { + .ti = ti, + .next =NULL ,
It will be NULL whether or not you set it. Is there significant documentation value in doing so explicitly?
Quoted 35 lines
drivers/firmware/arm_scmi/telemetry.c +28
-0+ }; +void *iter; +int ret; + +if (!ti->info.base.num_des) +return 0 ; + + iter = ph->hops->iter_response_init (ph, &ops, ti->info.base.num_des, +TELEMETRY_DE_DESCRIPTION , +sizeof (u32), &tpriv); +if (IS_ERR (iter)) +return PTR_ERR (iter); + + ret = ph->hops->iter_response_run (iter); +if (ret) +return ret; + +return 0 ; +} +static const struct scmi_telemetry_info *scmi_telemetry_info_get (const struct scmi_protocol_handle *ph) {@@ -108,10 +589,247 @@ scmi_telemetry_info_get(const struct scmi_protocol_handle *ph)return &ti->info; } +static const struct scmi_telemetry_res_info * +scmi_telemetry_resources_get (const struct scmi_protocol_handle *ph) +{ +struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); +struct telemetry_info *ti = ph->get_priv (ph); + +/* Ensure the latest installed res_get helper is visible */ + res_get =smp_load_acquire (&ti->res_get);
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_info_get(const struct scmi_protocol_handle *ph) · line 609
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ +return res_get (ti); +} + +static const struct scmi_telemetry_de * +scmi_telemetry_de_lookup (const struct scmi_protocol_handle *ph,u32 id) +{ +struct telemetry_info *ti = ph->get_priv (ph); + +/* Trigger a full proper enumeration */
I'm a bit confused, doesn't this only trigger full enumeration on first access? That comment seems to imply every time. Or is there something about this function that ensures it is always the first access and is only used that one time?
Quoted 40 lines
drivers/firmware/arm_scmi/telemetry.c +36
-0+scmi_telemetry_resources_get (ph); + +return xa_load (&ti->xa_des, id); +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, + .de_lookup = scmi_telemetry_de_lookup, + .res_get = scmi_telemetry_resources_get, }; +/** +* scmi_telemetry_resources_alloc - Resources allocation +* @ti: A reference to the telemetry info descriptor for this instance +* +* This allocates and initializes dedicated resources for the maximum possible +* number of needed telemetry resources, based on information gathered from +* the initial enumeration: these allocations represent an upper bound on +* the number of discoverable telemetry resources and they will be later +* populated during late deferred further discovery phases. +* +* Note that some of the allocations down below are designed to keep some +* class of data packed together since it eases retrieval from the ABI layer. +* +* Return: 0 on Success, errno otherwise +*/ +static int scmi_telemetry_resources_alloc (struct telemetry_info *ti) +{ +/* Array to hold pointers to discovered DEs */ + struct scmi_telemetry_de **des __free(kfree) = +kzalloc_objs (*des, ti->info.base.num_des); +if (!des) +return -ENOMEM ; + +/* The allocated DE descriptors */ + struct telemetry_de *tdes __free(kfree) = +kzalloc_objs (*tdes, ti->info.base.num_des); +if (!tdes) +return -ENOMEM ; +
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_info_get(const struct scmi_protocol_handle *ph) · line 659
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+/* Allocate a set of contiguous DE info descriptors. */ + struct scmi_telemetry_de_info *dei_store __free(kfree) = +kzalloc_objs (*dei_store, ti->info.base.num_des); +if (!dei_store) +return -ENOMEM ; + + struct scmi_telemetry_res_info *rinfo __free(kfree) = +kzalloc_obj (*rinfo); +if (!rinfo) +return -ENOMEM ;
It's a minor thing, but I'd allocate these in the order that is the reverse of that used for tear down. Right now they are not quite in the same order so it's not that you are following another consistent scheme.
Quoted 58 lines
drivers/firmware/arm_scmi/telemetry.c +57
-0+ +mutex_init (&ti->free_mtx); +INIT_LIST_HEAD (&ti->free_des); +for (int i =0 ; i < ti->info.base.num_des; i++) { +mutex_init (&tdes[i].mtx); +/* Bind contiguous DE info structures */ + tdes[i].de.info = &dei_store[i]; +scmi_telemetry_free_tdes_add (ti, &tdes[i]); + } + +INIT_LIST_HEAD (&ti->fcs_des); + + ti->tdes =no_free_ptr (tdes); + + rinfo->des =no_free_ptr (des); + rinfo->dei_store =no_free_ptr (dei_store); + +/* Ensure all of the above assignments are visible */ +smp_store_release (&ti->rinfo,no_free_ptr (rinfo)); + +return 0 ; +} + +static struct scmi_telemetry_res_info * +__scmi_telemetry_resources_get (struct telemetry_info *ti) +{ +/* Ensure rinfo descriptor is visible */ +return smp_load_acquire (&ti->rinfo); +} + +static void scmi_telemetry_resources_free (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; + +/* Get rinfo without triggering a recursive enumeration, cannot be NULL */ + rinfo =__scmi_telemetry_resources_get (ti); +/* Ensure rinfo is no more accessible upfront */ +smp_store_release (&ti->rinfo,NULL ); + +kfree (ti->tdes); + +kfree (rinfo->des); +kfree (rinfo->dei_store); + +kfree (rinfo); + +dev_dbg (ti->ph->dev,"SCMI Telemetry resources freed for instance\n" ); +} + +/** +* scmi_telemetry_resources_enumerate - Enumeration helper +* @ti: A reference to the telemetry info descriptor for this instance +* +* This helper is configured to be called once on the first enumeration +* attempt, when triggered by invoking ti->res_get() from somewhere else. +* +* Once run it substitues itself in ti->res_get() with the simple accessor
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_info_get(const struct scmi_protocol_handle *ph) · line 750
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+* __scmi_telemetry_resources_get, which returns a descriptor to the resources +* that were possibly discovered. +* +* Note that, while it attempts to fully enumerate Data Events and Groups, it +* does NOT fail when such enumerations fail, instead it simply gives up with +* the end result that only a partially populated, but consistent, resources +* descriptor will be returned; in such a case the incomplete descriptor will +* be marked as NOT fully_enumerated: this design enables the kernel to deal +* with badly implemented out-of-spec firmware support while keep on providing +* a minimal sane, albeit possibly incomplete, set of telemetry respources.
I'm curious about this. What's the motivation for being nice to people who write buggy firmware? I'd kind of prefer an opt in quirk for any that show up where a fix is impractical rather than a paper over the bad from the start.
Quoted 12 lines
drivers/firmware/arm_scmi/telemetry.c +12
-0+* +* Return: A reference to a fully or partially populated resources descriptor +*/ +static struct scmi_telemetry_res_info * +scmi_telemetry_resources_enumerate (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; +struct device *dev = ti->ph->dev; +int ret; + +/* +* Ensure the following initialization can be called only once
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_info_get(const struct scmi_protocol_handle *ph) · line 772
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+* from one thread of execution. +*/ +if (atomic_cmpxchg (&ti->rinfo_initializing,0 ,1 )) { +/* +* When initialization is already ongoing in another thread, +* just wait for its completion and return the fully or partially +* populated rinfo. +*/ +if (!completion_done (&ti->rinfo_initdone)) +wait_for_completion (&ti->rinfo_initdone);
Given this is a not particularly hot initialization path, do we care about the small saving the completion_done precheck is giving here? I'd just do the wait unconditionally. If you do need the completion_done check then add a comment for why or someone is likely to tidy it away in future!
Quoted 23 lines
drivers/firmware/arm_scmi/telemetry.c +23
-0+ +/* Ensure rinfo descriptor is visible */ +return smp_load_acquire (&ti->rinfo); + } + +/* Note that this code below can be run only once by one thread */ + ret =scmi_telemetry_de_descriptors_get (ti); +if (ret) { +dev_err (dev,FW_BUG "Cannot fully enumerate DEs resources. Degraded system.\n" ); + goto done; + } + +/* Get rinfo without triggering a recursive enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + rinfo->fully_enumerated = rinfo->num_des == ti->info.base.num_des; +done: +/* Disable initialization permanently */ +smp_store_release (&ti->res_get, __scmi_telemetry_resources_get); + +/* Unblock concurrent threads that have been stalled */ +complete_all (&ti->rinfo_initdone); + +/* Ensure local rinfo is visible before returning it */
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_info_get(const struct scmi_protocol_handle *ph) · line 805
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+smp_mb (); +return ti->rinfo; +} + +/** +* scmi_telemetry_instance_init - Instance initializer +* @ti: A reference to the telemetry info descriptor for this instance +* +* Note that this allocates and initialize all the resources possibly needed +* and then setups the @scmi_telemetry_resources_enumerate helper as the
then sets up
(setup only exists as a noun).
Quoted 15 lines
drivers/firmware/arm_scmi/telemetry.c +15
-0+* default method for the first call to ti->res_get(): this mechanism enables +* the possibility of optionally implementing deferred enumeration policies +* which optionally delay the discovery phase and related SCMI message exchanges +* to a later point in time. +* +* Return: 0 on Success, errno otherwise +*/ +static int scmi_telemetry_instance_init (struct telemetry_info *ti) +{ +int ret; + + ret =scmi_telemetry_resources_alloc (ti); +if (ret) +return ret; +
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_info_get(const struct scmi_protocol_handle *ph) · line 830
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+xa_init (&ti->xa_des); + +/* Setup resources lazy initialization */ +atomic_set (&ti->rinfo_initializing,0 ); +init_completion (&ti->rinfo_initdone); +/* Ensure the new res_get() operation is visible after this point */ +smp_store_mb (ti->res_get, scmi_telemetry_resources_enumerate); + +return devm_add_action_or_reset (ti->ph->dev, + scmi_telemetry_instance_cleanup, ti);
I'd have preferred clear association of each step this unwinds with where the thing was set up, but I suppose this isn't too bad as is.
Quoted 40 lines
drivers/firmware/arm_scmi/telemetry.c +1
-0+}include/linux/scmi_protocol.h +30
-0@@ -830,6 +830,37 @@ struct scmi_pinctrl_proto_ops {int (*pin_free)(const struct scmi_protocol_handle *ph,u32 pin); }; +struct scmi_telemetry_de_info { +unsigned int id; +unsigned int grp_id; +unsigned int data_sz; +unsigned int type; +unsigned int unit; +int unit_exp; +unsigned int ts_rate; +unsigned int instance_id; +unsigned int compo_instance_id; +unsigned int compo_type; +bool persistent; +char name[16 ]; +}; + +struct scmi_telemetry_de { +bool tstamp_support; +bool fc_support; +bool name_support; +bool enabled; +bool tstamp_enabled; +struct scmi_telemetry_de_info *info; +}; + +struct scmi_telemetry_res_info { +bool fully_enumerated; +unsigned int num_des; +struct scmi_telemetry_de **des; +struct scmi_telemetry_de_info *dei_store; +};
Cristian Marussi
reviewerRe: [PATCH v15 10/35] firmware: arm_scmi: Add Telemetry resources enumeration
Quoted 3 lines · Jonathan Cameron
On Mon, 9 Nov 2026 09:11:56 +0000 Cristian Marussi <[email protected]> wrote:
Just one eply down below, I will address and reply more in depth next days while addressing the usual zillion Sashiko issues (tiny now at least)
Quoted 6 lines
Quoted 6 lines
Add basic Telemetry allocation and initialization routines, introducing and wiring-up the logic needed to enumerate all the SCMI Telemetry resources optionally using a lazy on-demand scheme: this approach factors in the flexibility needed to minimize the amount of initialization work based on the effective access pattern used by the upper layer ABI.
[snip]
Quoted 15 lines
Quoted 15 lines
drivers/firmware/arm_scmi/telemetry.c +15
-0+ +/** +* scmi_telemetry_resources_enumerate - Enumeration helper +* @ti: A reference to the telemetry info descriptor for this instance +* +* This helper is configured to be called once on the first enumeration +* attempt, when triggered by invoking ti->res_get() from somewhere else. +* +* Once run it substitues itself in ti->res_get() with the simple accessor +* __scmi_telemetry_resources_get, which returns a descriptor to the resources +* that were possibly discovered. +* +* Note that, while it attempts to fully enumerate Data Events and Groups, it +* does NOT fail when such enumerations fail, instead it simply gives up with +* the end result that only a partially populated, but consistent, resources
Quoted 9 lines
Quoted 4 lines
+ * descriptor will be returned; in such a case the incomplete descriptor will
+ * be marked as NOTfully_enumerated: this design enables the kernel to deal
+ * with badly implemented out-of-spec firmware support while keep on providing
+ * a minimal sane, albeit possibly incomplete, set of telemetry respources.I'm curious about this. What's the motivation for being nice to people who write buggy firmware? I'd kind of prefer an opt in quirk for any that show up where a fix is impractical rather than a paper over the bad from the start.
Trust me it was never about being nice to FW guys :P
The reasoning, which I am indeed myself questioning these days, was to be able to carry on and boot a minimal Telemetry working system even without all of the detailed description retrieved by this command enumeration process since all of that data descriptors are NOT strictly needed, unless yout system defines groups or FastChannels.
Because on one side it is true thet DE_DESCRIPTION is a mandatory command BUT on the other side it is also true that the spec does really delegate any further detailed description of the Telemetry data to some external JSON-like file: so you can really carry on without all of this informational and topological data.
As an example if userspace does not want to use kernel mediated access to the Telemetry data (most of the IOCTL ABI later on) but instead it decides to grab and parse directly the RAW mmaped data from the SHMTI/TDCF, in that case you just need the fds to the areas to mmap and its fully comprehensive (hopefully) JSON file.
Another possible scenario is bring-up with a limited features FW.
This is also the reasoning behind having the lazy resource enumeration:
you can boot, get the SHMTIs fds and mmap those areas and that's it:
you're good to go without doing any enumeration at all.
There were more FW issues indeed that I used to 'survive' early in this series that I stop being graceful about it, and even in this case, despite surviving in a degraded state I printout a FW_BUG.
Now the bad side of the story is that keeping half-initialized stuff in a degraded system lead to an increasing number of possible issues flagged by Sashiko, mostly rare errors on path conditions to fix, but I am starting to wonder if all of this is worth...
Thanks,
Cristian
Cristian Marussi
reviewerPATCH v15 11/35 firmware: arm_scmi: Add Telemetry groups initialization
In reply to Cristian MarussiAllocate and initialize Telemetry Groups from base DataEvent data.
---
v14 --> v15
- removed needless vars from groups_init
- bailout from group initialization when NO DEs were found
- better handling of error path on groups init
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains the groups initialization code.
--- drivers/firmware/arm_scmi/telemetry.c | 154 +++++++++++++++++++++++++- include/linux/scmi_protocol.h | 24 ++++ 2 files changed, 176 insertions(+), 2 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +152 -2
@@ -387,6 +387,7 @@ static int scmi_telemetry_de_descriptor_parse(struct telemetry_info *ti, {const struct scmi_de_desc *desc = *next;size_t payld_sz =sizeof (*desc); +unsigned int grp_id;lockdep_assert_held (&tde->mtx);
@@ -394,7 +395,22 @@ static int scmi_telemetry_de_descriptor_parse(struct telemetry_info *ti,return -ENOSPC ; tde->de.info->id =le32_to_cpu (desc->de_id); - tde->de.info->grp_id =le32_to_cpu (desc->group_id); + grp_id =le32_to_cpu (desc->group_id); +if (grp_id !=SCMI_TLM_GRP_INVALID ) { +struct scmi_telemetry_res_info *rinfo; + +/* Get rinfo without triggering a recursive enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + +/* Group descriptors are empty but allocated at this point */ +if (grp_id >= ti->info.base.num_groups) +return -EINVAL ; + +/* Link to parent group */ + tde->de.grp = &rinfo->grps[grp_id]; + } + + tde->de.info->grp_id = grp_id; tde->de.info->data_sz =le32_to_cpu (desc->de_data_sz); tde->de.info->type =GET_DE_TYPE (desc); tde->de.info->unit =GET_DE_UNIT (desc);
@@ -546,6 +562,102 @@ static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph,if (tde->de.fc_support)list_add (&tde->item, &ti->fcs_des); +/* Account for this DE in group num_de counter */ +if (tde->de.grp) + tde->de.grp->info->num_des++; + +return 0 ; +} + +static int +scmi_telemetry_de_groups_init (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; +unsigned int num_groups =0 ; + +/* Get rinfo without triggering a recursive enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + +/* Bailout when no DEs were discovered */ +if (!rinfo->num_des) +return 0 ; + +/* Allocate all groups DEs IDs arrays at first ... */ +for (int i =0 ; i < ti->info.base.num_groups; i++, num_groups++) { +struct scmi_telemetry_group *grp = &rinfo->grps[i]; +size_t des_str_sz; + + unsigned int *des __free(kfree) = + kzalloc_objs(unsigned int, grp->info->num_des); +if (!des) +break ; + +/* +* Max size 32bit ID string in Hex: 0xCAFECAFE +* - 10 digits + ' '/'\n' = 11 bytes per number +* - terminating NUL character +*/ + des_str_sz = grp->info->num_des *11 +1 ; + char *des_str __free(kfree) = kzalloc_objs(*des_str, des_str_sz); +if (!des_str) +break ; + + grp->des =no_free_ptr (des); + grp->des_str =no_free_ptr (des_str); + } + +/* Unroll on failure... */ +if (num_groups < ti->info.base.num_groups) { +for (int i = num_groups -1 ; i >=0 ; i--) { +kfree (rinfo->grps[i].des); + rinfo->grps[i].des =NULL ; +kfree (rinfo->grps[i].des_str); + rinfo->grps[i].des_str =NULL ; + } + +return -ENOMEM ; + } + +/* Reset group DE counters before populating */ +for (int i =0 ; i < ti->info.base.num_groups; i++) + rinfo->grps[i].info->num_des =0 ; + +/* Scan DEs and populate DE IDs arrays for all groups */ +for (int i =0 ; i < rinfo->num_des; i++) { +struct scmi_telemetry_group *grp; + + grp = rinfo->des[i]->grp; +if (!grp) +continue ; + +/* +* Note that, at this point, num_des is guaranteed to be +* sane (in-bounds) by construction. +*/ + grp->des[grp->info->num_des++] = i; + } + +/* Build composing DES string */ +for (int i =0 ; i < ti->info.base.num_groups; i++) { +struct scmi_telemetry_group *grp = &rinfo->grps[i]; +size_t bufsize = grp->info->num_des *11 +1 ; +char *buf = grp->des_str; + +for (int j =0 ; j < grp->info->num_des; j++) { +char term = j != (grp->info->num_des -1 ) ?' ' :'\0' ; +int len; + + len =scnprintf (buf, bufsize,"0x%08X%c" , + rinfo->des[grp->des[j]]->info->id, term); + + buf += len; + bufsize -= len; + } + } + +/* Expose all groups once all fully initialized */ + rinfo->num_groups = num_groups; +return 0 ; }
@@ -578,7 +690,7 @@ static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti)if (ret)return ret; -return 0 ; +return scmi_telemetry_de_groups_init (ti); } static const struct scmi_telemetry_info *
@@ -653,6 +765,18 @@ static int scmi_telemetry_resources_alloc(struct telemetry_info *ti)if (!dei_store)return -ENOMEM ; +/* Array to hold descriptors of discovered GROUPs */ + struct scmi_telemetry_group *grps __free(kfree) = +kzalloc_objs (*grps, ti->info.base.num_groups); +if (!grps) +return -ENOMEM ; + +/* Allocate a set of contiguous Group info descriptors. */ + struct scmi_telemetry_grp_info *grps_store __free(kfree) = +kzalloc_objs (*grps_store, ti->info.base.num_groups); +if (!grps_store) +return -ENOMEM ; + struct scmi_telemetry_res_info *rinfo __free(kfree) =kzalloc_obj (*rinfo);if (!rinfo)
@@ -667,12 +791,20 @@ static int scmi_telemetry_resources_alloc(struct telemetry_info *ti)scmi_telemetry_free_tdes_add (ti, &tdes[i]); } +for (int i =0 ; i < ti->info.base.num_groups; i++) { + grps_store[i].grp_id = i; +/* Bind contiguous Group info struct */ + grps[i].info = &grps_store[i]; + } +INIT_LIST_HEAD (&ti->fcs_des); ti->tdes =no_free_ptr (tdes); rinfo->des =no_free_ptr (des); rinfo->dei_store =no_free_ptr (dei_store); + rinfo->grps =no_free_ptr (grps); + rinfo->grps_store =no_free_ptr (grps_store);/* Ensure all of the above assignments are visible */ smp_store_release (&ti->rinfo,no_free_ptr (rinfo));
@@ -701,6 +833,9 @@ static void scmi_telemetry_resources_free(struct telemetry_info *ti)kfree (rinfo->des);kfree (rinfo->dei_store); +kfree (rinfo->grps); +kfree (rinfo->grps_store); +kfree (rinfo);dev_dbg (ti->ph->dev,"SCMI Telemetry resources freed for instance\n" );
@@ -722,10 +857,25 @@ static void scmi_telemetry_tdes_cleanup(struct telemetry_info *ti)xa_destroy (&ti->xa_des); } +static void scmi_telemetry_groups_free (struct scmi_telemetry_res_info *rinfo) +{ +/* Cannot be NULL here by construction, make sure it remains like that */ +if (WARN_ON (!rinfo)) +return ; + +for (int i =0 ; i < rinfo->num_groups; i++) { +struct scmi_telemetry_group *grp = &rinfo->grps[i]; + +kfree (grp->des); +kfree (grp->des_str); + } +} +static void scmi_telemetry_instance_cleanup (void *arg) {struct telemetry_info *ti = arg; +scmi_telemetry_groups_free (ti->rinfo);scmi_telemetry_tdes_cleanup (ti);scmi_telemetry_resources_free (ti); }
include/linux/scmi_protocol.h +24 -0
@@ -830,6 +830,22 @@ struct scmi_pinctrl_proto_ops {int (*pin_free)(const struct scmi_protocol_handle *ph,u32 pin); }; +#define SCMI_TLM_GRP_INVALID 0xFFFFFFFF + +struct scmi_telemetry_grp_info { +unsigned int grp_id; +unsigned int num_des; +unsigned int num_intervals; +}; + +struct scmi_telemetry_group { +bool enabled; +bool tstamp_enabled; +unsigned int *des; +char *des_str; +struct scmi_telemetry_grp_info *info; +}; +struct scmi_telemetry_de_info {unsigned int id;unsigned int grp_id;
@@ -852,6 +868,7 @@ struct scmi_telemetry_de {bool enabled;bool tstamp_enabled;struct scmi_telemetry_de_info *info; +struct scmi_telemetry_group *grp; };struct scmi_telemetry_res_info {
@@ -859,6 +876,13 @@ struct scmi_telemetry_res_info {unsigned int num_des;struct scmi_telemetry_de **des;struct scmi_telemetry_de_info *dei_store; +unsigned int num_groups; +/* +* Allocated at max capacity and populated dynamically +* at runtime, cannot be counted_by num_groups. +*/ +struct scmi_telemetry_group *grps; +struct scmi_telemetry_grp_info *grps_store; };struct scmi_telemetry_base_info {
Signature · 1 line
2.54.0
Re: [PATCH v15 11/35] firmware: arm_scmi: Add Telemetry groups initialization
Quoted 3 lines · On Mon, 9 Nov 2026 09:11:57 +0000 Cristian Marussi <[email protected]>…
Allocate and initialize Telemetry Groups from base DataEvent data.
Signed-off-by Cristian Marussi <[email protected]>
drivers/firmware/arm_scmi/telemetry.c static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph, · line 573
Quoted 2 lines
drivers/firmware/arm_scmi/telemetry.c +2
-0+static int +scmi_telemetry_de_groups_init (struct telemetry_info *ti)
one line maybe? I guess follow local style.
drivers/firmware/arm_scmi/telemetry.c static void scmi_telemetry_resources_free(struct telemetry_info *ti) · line 837
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+{ +struct scmi_telemetry_res_info *rinfo;@@ -701,6 +833,9 @@ static void scmi_telemetry_resources_free(struct telemetry_info *ti)kfree (rinfo->des);kfree (rinfo->dei_store); +kfree (rinfo->grps); +kfree (rinfo->grps_store);
Same as earlier about reverse order being most consistent way to do release of stuff.
Quoted 17 lines
drivers/firmware/arm_scmi/telemetry.c +3
-0+kfree (rinfo);dev_dbg (ti->ph->dev,"SCMI Telemetry resources freed for instance\n" );@@ -722,10 +857,25 @@ static void scmi_telemetry_tdes_cleanup(struct telemetry_info *ti)xa_destroy (&ti->xa_des); } +static void scmi_telemetry_instance_cleanup (void *arg) {struct telemetry_info *ti = arg; +scmi_telemetry_groups_free (ti->rinfo);scmi_telemetry_tdes_cleanup (ti);scmi_telemetry_resources_free (ti); }
Cristian Marussi
reviewerPATCH v15 12/35 firmware: arm_scmi: Add Telemetry update intervals enumeration
In reply to Cristian MarussiAdd support to enumerate available Telemetry update intervals.
---
v14 --> v15
- removed unused dev reference in interval parsing
- added missing initialization of intrvs->grp_id
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains the update_intervals enumeration routines
--- drivers/firmware/arm_scmi/telemetry.c | 198 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 21 +++ 2 files changed, 219 insertions(+)
drivers/firmware/arm_scmi/telemetry.c +198 -0
@@ -92,6 +92,21 @@ struct scmi_msg_resp_telemetry_de_description {struct scmi_de_desc desc[]; }; +struct scmi_msg_telemetry_update_intervals { +__le32 index; +__le32 group_identifier; +#define ALL_DES_NO_GROUP 0x0 +#define SPECIFIC_GROUP_DES 0x1 +#define ALL_DES_ANY_GROUP 0x2 +__le32 flags; +}; + +struct scmi_msg_resp_telemetry_update_intervals { +__le32 flags; +#define INTERVALS_DISCRETE (x) (le32_get_bits((x), BIT(12)) == 0) +__le32 intervals[]; +}; +#define IS_TDE_TSTAMP_TYPE (__t, __ttype) ((__t)->ts_type == (__ttype))#define IS_TDE_TS_NONE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_NONE))#define IS_TDE_TS_LINE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_LINE))
@@ -693,6 +708,176 @@ static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti)return scmi_telemetry_de_groups_init (ti); } +struct scmi_tlm_ivl_priv { +struct scmi_telemetry_intervals **intrvs; +unsigned int grp_id; +unsigned int flags; +}; + +static void iter_intervals_prepare_message (void *message, +unsigned int desc_index, +const void *priv) +{ +struct scmi_msg_telemetry_update_intervals *msg = message; +const struct scmi_tlm_ivl_priv *p = priv; + + msg->index =cpu_to_le32 (desc_index); + msg->group_identifier =cpu_to_le32 (p->grp_id); + msg->flags =cpu_to_le32 (FIELD_PREP (GENMASK (3 ,0 ), p->flags)); +} + +static int iter_intervals_update_state (struct scmi_iterator_state *st, +const void *response,void *priv) +{ +const struct scmi_msg_resp_telemetry_update_intervals *r = response; + + st->num_returned =le32_get_bits (r->flags,GENMASK (11 ,0 )); + st->num_remaining =le32_get_bits (r->flags,GENMASK (31 ,16 )); + +if (st->rx_len < (sizeof (*r) +sizeof (r->intervals[0 ]) * st->num_returned)) +return -EINVAL ; + +/* +* total intervals is not declared previously anywhere so we +* assume it's returned+remaining on first call. +*/ +if (!st->max_resources) { +struct scmi_tlm_ivl_priv *p = priv; +struct scmi_telemetry_intervals *intrvs; +bool discrete; +int inum; + + discrete =INTERVALS_DISCRETE (r->flags); +/* Check consistency on first call */ +if (!discrete && (st->num_returned !=3 || st->num_remaining !=0 )) +return -EINVAL ; + + inum = st->num_returned + st->num_remaining; + intrvs =kzalloc_flex (*intrvs, update_intervals, inum); +if (!intrvs) +return -ENOMEM ; + + intrvs->num_intervals = inum; + intrvs->discrete = discrete; + intrvs->grp_id = p->grp_id; + st->max_resources = intrvs->num_intervals; + + *p->intrvs = intrvs; + } + +return 0 ; +} + +static int +iter_intervals_process_response (const struct scmi_protocol_handle *ph, +const void *response, +struct scmi_iterator_state *st,void *priv) +{ +const struct scmi_msg_resp_telemetry_update_intervals *r = response; +struct scmi_tlm_ivl_priv *p = priv; +struct scmi_telemetry_intervals *intrvs = *p->intrvs; +unsigned int idx = st->loop_idx; + + intrvs->update_intervals[st->desc_index + idx] = +le32_to_cpu (r->intervals[idx]); + +return 0 ; +} + +static int +scmi_tlm_enumerate_update_intervals (struct telemetry_info *ti, +struct scmi_telemetry_intervals **intervals, +int grp_id,unsigned int flags) +{ +struct scmi_iterator_ops ops = { + .prepare_message = iter_intervals_prepare_message, + .update_state = iter_intervals_update_state, + .process_response = iter_intervals_process_response, + }; +const struct scmi_protocol_handle *ph = ti->ph; +struct scmi_telemetry_intervals *intrvs =NULL ; +struct scmi_tlm_ivl_priv ipriv = { + .grp_id = grp_id, + .intrvs = &intrvs, + .flags = flags, + }; +void *iter; +int ret; + + iter = ph->hops->iter_response_init (ph, &ops,0 , +TELEMETRY_LIST_UPDATE_INTERVALS , +sizeof (struct scmi_msg_telemetry_update_intervals ), + &ipriv); +if (IS_ERR (iter)) +return PTR_ERR (iter); + +/* Cleanup iterator inner allocations on failure */ + ret = ph->hops->iter_response_run (iter); +if (ret) { +kfree (intrvs); +return ret; + } + + *intervals = intrvs; + +return 0 ; +} + +static int +scmi_telemetry_enumerate_groups_intervals (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; + +if (!ti->info.per_group_config_support) +return 0 ; + +/* Get rinfo without triggering a recursive enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + +for (int id =0 ; id < rinfo->num_groups; id++) { +int ret; + + ret =scmi_tlm_enumerate_update_intervals (ti, + &rinfo->grps[id].intervals, + id,SPECIFIC_GROUP_DES ); +if (ret) +return ret; + + rinfo->grps_store[id].num_intervals = + rinfo->grps[id].intervals->num_intervals; + } + +return 0 ; +} + +static void scmi_telemetry_intervals_free (void *interval) +{ +kfree (interval); +} + +static int +scmi_telemetry_enumerate_common_intervals (struct telemetry_info *ti) +{ +unsigned int flags; +int ret; + + flags = !ti->info.per_group_config_support ? +ALL_DES_ANY_GROUP :ALL_DES_NO_GROUP ; + + ret =scmi_tlm_enumerate_update_intervals (ti, &ti->info.intervals, +SCMI_TLM_GRP_INVALID , flags); +if (ret) +return ret; + +/* A copy for UAPI access... */ + ti->info.base.num_intervals = ti->info.intervals->num_intervals; + +/* Delegate freeing of allocated intervals to unbind time */ +return devm_add_action_or_reset (ti->ph->dev, + scmi_telemetry_intervals_free, + ti->info.intervals); +} +static const struct scmi_telemetry_info *scmi_telemetry_info_get (const struct scmi_protocol_handle *ph) {
@@ -868,6 +1053,7 @@ static void scmi_telemetry_groups_free(struct scmi_telemetry_res_info *rinfo)kfree (grp->des);kfree (grp->des_str); +kfree (grp->intervals); } }
@@ -932,6 +1118,12 @@ scmi_telemetry_resources_enumerate(struct telemetry_info *ti) goto done; } + ret =scmi_telemetry_enumerate_groups_intervals (ti); +if (ret) { +dev_err (dev,FW_BUG "Cannot fully enumerate group intervals. Degraded system.\n" ); + goto done; + } +/* Get rinfo without triggering a recursive enumeration */ rinfo =__scmi_telemetry_resources_get (ti); rinfo->fully_enumerated = rinfo->num_des == ti->info.base.num_des;
@@ -1007,6 +1199,12 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph)return ret; } + ret =scmi_telemetry_enumerate_common_intervals (ti); +if (ret) { +dev_err (dev,FW_BUG "Cannot enumerate update intervals. Abort.\n" ); +return ret; + } + ti->info.base.version = ph->version;return ph->set_priv (ph, ti);
include/linux/scmi_protocol.h +21 -0
@@ -830,8 +830,25 @@ struct scmi_pinctrl_proto_ops {int (*pin_free)(const struct scmi_protocol_handle *ph,u32 pin); }; +#define SCMI_TLM_GET_UPDATE_INTERVAL_SECS (x) (FIELD_GET(GENMASK(20, 5), (x))) +#define SCMI_TLM_GET_UPDATE_INTERVAL_EXP (x) (sign_extend32((x), 4)) + +#define SCMI_TLM_GET_UPDATE_INTERVAL (x) (FIELD_GET(GENMASK(20, 0), (x))) +#define SCMI_TLM_BUILD_UPDATE_INTERVAL (s, e) \ + (FIELD_PREP(GENMASK(20, 5), (s)) | FIELD_PREP(GENMASK(4, 0), (e))) +#define SCMI_TLM_GRP_INVALID 0xFFFFFFFF +struct scmi_telemetry_intervals { +unsigned int grp_id; +bool discrete; +unsigned int num_intervals; +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_LOW 0 +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_HIGH 1 +#define SCMI_TLM_UPDATE_INTVL_SEGMENT_STEP 2 + unsigned int update_intervals[] __counted_by(num_intervals); +}; +struct scmi_telemetry_grp_info {unsigned int grp_id;unsigned int num_des;
@@ -844,6 +861,8 @@ struct scmi_telemetry_group {unsigned int *des;char *des_str;struct scmi_telemetry_grp_info *info; +unsigned int active_update_interval; +struct scmi_telemetry_intervals *intervals; };struct scmi_telemetry_de_info {
@@ -901,6 +920,8 @@ struct scmi_telemetry_info {bool reset_support;bool fc_support;struct scmi_telemetry_base_info base; +unsigned int active_update_interval; +struct scmi_telemetry_intervals *intervals; };/**
Signature · 1 line
2.54.0
Re: [PATCH v15 12/35] firmware: arm_scmi: Add Telemetry update intervals enumeration
Quoted 20 lines · On Mon, 9 Nov 2026 09:11:58 +0000 Cristian Marussi <[email protected]>…
Add support to enumerate available Telemetry update intervals.
Signed-off-by Cristian Marussi <[email protected]>---
v14 --> v15
- removed unused dev reference in interval parsing
- added missing initialization ofintrvs->grp_id
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains theupdate_intervalsenumeration routines--- drivers/firmware/arm_scmi/telemetry.c | 198 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 21 +++ 2 files changed, 219 insertions(+)drivers/firmware/arm_scmi/telemetry.c +0
-0
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_de_description { · line 100
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +6
-0@@ -92,6 +92,21 @@ struct scmi_msg_resp_telemetry_de_description {struct scmi_de_desc desc[]; }; +struct scmi_msg_telemetry_update_intervals { +__le32 index; +__le32 group_identifier; +#define ALL_DES_NO_GROUP 0x0 +#define SPECIFIC_GROUP_DES 0x1 +#define ALL_DES_ANY_GROUP 0x2
For previous defines like this they have followed the value they are in. So I think these belong after flags. (That fits with common practice for this sort of thing which is why I looked).
Quoted 13 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+__le32 flags; +}; + +struct scmi_msg_resp_telemetry_update_intervals { +__le32 flags; +#define INTERVALS_DISCRETE (x) (le32_get_bits((x), BIT(12)) == 0) +__le32 intervals[]; +}; +#define IS_TDE_TSTAMP_TYPE (__t, __ttype) ((__t)->ts_type == (__ttype))#define IS_TDE_TS_NONE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_NONE))#define IS_TDE_TS_LINE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_LINE))
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti) · line 714
Quoted 7 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0@@ -693,6 +708,176 @@ static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti)return scmi_telemetry_de_groups_init (ti); } +struct scmi_tlm_ivl_priv { +struct scmi_telemetry_intervals **intrvs; +unsigned int grp_id; +unsigned int flags;
Given these are fixed size, maybe u32 for each of them just to make sure that matches up.
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti) · line 726
Quoted 12 lines
drivers/firmware/arm_scmi/telemetry.c +12
-0+}; + +static void iter_intervals_prepare_message (void *message, +unsigned int desc_index, +const void *priv) +{ +struct scmi_msg_telemetry_update_intervals *msg = message; +const struct scmi_tlm_ivl_priv *p = priv; + + msg->index =cpu_to_le32 (desc_index); + msg->group_identifier =cpu_to_le32 (p->grp_id); + msg->flags =cpu_to_le32 (FIELD_PREP (GENMASK (3 ,0 ), p->flags));
Need the mask? It's valid but I'd expect the protection on sanity to be at the point of p->flags being written rather than here.
Quoted 46 lines
drivers/firmware/arm_scmi/telemetry.c +46
-0+} + +static int iter_intervals_update_state (struct scmi_iterator_state *st, +const void *response,void *priv) +{ +const struct scmi_msg_resp_telemetry_update_intervals *r = response; + + st->num_returned =le32_get_bits (r->flags,GENMASK (11 ,0 )); + st->num_remaining =le32_get_bits (r->flags,GENMASK (31 ,16 )); + +if (st->rx_len < (sizeof (*r) +sizeof (r->intervals[0 ]) * st->num_returned)) +return -EINVAL ; + +/* +* total intervals is not declared previously anywhere so we +* assume it's returned+remaining on first call. +*/ +if (!st->max_resources) { +struct scmi_tlm_ivl_priv *p = priv; +struct scmi_telemetry_intervals *intrvs; +bool discrete; +int inum; + + discrete =INTERVALS_DISCRETE (r->flags); +/* Check consistency on first call */ +if (!discrete && (st->num_returned !=3 || st->num_remaining !=0 )) +return -EINVAL ; + + inum = st->num_returned + st->num_remaining; + intrvs =kzalloc_flex (*intrvs, update_intervals, inum); +if (!intrvs) +return -ENOMEM ; + + intrvs->num_intervals = inum; + intrvs->discrete = discrete; + intrvs->grp_id = p->grp_id; + st->max_resources = intrvs->num_intervals; + + *p->intrvs = intrvs; + } + +return 0 ; +} + +static int +iter_intervals_process_response(const struct scmi_protocol_handle *ph,
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti) · line 782
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+const void *response, + struct scmi_iterator_state *st, void *priv) +{ + const struct scmi_msg_resp_telemetry_update_intervals *r = response; +struct scmi_tlm_ivl_priv *p = priv; +struct scmi_telemetry_intervals *intrvs = *p->intrvs; +unsigned int idx = st->loop_idx; + + intrvs->update_intervals[st->desc_index + idx] = +le32_to_cpu (r->intervals[idx]);
I'd go a bit long and put that on one line.
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_de_descriptors_get(struct telemetry_info *ti) · line 865
Quoted 12 lines
drivers/firmware/arm_scmi/telemetry.c +11
-0+ +return 0 ; +} +static int +scmi_telemetry_enumerate_common_intervals (struct telemetry_info *ti) +{ +unsigned int flags; +int ret; + + flags = !ti->info.per_group_config_support ? +ALL_DES_ANY_GROUP :ALL_DES_NO_GROUP ;
Why not flip the condition and reverse the values? This thing is weird enough I'd add a comment or rename them to be more helpful. ALL_DES_THAT_HAVE_NO_GROUP for example.
Quoted 16 lines
drivers/firmware/arm_scmi/telemetry.c +14
-0+ + ret =scmi_tlm_enumerate_update_intervals (ti, &ti->info.intervals, +SCMI_TLM_GRP_INVALID , flags); +if (ret) +return ret; + +/* A copy for UAPI access... */ + ti->info.base.num_intervals = ti->info.intervals->num_intervals; + +/* Delegate freeing of allocated intervals to unbind time */ +return devm_add_action_or_reset (ti->ph->dev, + scmi_telemetry_intervals_free, + ti->info.intervals); +}/**
Cristian Marussi
reviewerPATCH v15 13/35 firmware: arm_scmi: Add Telemetry SHMTI areas enumeration
In reply to Cristian MarussiAdd Telemetry support to enumerate SHMTI areas.
---
v14 --> v15
- fix prologue/epilogue endianity fields
- rework SHMTI initialization and allocation
- add snapshot suppport data structures
- added alignment checks on SHMTII length size
- fixed error string formatting
- warn if less than expected SHMTIs are enumerated
- improve memory area sanity checks
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains the SHMTI areas enumeration routines
--- drivers/firmware/arm_scmi/telemetry.c | 312 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 59 +++++ 2 files changed, 371 insertions(+)
drivers/firmware/arm_scmi/telemetry.c +312 -0
@@ -16,6 +16,7 @@#include <linux/limits.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/sizes.h> #include <linux/slab.h> #include <linux/sprintf.h> #include <linux/string.h>
@@ -56,6 +57,19 @@ struct scmi_msg_resp_telemetry_protocol_attributes {__le32 default_blk_ts_rate; }; +struct scmi_shmti_desc { +__le32 id; +__le32 addr_low; +__le32 addr_high; +__le32 length; +__le32 flags; +}; + +struct scmi_msg_resp_telemetry_shmti_list { +__le32 num_shmti; +struct scmi_shmti_desc desc[]; +}; +struct de_desc_fastchannel {__le32 addr_low;__le32 addr_high;
@@ -107,6 +121,72 @@ struct scmi_msg_resp_telemetry_update_intervals {__le32 intervals[]; }; +/* TDCF */ + +#define _I (__a) (ioread32((void __iomem *)(__a))) + +struct prlg { +__le32 sign_start; +#define SIGNATURE_START 0x5442474E/* TBGN */ +__le32 match_start; +__le32 num_qwords; +__le32 hdr_meta_1; +#define TDCF_REVISION_GET (x) (le32_get_bits((x)->hdr_meta_1, GENMASK(7, 0))) +}; + +struct eplg { +__le32 match_end; +__le32 sign_end; +#define SIGNATURE_END 0x54454E44/* TEND */ +}; + +#define TDCF_EPLG_SZ (sizeof(struct eplg)) + +struct tdcf { +struct prlg prlg; +unsigned char payld[]; +}; + +#define SHMTI_MIN_SIZE (sizeof(struct tdcf) + TDCF_EPLG_SZ) +/* An arbitrary upper bound to avoid crazy allocations driven by buggy FW */ +#define SHMTI_MAX_SIZE SZ_64K + +#define TDCF_START_SIGNATURE (x) (_I(&((x)->prlg.sign_start))) +#define TDCF_END_SIGNATURE (e) (_I(&((e)->sign_end))) + +struct telemetry_shmti ; + +struct shmti_snap { +struct telemetry_shmti *shmti; +u32 magic; +u8 base[]__aligned(8) ; +}; + +struct telemetry_shmti { +u32 flags; +void __iomem *base; +struct scmi_telemetry_shmti_info info; +/* Protect srcu operation */ +struct mutex lock; +struct srcu_struct srcu; +struct shmti_snap *snaps[2 ]; +struct shmti_snap __rcu *cur; +/* Mutex to protect @xa_lines */ +struct mutex lines_mtx; +struct xarray xa_lines; +#define PAYLD_HASH_BITS 4 +struct mutex payld_locks[1 <<PAYLD_HASH_BITS ]; +}; + +#define SHMTI_EPLG (s) \ + ({ \ + struct telemetry_shmti *_s = (s); \ + struct eplg __iomem *_eplg; \ + \ + _eplg = _s->base + _s->info.len - TDCF_EPLG_SZ; \ + (_eplg); \ + }) +#define IS_TDE_TSTAMP_TYPE (__t, __ttype) ((__t)->ts_type == (__ttype))#define IS_TDE_TS_NONE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_NONE))#define IS_TDE_TS_LINE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_LINE))
@@ -135,6 +215,7 @@ struct telemetry_de {u32 ts_rate;bool enumerated;void __iomem *base; +struct eplg __iomem *eplg;u32 sid;u32 offset;/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */
@@ -153,6 +234,7 @@ struct telemetry_info {unsigned int num_des_tstamp;unsigned int default_blk_ts_rate;const struct scmi_protocol_handle *ph; +struct telemetry_shmti *shmti;struct telemetry_de *tdes;struct xarray xa_des;/* Mutex to protect access to @free_des */
@@ -878,6 +960,211 @@ scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) ti->info.intervals); } +static int iter_shmti_update_state (struct scmi_iterator_state *st, +const void *response,void *priv) +{ +const struct scmi_msg_resp_telemetry_shmti_list *r = response; + + st->num_returned =le32_get_bits (r->num_shmti,GENMASK (15 ,0 )); + st->num_remaining =le32_get_bits (r->num_shmti,GENMASK (31 ,16 )); + +if (st->rx_len < (sizeof (*r) +sizeof (r->desc[0 ]) * st->num_returned)) +return -EINVAL ; + +return 0 ; +} + +static inline int +scmi_telemetry_shmti_validate (struct device *dev,struct telemetry_shmti *shmti) +{ +struct tdcf __iomem *tdcf = shmti->base; +u32 sign_start, sign_end; + + sign_start =TDCF_START_SIGNATURE (tdcf); + sign_end =TDCF_END_SIGNATURE (SHMTI_EPLG (shmti)); + +if (sign_start !=SIGNATURE_START || sign_end !=SIGNATURE_END ) { +dev_err (dev, +"BAD signature for SHMTI ID:%u @phys:%pa - START:0x%08X END:0x%08X\n" , + shmti->info.sid, &shmti->info.phys, sign_start, sign_end); +return -EINVAL ; + } + +return 0 ; +} + +static int iter_shmti_process_response (const struct scmi_protocol_handle *ph, +const void *response, +struct scmi_iterator_state *st, +void *priv) +{ +const struct scmi_msg_resp_telemetry_shmti_list *r = response; +struct telemetry_info *ti = priv; +struct telemetry_shmti *shmti; +const struct scmi_shmti_desc *desc; +void __iomem *addr; +phys_addr_t phys_addr; +size_t len; +int ret; + + desc = &r->desc[st->loop_idx]; + shmti = &ti->shmti[st->desc_index + st->loop_idx]; + + shmti->info.sid =le32_to_cpu (desc->id); + shmti->flags =le32_to_cpu (desc->flags); + + ret =scmi_telemetry_area_check (ph->dev,"SHMTI" , +le32_to_cpu (desc->addr_low), +le32_to_cpu (desc->addr_high), +le32_to_cpu (desc->length), + &phys_addr, &len); +if (ret) { +dev_err (ph->dev,"Malformed SHMTI ID:%u. Abort.\n" , + shmti->info.sid); +return ret; + } + +if (len <SHMTI_MIN_SIZE || len >SHMTI_MAX_SIZE ) { +dev_err (ph->dev, +FW_BUG "Invalid length for SHMTI ID:%u len:%zu. Abort.\n" , + shmti->info.sid, len); +return -EINVAL ; + } + +/* SHMTI base phys_addr SHOULD be 64bit aligned by the SCMI spec */ +if (!IS_ALIGNED (phys_addr,8 )) +dev_warn (ph->dev, +"SHMTI ID:%u is NOT 64bit aligned (paddr:0x%llX)\n" , + shmti->info.sid, phys_addr); + + addr =devm_ioremap (ph->dev, phys_addr, len); +if (!addr) +return -EADDRNOTAVAIL ; + + shmti->base = addr; + shmti->info.len = len; + shmti->info.phys = phys_addr; + shmti->info.offset = shmti->info.phys & ~PAGE_MASK ; + + ret =scmi_telemetry_shmti_validate (ph->dev, shmti); +if (ret) +return ret; + + ti->num_shmti++; + +return 0 ; +} + +static int scmi_telemetry_shmti_list (const struct scmi_protocol_handle *ph, +struct telemetry_info *ti) +{ +struct scmi_iterator_ops ops = { + .prepare_message = iter_tlm_prepare_message, + .update_state = iter_shmti_update_state, + .process_response = iter_shmti_process_response, + }; +int num_shmti; +void *iter; +int ret; + +/* Save and zero...calculate real number of returned */ + num_shmti = ti->num_shmti; + ti->num_shmti =0 ; + iter = ph->hops->iter_response_init (ph, &ops, num_shmti, +TELEMETRY_LIST_SHMTI , +sizeof (u32), ti); +if (IS_ERR (iter)) +return PTR_ERR (iter); + + ret = ph->hops->iter_response_run (iter); +if (ret) +return ret; + +if (num_shmti != ti->num_shmti) +dev_warn (ph->dev, +FW_BUG "SHMTI enumeration returned %u items, expected %u\n" , + ti->num_shmti, num_shmti); + +return 0 ; +} + +static void scmi_telemetry_shmti_cleanup (void *arg) +{ +struct telemetry_shmti *shmti = arg; + +cleanup_srcu_struct (&shmti->srcu); + +kvfree (shmti->snaps[0 ]); +kvfree (shmti->snaps[1 ]); + +xa_destroy (&shmti->xa_lines); +} + +static int scmi_telemetry_shmti_init (struct device *dev, +struct telemetry_shmti *shmti) +{ +int ret; + + struct shmti_snap *s0 __free(kvfree) = +kvzalloc_flex (*s0, base, shmti->info.len); +if (!s0) +return -ENOMEM ; + + struct shmti_snap *s1 __free(kvfree) = +kvzalloc_flex (*s1, base, shmti->info.len); +if (!s1) +return -ENOMEM ; + + ret =init_srcu_struct (&shmti->srcu); +if (ret) +return ret; + +for (int i =0 ; i <1 <<PAYLD_HASH_BITS ; i++) +mutex_init (&shmti->payld_locks[i]); + +mutex_init (&shmti->lock); +RCU_INIT_POINTER (shmti->cur,NULL ); + +mutex_init (&shmti->lines_mtx); +xa_init (&shmti->xa_lines); + + s0->shmti = shmti; + shmti->snaps[0 ] =no_free_ptr (s0); + + s1->shmti = shmti; + shmti->snaps[1 ] =no_free_ptr (s1); + +return devm_add_action_or_reset (dev, scmi_telemetry_shmti_cleanup, shmti); +} + +static int scmi_telemetry_enumerate_shmti (struct telemetry_info *ti) +{ +const struct scmi_protocol_handle *ph = ti->ph; +int ret; + +if (!ti->num_shmti) +return 0 ; + + ret =scmi_telemetry_shmti_list (ph, ti); +if (ret) { +dev_err (ph->dev,"Cannot get SHMTI list descriptors" ); +return ret; + } + +for (int i =0 ; i < ti->num_shmti; i++) { + ret =scmi_telemetry_shmti_init (ph->dev, &ti->shmti[i]); +if (ret) +return ret; + + ti->info.shmtis[i] = &ti->shmti[i].info; + } + +/* Expose the fully initialized SHMTI infos */ +smp_store_release (&ti->info.base.num_shmtis, ti->num_shmti); + +return 0 ; +} +static const struct scmi_telemetry_info *scmi_telemetry_info_get (const struct scmi_protocol_handle *ph) {
@@ -931,7 +1218,20 @@ static const struct scmi_telemetry_proto_ops tlm_proto_ops = {* Return: 0 on Success, errno otherwise */ static int scmi_telemetry_resources_alloc (struct telemetry_info *ti) + { +/* Array to hold SHMTIs descriptors */ + struct telemetry_shmti *shmtis __free(kfree) = +kzalloc_objs (*shmtis, ti->num_shmti); +if (!shmtis) +return -ENOMEM ; + +/* Array of initialized SHMTIs references */ + struct scmi_telemetry_shmti_info **shmtis_info __free(kfree) = +kzalloc_objs (*shmtis_info, ti->num_shmti); +if (!shmtis_info) +return -ENOMEM ; +/* Array to hold pointers to discovered DEs */ struct scmi_telemetry_de **des __free(kfree) =kzalloc_objs (*des, ti->info.base.num_des);
@@ -984,6 +1284,9 @@ static int scmi_telemetry_resources_alloc(struct telemetry_info *ti)INIT_LIST_HEAD (&ti->fcs_des); + ti->shmti =no_free_ptr (shmtis); + ti->info.shmtis =no_free_ptr (shmtis_info); + ti->tdes =no_free_ptr (tdes); rinfo->des =no_free_ptr (des);
@@ -1013,6 +1316,9 @@ static void scmi_telemetry_resources_free(struct telemetry_info *ti)/* Ensure rinfo is no more accessible upfront */ smp_store_release (&ti->rinfo,NULL ); +kfree (ti->info.shmtis); +kfree (ti->shmti); +kfree (ti->tdes);kfree (rinfo->des);
@@ -1205,6 +1511,12 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph)return ret; } + ret =scmi_telemetry_enumerate_shmti (ti); +if (ret) { +dev_err (dev,FW_BUG "Cannot enumerate SHMTIs. Abort.\n" ); +return ret; + } + ti->info.base.version = ph->version;return ph->set_priv (ph, ti);
include/linux/scmi_protocol.h +59 -0
77 lines not on this page · raw message
Signature · 1 line
2.54.0
Re: [PATCH v15 13/35] firmware: arm_scmi: Add Telemetry SHMTI areas enumeration
Quoted 1 line · On Mon, 9 Nov 2026 09:11:59 +0000 Cristian Marussi <[email protected]>…
Add Telemetry support to enumerate SHMTI areas.
I'd be tempted to leave the local data structures like snaps to be introduced only when code actually uses them.
Quoted 24 lines
Signed-off-by Cristian Marussi <[email protected]>---
v14 --> v15
- fix prologue/epilogue endianity fields
- rework SHMTI initialization and allocation
- add snapshot suppport data structures
- added alignment checks on SHMTII length size
- fixed error string formatting
- warn if less than expected SHMTIs are enumerated
- improve memory area sanity checks
v13 --> v14
- splitted out of the old Basic Telemetry patch, contains the SHMTI areas enumeration routines--- drivers/firmware/arm_scmi/telemetry.c | 312 ++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 59 +++++ 2 files changed, 371 insertions(+)drivers/firmware/arm_scmi/telemetry.c +0
-0
drivers/firmware/arm_scmi/telemetry.c
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +3
-0@@ -16,6 +16,7 @@#include <linux/limits.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/sizes.h> #include <linux/slab.h> #include <linux/sprintf.h> #include <linux/string.h> +struct scmi_msg_resp_telemetry_shmti_list { +__le32 num_shmti;
Add a comment that this is not actually the number of shmti.
Not confusing spec name at all. Maybe just put a pair of
extraction macros right after it like you've had for some of the other similar cases and that will act as documentation that not all is what it seems.
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_update_intervals { · line 126
Quoted 13 lines
drivers/firmware/arm_scmi/telemetry.c +6
-0+struct scmi_shmti_desc desc[]; +}; +struct de_desc_fastchannel {__le32 addr_low;__le32 addr_high;@@ -107,6 +121,72 @@ struct scmi_msg_resp_telemetry_update_intervals {__le32 intervals[]; }; +/* TDCF */ + +#define _I (__a) (ioread32((void __iomem *)(__a)))
Is that really worth doing? I think I'd rather see some long lines with that expanded out.
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_update_intervals { · line 134
Quoted 8 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+ +struct prlg { +__le32 sign_start; +#define SIGNATURE_START 0x5442474E/* TBGN */ +__le32 match_start; +__le32 num_qwords; +__le32 hdr_meta_1; +#define TDCF_REVISION_GET (x) (le32_get_bits((x)->hdr_meta_1, GENMASK(7, 0)))
Outer brackets needed?
Quoted 25 lines
drivers/firmware/arm_scmi/telemetry.c +21
-0+}; + +struct eplg { +__le32 match_end; +__le32 sign_end; +#define SIGNATURE_END 0x54454E44/* TEND */ +}; + +#define TDCF_EPLG_SZ (sizeof(struct eplg)) + +struct tdcf { +struct prlg prlg; +unsigned char payld[]; +}; + +#define SHMTI_MIN_SIZE (sizeof(struct tdcf) + TDCF_EPLG_SZ) +/* An arbitrary upper bound to avoid crazy allocations driven by buggy FW */ +#define SHMTI_MAX_SIZE SZ_64K + +#define TDCF_START_SIGNATURE (x) (_I(&((x)->prlg.sign_start))) +#define TDCF_END_SIGNATURE (e) (_I(&((e)->sign_end)))/* Mutex to protect access to @free_des */
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) · line 969
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +7
-0@@ -878,6 +960,211 @@ scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) ti->info.intervals); } +static int iter_shmti_update_state (struct scmi_iterator_state *st, +const void *response,void *priv) +{ +const struct scmi_msg_resp_telemetry_shmti_list *r = response; + + st->num_returned =le32_get_bits (r->num_shmti,GENMASK (15 ,0 )); + st->num_remaining =le32_get_bits (r->num_shmti,GENMASK (31 ,16 ));
I'd move those two into defines above, just to document that num_shmti is more complex than it seems at first glance. Also for general consistency.
Quoted 21 lines
drivers/firmware/arm_scmi/telemetry.c +20
-0+ +if (st->rx_len < (sizeof (*r) +sizeof (r->desc[0 ]) * st->num_returned)) +return -EINVAL ; + +return 0 ; +} + +static int iter_shmti_process_response (const struct scmi_protocol_handle *ph, +const void *response, +struct scmi_iterator_state *st, +void *priv) +{ +const struct scmi_msg_resp_telemetry_shmti_list *r = response; +struct telemetry_info *ti = priv; +struct telemetry_shmti *shmti; +const struct scmi_shmti_desc *desc; +void __iomem *addr; +phys_addr_t phys_addr; +size_t len; +int ret;
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) · line 1018
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ + desc = &r->desc[st->loop_idx]; + shmti = &ti->shmti[st->desc_index + st->loop_idx]; + + shmti->info.sid =le32_to_cpu (desc->id); + shmti->flags =le32_to_cpu (desc->flags); + + ret = scmi_telemetry_area_check(ph->dev, "SHMTI", + le32_to_cpu(desc->addr_low), + le32_to_cpu(desc->addr_high),
Same as before. I'd do the build of phys_addr and len outside the call. Keep it to checking validity as it's name suggests.
Quoted 70 lines
drivers/firmware/arm_scmi/telemetry.c +38
-0+le32_to_cpu (desc->length), + &phys_addr, &len); +if (ret) { +dev_err (ph->dev,"Malformed SHMTI ID:%u. Abort.\n" , + shmti->info.sid); +return ret; + } + +if (len <SHMTI_MIN_SIZE || len >SHMTI_MAX_SIZE ) { +dev_err (ph->dev, +FW_BUG "Invalid length for SHMTI ID:%u len:%zu. Abort.\n" , + shmti->info.sid, len); +return -EINVAL ; + } + +/* SHMTI base phys_addr SHOULD be 64bit aligned by the SCMI spec */ +if (!IS_ALIGNED (phys_addr,8 )) +dev_warn (ph->dev, +"SHMTI ID:%u is NOT 64bit aligned (paddr:0x%llX)\n" , + shmti->info.sid, phys_addr); + + addr =devm_ioremap (ph->dev, phys_addr, len); +if (!addr) +return -EADDRNOTAVAIL ; + + shmti->base = addr; + shmti->info.len = len; + shmti->info.phys = phys_addr; + shmti->info.offset = shmti->info.phys & ~PAGE_MASK ; + + ret =scmi_telemetry_shmti_validate (ph->dev, shmti); +if (ret) +return ret; + + ti->num_shmti++; + +return 0 ; +}include/linux/scmi_protocol.h +21
-0@@ -830,6 +830,57 @@ struct scmi_pinctrl_proto_ops {int (*pin_free)(const struct scmi_protocol_handle *ph,u32 pin); }; + +enum scmi_telemetry_compo_type { +SCMI_TLM_COMPO_TYPE_USPECIFIED , +SCMI_TLM_COMPO_TYPE_CPU , +SCMI_TLM_COMPO_TYPE_CLUSTER , +SCMI_TLM_COMPO_TYPE_GPU , +SCMI_TLM_COMPO_TYPE_NPU , +SCMI_TLM_COMPO_TYPE_INTERCONNECT , +SCMI_TLM_COMPO_TYPE_MEM_CNTRL , +SCMI_TLM_COMPO_TYPE_L1_CACHE , +SCMI_TLM_COMPO_TYPE_L2_CACHE , +SCMI_TLM_COMPO_TYPE_L3_CACHE , +SCMI_TLM_COMPO_TYPE_LL_CACHE , +SCMI_TLM_COMPO_TYPE_SYS_CACHE , +SCMI_TLM_COMPO_TYPE_DISP_CNTRL , +SCMI_TLM_COMPO_TYPE_IPU , +SCMI_TLM_COMPO_TYPE_CHIPLET , +SCMI_TLM_COMPO_TYPE_PACKAGE , +SCMI_TLM_COMPO_TYPE_SOC , +SCMI_TLM_COMPO_TYPE_SYSTEM , +SCMI_TLM_COMPO_TYPE_SMCU ,
include/linux/scmi_protocol.h struct scmi_pinctrl_proto_ops { · line 864
Quoted 10 lines
include/linux/scmi_protocol.h +10
-0+SCMI_TLM_COMPO_TYPE_ACCEL , +SCMI_TLM_COMPO_TYPE_BATTERY , +SCMI_TLM_COMPO_TYPE_CHARGER , +SCMI_TLM_COMPO_TYPE_PMIC , +SCMI_TLM_COMPO_TYPE_BOARD , +SCMI_TLM_COMPO_TYPE_MEMORY , +SCMI_TLM_COMPO_TYPE_PERIPH , +SCMI_TLM_COMPO_TYPE_PERIPH_SUBC , +SCMI_TLM_COMPO_TYPE_LID , + SCMI_TLM_COMPO_TYPE_DISPLAY,
Die 0x1d is in the version I'm looking at. Perhaps a comment for specific version of spec used. I'd also
be tempted to assign every one of these as it makes it easy to eyeball an particular one vs the spec.
Quoted 5 lines
include/linux/scmi_protocol.h +5
-0+SCMI_TLM_COMPO_TYPE_RESERVED_START =0x1d , +SCMI_TLM_COMPO_TYPE_RESERVED_END =0xdf , +SCMI_TLM_COMPO_TYPE_OEM_START =0xe0 , + SCMI_TLM_COMPO_TYPE_OEM_END = 0xff, +};
Cristian Marussi
reviewerPATCH v15 14/35 firmware: arm_scmi: Add Telemetry TDCF areas parsing support
In reply to Cristian MarussiAdd logic to scan the SHMTI areas, parsing the TDCF descriptors and collect DataEvent contained in TDCF DATA lines. Ignore TDCF BLK_TS and UUID lines.
---
v14 --> v15
- account for tstamped TDE during discovery
- correct SHMTI overflow off-by-one
- rework TDE cache hit/miss logic
- introduce snapshots on full scan reads
- validate DE location on SCAN_UPDATE
- add custom SNAP eplg and overflow helpers
- moved here definition of scmi_telemetry_payld_offset_safe_calc
v13 --> v14
- fixed AB-BA dealock on blkts_link
- relocate xa_destroy for xa_lines after uuids cleanup
- report errors on scmi_telemetry_line_data_parse()
- moved some common protocol definitions here from previous
patches
- split out of this patch all of the BLK_TS and UUID lines
management, keeping only DATA line processing and delegating
the other line types to following smaller patches
- using a less regular time interval for TDCF scan retries
v12 --> v13
- refactored cleanup resources
- relocate tde->mtx guards higher up and add lockdep assertions
- drop refcount on previous active lines when scanning
- harden freeing path of UUIDs RCU items
- better DATA LINE size-validation while parsing
- reset active lines trackers on scan retries
- validate used_qwords
- redefine startm/endm bad sentinels and error path
- add more BLKTS_INVALID checks on parsing
- unlink stale BTS line when finding a new actibe for the TDE
- retry scans on TDCF sanity failures..could be a torn read
- use *alloc_objs
- remove TO_CPU64
v11 --> v12
- reject zero as MATCHSEQ
- reworked locking around line_get/put
- reworked locking around blkts_read
- a few comments on locking acquisition order
- cleanup active line refs even when bailing out of a scan in error
- added hashed locks for fine grain locking around blkts/uuid line creation
- added RCU protection on UUIDs access
v10 --> v11
- relocated some line handlers from another patch
- reworked TDE and lines locking logic
- fix UUID/BLKTS line refcounting to properly drop unused lines
- add a mutex to protect des cache accesses
v7 --> v9
- avoid referencing a NULL in trace_scmi_tlm_access O_o
- refactor scmi_telemetry_line_put to free its container on release
- convert SHMTI_OVERFLOW macro to an inline function
v7 --> v8
- Fixes for some Sashiko issues on v7
- reword a few dev_errs
- removed a few unused function (moved to next patch)
- add more safeguards and checks to SHMTI_OVERFLOWS
- fix UUIDs copy and allocation
- fix UUID reference store and cleanup
- cleanup all UNBOUND lines and their containers in resources_free
- remove mutex on primary init
v6 --> v7
- add _RAW helpers to manage non MMIO accesses
- sparse fixes
- use uuid_t for UUID lines
- fix proper UUID line parsing and binding
- adding a few traces for TDCF line handling
- add new dynamically sized UUID DB to ease discovered UUIDs lookup
- fix endianity handling in UUID TDCF lines
- handle Primary UUID lines with regular uuid_line
v5 --> v6
- consider UUID line in TDCF as BE when scanning
- use const booleans in scmi_telemetry_de_state_update()
- fix Sashiko review https://sashiko.dev/#/patchset/20260703123601.381275-1-cristian.marussi%40arm.com?part=8
- check SHMTI overflow while parsing
- use proper bytes increment while parsing line_by_type
- use mutex guard when link BLK_TS lines
v4 --> v5
- converted TDE_HAS_TSTAMP to inline scmi_tde_has_tstamp
- fixes sparse warnings
v3 --> v4
- use kzalloc_obj
- track SHMTI-discovered Telemetry DE states
- refactor TDE cache with dedicated helpers
- force tstamp to zero when timestamp is NOT supported or disabled
v2 --> v3
- split from monolithic Telemetry patch
- avoid devres allocation for resources that are added to the xa_lines XArray
- simplify prototype of line parsing helpers to drop unneeded dev
- flip tstmap logic in scmi_telemetry_line_data_parse() to properly emit
a TLM ftrace event
- use ternary ops to simplify quite a few expressions
--- drivers/firmware/arm_scmi/telemetry.c | 799 +++++++++++++++++++++++++- 1 file changed, 798 insertions(+), 1 deletion(-)
drivers/firmware/arm_scmi/telemetry.c +798 -1
@@ -14,21 +14,29 @@#include <linux/err.h> #include <linux/io.h> #include <linux/limits.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/refcount.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/sprintf.h> #include <linux/string.h> +#include <linux/time.h> #include <linux/uuid.h> #include <linux/xarray.h> #include "protocols.h" #include "notify.h" +#include <trace/events/scmi.h> +/* Updated only after ALL the mandatory features for that version are merged */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 +#define SCMI_TLM_TDCF_MAX_RETRIES 15 +#define SCMI_TLM_TDCF_RETRY_GAP_US 1234 +#define SCMI_TLM_DE_IMPL_NUM_DWORDS 4/* An enum listing the protocol specific commands definitions */
@@ -125,6 +133,158 @@ struct scmi_msg_resp_telemetry_update_intervals {#define _I (__a) (ioread32((void __iomem *)(__a))) +/* +* Define the behaviour of a SHMTI scan defining what information will +* be gathered and which Telemetry items can be updated. +*/ +enum scan_mode { +SCAN_LOOKUP ,/* Update only value/tstamp */ +SCAN_UPDATE ,/* Update also location offset */ +SCAN_DISCOVERY /* Update xa_des: allows for new DEs to be discovered */ +}; + +struct fc_line { +__le32 data_low; +__le32 data_high; +}; + +struct fc_tsline { +__le32 data_low; +__le32 data_high; +__le32 ts_low; +__le32 ts_high; +}; + +struct line { +__le32 data_low; +__le32 data_high; +}; + +struct tsline { +__le32 data_low; +__le32 data_high; +__le32 ts_low; +__le32 ts_high; +}; + +#define LINE_DATA_GET_RAW (f) \ +({ \ + typeof(f) _f = (f); \ + u32 _high = le32_to_cpu(_f->data_high); \ + u32 _low = le32_to_cpu(_f->data_low); \ + u64 _res; \ + \ + _res = (((u64)(_high)) << 32) | (_low); \ + _res; \ +}) + +#define LINE_DATA_GET (f) \ +({ \ + typeof(f) _f = (f); \ + u32 _high = _I(&_f->data_high); \ + u32 _low = _I(&_f->data_low); \ + u64 _res; \ + \ + _res = (((u64)(_high)) << 32) | (_low); \ + _res; \ +}) + +#define LINE_TSTAMP_GET_RAW (f) \ +({ \ + typeof(f) _f = (f); \ + u32 _high = le32_to_cpu(_f->ts_high); \ + u32 _low = le32_to_cpu(_f->ts_low); \ + u64 _res; \ + \ + _res = (((u64)(_high)) << 32) | (_low); \ + _res; \ +}) + +#define LINE_TSTAMP_GET (f) \ +({ \ + typeof(f) _f = (f); \ + u32 _high = _I(&_f->ts_high); \ + u32 _low = _I(&_f->ts_low); \ + u64 _res; \ + \ + _res = (((u64)(_high)) << 32) | (_low); \ + _res; \ +}) + +#define BLK_TS_STAMP (f) LINE_TSTAMP_GET(f) +#define BLK_TS_STAMP_RAW (f) LINE_TSTAMP_GET_RAW(f) +#define BLK_TS_RATE (p) PAYLD_ID(p) +#define BLK_TS_RATE_RAW (p) PAYLD_ID_RAW(p) + +enum tdcf_line_types { +TDCF_DATA_LINE =0 , +TDCF_BLK_TS_LINE =1 , +TDCF_UUID_LINE =2 , +}; + +struct payload { +__le32 meta; +#define PAYLD_META (x) (_I(&((x)->meta))) +#define PAYLD_META_RAW (x) (le32_to_cpu((x)->meta)) +#define LINE_TYPE_RAW (m) (FIELD_GET(GENMASK(7, 4), (m))) +#define LINE_TYPE (x) (LINE_TYPE_RAW(PAYLD_META(x))) +#define IS_DATA_LINE (x) (LINE_TYPE(x) == TDCF_DATA_LINE) +#define IS_DATA_LINE_RAW (x) (LINE_TYPE_RAW(x) == TDCF_DATA_LINE) +#define IS_BLK_TS_LINE (x) (LINE_TYPE(x) == TDCF_BLK_TS_LINE) +#define IS_BLK_TS_LINE_RAW (x) (LINE_TYPE_RAW(x) == TDCF_BLK_TS_LINE) +#define IS_UUID_LINE (x) (LINE_TYPE(x) == TDCF_UUID_LINE) +#define USE_BLK_TS_RAW (m) ((m) & BIT(3)) +#define USE_BLK_TS (x) (USE_BLK_TS_RAW(PAYLD_META(x))) +#define HAS_LINE_EXT_RAW (m) ((m) & BIT(2)) +#define HAS_LINE_EXT (x) (HAS_LINE_EXT_RAW(PAYLD_META(x))) +#define LINE_TS_VALID_RAW (m) ((m) & BIT(1)) +#define LINE_TS_VALID (x) (LINE_TS_VALID_RAW(PAYLD_META(x))) +#define DATA_INVALID_RAW (m) ((m) & BIT(0)) +#define DATA_INVALID (x) (DATA_INVALID_RAW(PAYLD_META(x))) +#define BLK_TS_INVALID_RAW (m) \ +({ \ + bool invalid; \ + \ + invalid = LINE_TS_VALID_RAW(m) || HAS_LINE_EXT_RAW(m) || \ + USE_BLK_TS_RAW(m) || DATA_INVALID_RAW(m); \ + invalid; \ +}) +#define BLK_TS_INVALID (p) BLK_TS_INVALID_RAW(PAYLD_META(p)) + +#define UUID_INVALID_RAW (m) \ +({ \ + bool invalid; \ + \ + invalid = LINE_TS_VALID_RAW(m) || USE_BLK_TS_RAW(m) || \ + DATA_INVALID_RAW(m) || !HAS_LINE_EXT_RAW(m); \ + invalid; \ +}) +#define UUID_INVALID (p) UUID_INVALID_RAW(PAYLD_META(p)) +__le32 id; +union { +struct line l; +struct tsline tsl; + }; +}; + +#define PAYLD_ID (x) (_I(&(((struct payload __iomem *)(x))->id))) +#define PAYLD_ID_RAW (x) (le32_to_cpu((((struct payload *)(x))->id))) + +#define LINE_DATA_PAYLD_WORDS \ + ((sizeof(u32) + sizeof(u32) + sizeof(struct line)) / sizeof(u32)) +#define EXT_LINE_DATA_PAYLD_WORDS \ + ((sizeof(u32) + sizeof(u32) + sizeof(struct tsline)) / sizeof(u32)) + +#define LINE_LENGTH_WORDS_RAW (m) \ + (HAS_LINE_EXT_RAW((m)) ? EXT_LINE_DATA_PAYLD_WORDS : LINE_DATA_PAYLD_WORDS) + +#define LINE_LENGTH_WORDS (x) LINE_LENGTH_WORDS_RAW(PAYLD_META(x)) +#define LINE_LENGTH_QWORDS (x) ((LINE_LENGTH_WORDS(x)) / 2) +#define LINE_LENGTH_QWORDS_RAW (x) ((LINE_LENGTH_WORDS_RAW(x)) / 2) + +#define LINE_LENGTH_BYTES_RAW (m) ((LINE_LENGTH_WORDS_RAW(m)) * sizeof(u32)) +#define LINE_LENGTH_BYTES (x) (LINE_LENGTH_BYTES_RAW(PAYLD_META(x))) +struct prlg {__le32 sign_start;#define SIGNATURE_START 0x5442474E/* TBGN */
@@ -147,12 +307,24 @@ struct tdcf {unsigned char payld[]; }; +#define QWORDS (_t) (_I(&(_t)->prlg.num_qwords)) +#define QWORDS_RAW (_t) (le32_to_cpu((_t)->prlg.num_qwords)) +#define SHMTI_MIN_SIZE (sizeof(struct tdcf) + TDCF_EPLG_SZ)/* An arbitrary upper bound to avoid crazy allocations driven by buggy FW */ #define SHMTI_MAX_SIZE SZ_64K -#define TDCF_START_SIGNATURE (x) (_I(&((x)->prlg.sign_start))) +#define TDCF_START_SIGNATURE (x) (_I(&((x)->prlg.sign_start))) +#define TDCF_START_SEQ_GET (x) (_I(&((x)->prlg.match_start))) +#define TDCF_START_SEQ_GET_RAW (x) (le32_to_cpu((x)->prlg.match_start)) +/* TDCF MatchSequence cannot be zero by the SCMI v4.0 Specification */ +#define TDCF_INVALID_MATCH_SEQ 0 +#define IS_BAD_START_SEQ (s) ((s) & 0x1 || (s) == TDCF_INVALID_MATCH_SEQ) + +#define TDCF_END_SEQ_GET (e) (_I(&((e)->match_end))) +#define TDCF_END_SEQ_GET_RAW (e) (le32_to_cpu((e)->match_end))#define TDCF_END_SIGNATURE (e) (_I(&((e)->sign_end))) +#define TDCF_BAD_END_SEQ GENMASK(31, 0)struct telemetry_shmti ;
@@ -178,6 +350,9 @@ struct telemetry_shmti {struct mutex payld_locks[1 <<PAYLD_HASH_BITS ]; }; +#define TDCF_OFFS (_b, _p) \ + ((unsigned long)((__force void *)(_p) - (__force void *)(_b))) +#define SHMTI_EPLG (s) \ ({ \ struct telemetry_shmti *_s = (s); \
@@ -187,6 +362,44 @@ struct telemetry_shmti { (_eplg); \ }) +#define SNAP_EPLG (s) \ + ({ \ + struct shmti_snap *_s = (s); \ + struct eplg *_eplg; \ + \ + _eplg = (void *)_s->base + _s->shmti->info.len - TDCF_EPLG_SZ; \ + (_eplg); \ + }) + +struct telemetry_info ; + +struct telemetry_line { +enum tdcf_line_types type; +u32 last_magic; +unsigned long offset; +refcount_t users; +struct telemetry_shmti *shmti; +/* Protect line accesses */ +struct mutex mtx; +}; + +struct telemetry_block_ts { +u64 last_ts; +u32 last_rate; +struct telemetry_line line; +}; + +#define to_blkts (l) container_of(l, struct telemetry_block_ts, line) + +struct telemetry_uuid { +u32 key; +struct hlist_node node_ht; +struct hlist_node node_lst; +uuid_t uuid; +}; + +#define to_uuid_from_uuid_t (u) container_of(u, struct telemetry_uuid, uuid) +#define IS_TDE_TSTAMP_TYPE (__t, __ttype) ((__t)->ts_type == (__ttype))#define IS_TDE_TS_NONE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_NONE))#define IS_TDE_TS_LINE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_LINE))
@@ -220,7 +433,14 @@ struct telemetry_de {u32 offset;/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */ u32 fc_size; +u64 last_val; +u64 last_ts; +u32 last_magic;struct list_head item; +struct telemetry_block_ts *bts; +#define BLK_TS_CHANGED (t, s, o) \ + (!((t)->bts) || (t)->bts->line.shmti != (s) || (t)->bts->line.offset != (o)) +struct telemetry_uuid *uuid;struct scmi_telemetry_de de;/* Protect last_val/ts/magic accesses - MUST BE KEPT LAST */ struct mutex mtx;
@@ -228,10 +448,24 @@ struct telemetry_de {#define to_tde (d) container_of(d, struct telemetry_de, de) +static inline bool scmi_tde_has_tstamp (struct telemetry_de *t) +{ +return t->de.tstamp_support && t->de.tstamp_enabled; +} + +enum de_state { +ENA_STATE , +ENA_TSTAMP , +ENA_MAX +}; +struct telemetry_info {bool streaming_mode;unsigned int num_shmti;unsigned int num_des_tstamp; +/* Mutex to protect des_enabled_mtx */ +struct mutex des_enabled_mtx; +atomic_t des_enabled[ENA_MAX ];unsigned int default_blk_ts_rate;const struct scmi_protocol_handle *ph;struct telemetry_shmti *shmti;
@@ -251,6 +485,27 @@ struct telemetry_info {static struct scmi_telemetry_res_info *__scmi_telemetry_resources_get (struct telemetry_info *ti); +static inline struct tdcf *snap_tdcf (struct shmti_snap *s); + +static int scmi_telemetry_shmti_scan (struct telemetry_info *ti, +unsigned int shmti_id,enum scan_mode mode); + +static inline void +scmi_telemetry_de_state_update (struct telemetry_info *ti,enum de_state state, +bool *current_state,const bool next_state) +{ +guard (mutex)(&ti->des_enabled_mtx); +if (!current_state || *current_state != next_state) +atomic_add (next_state ?1 :-1 , &ti->des_enabled[state]); + +if (current_state) + *current_state = next_state; + +dev_dbg (ti->ph->dev,"Telemetry des_enabled[%s]:%u\n" , + state ==ENA_STATE ?"STATE" :"TSTAMP" , +atomic_read (&ti->des_enabled[state])); +} +static struct telemetry_de *scmi_telemetry_free_tde_get (struct telemetry_info *ti) {
@@ -357,6 +612,35 @@ static int scmi_telemetry_tde_register(struct telemetry_info *ti,return ret; } +static inline bool +scmi_telemetry_tde_cache_stale (struct telemetry_de *tde,u32 magic) +{ +lockdep_assert_held (&tde->mtx); + +/* last_magic < magic */ +return time_before32 (tde->last_magic, magic); +} + +static void +scmi_telemetry_tde_cache_update (struct telemetry_de *tde,u64 val, +u64 *tstamp,u32 *magic) +{ +lockdep_assert_held (&tde->mtx); + +/* +* Update the cache only if stale compared to the current +* sample, so bail-out when last_magic >= *magic +*/ +if (magic && !scmi_telemetry_tde_cache_stale (tde, *magic)) +return ; + + tde->last_magic = magic ? *magic :TDCF_BAD_END_SEQ ; + tde->last_val = val; + tde->last_ts = tstamp &&scmi_tde_has_tstamp (tde) ? *tstamp :0 ; +if (tstamp) + *tstamp = tde->last_ts; +} +struct scmi_tlm_de_priv {struct telemetry_info *ti;void *next;
@@ -1196,6 +1480,510 @@ scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id)return xa_load (&ti->xa_des, id); } +static inline struct payload __iomem * +scmi_telemetry_payld_offset_safe_calc (struct telemetry_shmti *shmti,u32 offs) +{ +struct payload __iomem *payld; +unsigned long p; +u32 end; + +/* Check if accessing payld->meta is safe. */ +if (check_add_overflow (offs,sizeof (u32), &end) || + end > shmti->info.len) +return IOMEM_ERR_PTR (-EPROTO ); + +if (check_add_overflow ((__force unsigned long )shmti->base, offs, &p)) +return IOMEM_ERR_PTR (-EPROTO ); + + payld = shmti->base + offs; +/* Check line boundary */ +if (check_add_overflow (offs,LINE_LENGTH_BYTES (payld), &end) || + end > shmti->info.len) +return IOMEM_ERR_PTR (-EPROTO ); + +return payld; +} + +static u64 +scmi_telemetry_blkts_read (u32 magic,struct telemetry_de *tde) +{ +lockdep_assert_held (&tde->mtx); + +return 0 ; +} + +static inline struct telemetry_de * +scmi_telemetry_tde_allocate (struct telemetry_info *ti,u32 de_id, +struct payload *payld) +{ +u32 meta =PAYLD_META_RAW (payld); +struct telemetry_de *tde; + + tde =scmi_telemetry_tde_get (ti, de_id); +if (IS_ERR (tde)) +return NULL ; + +guard (mutex)(&tde->mtx); + tde->de.info->id = de_id; + tde->de.enabled = true; + tde->de.tstamp_enabled =LINE_TS_VALID_RAW (meta) ||USE_BLK_TS_RAW (meta); +/* An approximation till the real DE discovery is performed */ + tde->de.tstamp_support = tde->de.tstamp_enabled; + +if (scmi_telemetry_tde_register (ti, tde)) { +scmi_telemetry_free_tde_put (ti, tde); +return NULL ; + } + +scmi_telemetry_de_state_update (ti,ENA_STATE ,NULL , true); +if (tde->de.tstamp_enabled) +scmi_telemetry_de_state_update (ti,ENA_TSTAMP ,NULL , true); + +/* Count timestamped DEs */ + ti->num_des_tstamp += !!tde->de.tstamp_support; + +return tde; +} + +static inline int +scmi_telemetry_line_data_parse (struct telemetry_de *tde,struct shmti_snap *snap, +u64 *val,u64 *tstamp,struct payload *payld, +struct payload *bts_payld,u32 magic) +{ +u32 meta =PAYLD_META_RAW (payld); + +lockdep_assert_held (&tde->mtx); + +/* Clear returned values in case of errors */ + *val =0 ; + +if (tstamp) { + *tstamp =0 ; +if (USE_BLK_TS_RAW (meta)) { +/* Read out the actual BLK_TS */ + *tstamp =scmi_telemetry_blkts_read (magic, tde); + }else if (LINE_TS_VALID_RAW (meta)) { +/* +* Note that LINE_TS_VALID implies HAS_LINE_EXT and that +* the per DE line_ts_rate is advertised in the DE +* descriptor, BUT we check anyway against buggy FW. +*/ +if (!HAS_LINE_EXT_RAW (meta)) { +trace_scmi_tlm_access (tde->de.info->id, +"SHMTI_BAD_TS_LINE" ,0 ,0 ); +return -EPROTO ; + } + + *tstamp =LINE_TSTAMP_GET_RAW (&payld->tsl); + } + } + +/* Data is always valid since we are NOT handling BLK TS lines here */ + *val =LINE_DATA_GET_RAW (&payld->l); + +trace_scmi_tlm_collect (tstamp ? *tstamp :0 , tde->de.info->id, + *val,"SHMTI_DE_READ" ); + +scmi_telemetry_tde_cache_update (tde, *val, tstamp, &magic); + +return 0 ; +} + +/** +* scmi_telemetry_tdcf_data_parse - TDCF DataLine parsing +* @ti: A reference to the telemetry info descriptor +* @payld: Line payload to parse +* @snap: A reference to the working SHMTI snapshot that will be processed +* @mode: A flag to determine the behaviour of the scan +* @active_bts: A pointer to keep track and report any found BLK timestamp line +* @active_uuid: A pointer to keep track and report any found UUID line +* +* This routine takes care to: +* - verify line consistency in relation to the used flags and the current +* context: e.g. is there an active preceding BLK_TS line if the DataLine +* sports a USE_BLKTS flag ? +* - verify the related Data Event ID exists OR create a brand new DE +* (depending on the @mode of operation) +* - links any active BLK_TS or UUID line to the current DE +* - read and save value/tstamp for the DE ONLY if anything has changed (by +* tracking the last TDCF magic) and update related magic: this allows to +* minimize future needs of single-DE reads +* +* Modes of operation. +* +* The scan behaviour depends on the chosen @mode: +* - SCAN_LOOKUP: the basic scan which aims to update value associated to +* existing DEs. Any discovered DataLine that could NOT be +* matched to an existing, previously discovered, DE is +* discarded. This is the normal scan behaviour. +* - SCAN_UPDATE: a more advanced scan which provides all the SCAN_LOOKUP +* features plus takes care to update the DEs location +* coordinates inside the SHMTI: note that the related DEs are +* still supposed to have been previously discovered when +* this scan runs. This is used to update location +* coordinates for DEs contained in a Group when such group +* is enabled. +* - SCAN_DISCOVERY: the most advanced scan available which provides all +* the SCAN_LOOKUP features plus discovery capabilities: +* any DataLine referring to a previously unknown DE leads +* to the allocation of a new DE descriptor. +* This mode is used on the first scan at init time, ONLY +* if Telemetry was found to be already enabled at boot on +* the platform side: this helps to maximize gathered +* information when dealing with out of spec firmwares. +* Any usage of this discovery mode other than in a boot-on +* enabled scenario is discouraged since it can easily +* lead to spurious DE discoveries. +*/ +static void scmi_telemetry_tdcf_data_parse (struct telemetry_info *ti, +struct payload *payld, +struct shmti_snap *snap, +enum scan_mode mode, +struct telemetry_block_ts *active_bts, +struct telemetry_uuid *active_uuid) +{ +struct telemetry_shmti *shmti = snap->shmti; +struct telemetry_de *tde; +u64 val, tstamp =0 ; +u32 de_id; + + de_id =PAYLD_ID_RAW (payld); +/* Is this DE ID known ? */ + tde =scmi_telemetry_tde_lookup (ti, de_id); +if (!tde) { +if (mode !=SCAN_DISCOVERY ) { +trace_scmi_tlm_access (de_id,"DE_INVALID" ,0 ,0 ); +return ; + } + +/* In SCAN_DISCOVERY mode we allocate new DEs for unknown IDs */ + tde =scmi_telemetry_tde_allocate (ti, de_id, payld); +if (!tde) { +dev_err (ti->ph->dev, +"Cannot allocate TDE for ID:0x%08X\n" , de_id); +return ; + } + } + +guard (mutex)(&tde->mtx); +/* +* Update DE location refs if requested: normally done only on group +* enable. +* Note that all the DE location information is relative to the +* original SHMTI device-memory area, NOT to the snapshot we are parsing. +*/ +if (mode >=SCAN_UPDATE ) { +u32 de_offs; +void __iomem *p; + +/* Offest is the same in the SHMTI and in the snapshot indeed */ + de_offs = (void __iomem *)payld - (void __iomem *)snap->base; +/* Validate location before update */ + p =scmi_telemetry_payld_offset_safe_calc (shmti, de_offs); +if (IS_ERR (p)) { +dev_err (ti->ph->dev, +"TDE 0x%08X updated location invalid - shmti:%u offs:%u\n" , + de_id, shmti->info.sid, de_offs); +trace_scmi_tlm_access (de_id,"DE_LOCATION_INVALID" ,0 ,0 ); +return ; + } + + tde->sid = shmti->info.sid; + tde->base = shmti->base; + tde->eplg =SHMTI_EPLG (shmti); + tde->offset = de_offs; + +trace_scmi_tlm_access (de_id,"DE_LOCATION_UPDATED" ,0 ,0 ); + +dev_dbg (ti->ph->dev, +"TDCF-updated DE_ID:0x%08X - shmti:%pK offset:%u\n" , + tde->de.info->id, tde->base, tde->offset); + } + +/* +* Has any value/tstamp really changed ? +* Note that, if the magic currently associated with this TDE has +* NOT changed compared to the current SHMTI magic, nothing could have +* been changed across the whole TDCF, including any relation to +* BLK_TS or UUID lines. +*/ +if (!scmi_telemetry_tde_cache_stale (tde, snap->magic)) +return ; + +/* +* Parse data words ... only for the sake of updating TDE cache, +* any error is logged by ignored. +*/ +scmi_telemetry_line_data_parse (tde, snap, &val, &tstamp, payld,NULL , + snap->magic); +} + +/** +* scmi_telemetry_tdcf_line_parse - Line parser +* @ti: A reference to the Telemetry instance +* @payld: A reference to the payload representing the line to parse +* @snap: A reference to the working SHMTI snapshot that will be processed +* @mode: Specification of the scanning mode +* @active_bts: A reference used to track the last BLK_TS line found +* @active_uuid: A reference used to track the last UUID line found +* +* Note that @active_bts and @active_uuid references are propagated back and +* forth from the line parsers to keep track of the nearest active BLK_TS or +* UUID line found in the SHMTI areas and, while doing that, such descriptors +* are refcounted and tracked in the xa_lines XArray indexed by @payld. +* BLK_TS and UUID descriptors are dropped when no more linked to any DATA line, +* during the normal system operation OR later on when the protocol is +* de-initialized following a driver removal. +* +* Return: Number of QWORDS consumed from @payld by this line parsing; note +* that buggy firmware could return bad, out of range values, so this +* value MUST be carefully checked for SHMTI boundary overflows by the +* caller. +*/ +static int scmi_telemetry_tdcf_line_parse (struct telemetry_info *ti, +struct payload *payld, +struct shmti_snap *snap, +enum scan_mode mode, +struct telemetry_block_ts **active_bts, +struct telemetry_uuid **active_uuid) +{ +u32 meta =le32_to_cpu (payld->meta); +int used_qwords; + + used_qwords =LINE_LENGTH_QWORDS_RAW (meta); +/* Invalid lines are not an error, could simply be disabled DEs */ +if (DATA_INVALID_RAW (meta)) { +trace_scmi_tlm_access (le32_to_cpu (payld->id),"TDCF_INVALID" ,0 ,0 ); +return used_qwords; + } + +/* +* Problematic lines are ignored, their information discarded, and such +* issues are logged, but the scan process continues. +*/ +switch (LINE_TYPE_RAW (meta)) { +case TDCF_DATA_LINE : +scmi_telemetry_tdcf_data_parse (ti, payld, snap, mode, + *active_bts, *active_uuid); +break ; +default : +trace_scmi_tlm_access (PAYLD_ID_RAW (payld),"TDCF_LINE_UNKNOWN" ,0 ,0 ); +break ; + } + +return used_qwords; +} + +static inline bool +scmi_telemetry_shmti_overflow (struct telemetry_shmti *shmti,void __iomem *next) +{ +void __iomem *limit =SHMTI_EPLG (shmti); + +/* Is payld->meta accessible ? */ +if (next >= limit -sizeof (u32)) +return true; + + next +=LINE_LENGTH_BYTES ((struct payload __iomem *)next); + +return next > limit; +} + +static inline struct tdcf *snap_tdcf (struct shmti_snap *s) +{ +return (struct tdcf *)s->base; +} + +static inline bool +scmi_telemetry_snap_overflow (struct shmti_snap *snap,const void *next) +{ +void *limit =SNAP_EPLG (snap); + +/* Is payld->meta accessible ? */ +if (next >= limit -sizeof (u32)) +return true; + + next +=LINE_LENGTH_BYTES_RAW (PAYLD_META_RAW ((struct payload *)next)); + +return next > limit; +} + +static int scmi_telemetry_shmti_snapshot (struct telemetry_shmti *shmti, +struct shmti_snap *work) +{ +struct tdcf __iomem *tdcf = shmti->base; +int retries =SCMI_TLM_TDCF_MAX_RETRIES ; +struct tdcf *snap =snap_tdcf (work); +u32 startm =0 , endm =0 , live; + +lockdep_assert_held (&shmti->lock); + +do { +/* A bit of exponential backoff between retries */ +fsleep ((SCMI_TLM_TDCF_MAX_RETRIES - retries) *SCMI_TLM_TDCF_RETRY_GAP_US ); + +memcpy_fromio (snap, tdcf, shmti->info.len); +/* Assure the TDCF snapshot has been fully taken before checking seqnum */ +rmb (); +/* Used to detect changes while copying */ + live =TDCF_START_SEQ_GET (tdcf); + + startm =TDCF_START_SEQ_GET_RAW (snap); +if (IS_BAD_START_SEQ (startm)) { +trace_scmi_tlm_access (0 ,"MSEQ_BADSTART" , startm,0 ); +continue ; + } + + endm =TDCF_END_SEQ_GET_RAW (SNAP_EPLG (work)); +if (startm != endm || startm != live) { +trace_scmi_tlm_access (0 ,"MSEQ_MISMATCH" , startm, + startm != endm ? endm : live); +continue ; + } + +trace_scmi_tlm_access (0 ,"TDCF_SNAPSHOT_OK" , startm, endm); + +/* Ensure any snapshot is visible before updating magic */ +smp_store_release (&work->magic, startm); + +return 0 ; + }while (--retries); + +trace_scmi_tlm_access (0 ,"TDCF_SNAPSHOT_FAIL" , startm, endm); + +return -EPROTO ; +} + +/* +* True if neither TDCF marker has moved since the cached snapshot was taken. +* Any platform update must write one marker before touching data (start first +* or end first, depending on the spec's order), so checking both is +* independent of that order. +*/ +static bool scmi_telemetry_shmti_unchanged (struct telemetry_shmti *shmti,u32 magic) +{ +struct tdcf __iomem *tdcf = shmti->base; + +return TDCF_START_SEQ_GET (tdcf) == magic && +TDCF_END_SEQ_GET (SHMTI_EPLG (shmti)) == magic; +} + +/* Returns with the SRCU read lock held (index in *idx) on a snapshot that matched at check time */ +static struct shmti_snap * +scmi_telemetry_shmti_refresh (struct telemetry_shmti *shmti,int *idx) +{ +struct shmti_snap *cur, *work; +int ret; + +guard (mutex)(&shmti->lock); + + cur =rcu_dereference_protected (shmti->cur,lockdep_is_held (&shmti->lock)); +/* Someone else may have refreshed while we waited for the mutex */ +if (cur &&scmi_telemetry_shmti_unchanged (shmti, cur->magic)) { +/* Safe: recycling cur requires shmti->lock, which we hold */ + *idx =srcu_read_lock (&shmti->srcu); +return cur; + } + +/* Pick the spare buffer */ + work = (cur == shmti->snaps[0 ]) ? shmti->snaps[1 ] : shmti->snaps[0 ]; +/* +* Wait for readers that may still be parsing 'work' from its time +* as current +*/ +synchronize_srcu (&shmti->srcu); + + ret =scmi_telemetry_shmti_snapshot (shmti, work); +if (ret) +return ERR_PTR (ret); + +/* +* Take our read lock BEFORE publishing so 'work' cannot be +* recycled under us +*/ + *idx =srcu_read_lock (&shmti->srcu); +rcu_assign_pointer (shmti->cur, work); + +return work; +} + +/** +* scmi_telemetry_shmti_scan - Full SHMTI scan +* @ti: A reference to the telemetry info descriptor +* @shmti_id: ID of the SHMTI area that has to be scanned +* @mode: A flag to determine the behaviour of the scan +* +* Return: 0 on Success +*/ +static int scmi_telemetry_shmti_scan (struct telemetry_info *ti, +unsigned int shmti_id,enum scan_mode mode) +{ +struct telemetry_shmti *shmti = &ti->shmti[shmti_id]; +struct telemetry_block_ts *active_bts =NULL ; +struct telemetry_uuid *active_uuid =NULL ; +unsigned int qwords; +struct shmti_snap *snap; +struct tdcf *tdcf; +int idx, ret =0 ; +void *next; + +if (!shmti->base) +return -ENODEV ; + + idx =srcu_read_lock (&shmti->srcu); + snap =srcu_dereference (shmti->cur, &shmti->srcu); +if (!snap || !scmi_telemetry_shmti_unchanged (shmti, snap->magic)) { +/* MUST drop the read lock: refresh calls synchronize_srcu() */ +srcu_read_unlock (&shmti->srcu, idx); + snap =scmi_telemetry_shmti_refresh (shmti, &idx); +if (IS_ERR (snap)) +return PTR_ERR (snap); + } + + tdcf =snap_tdcf (snap); + next = tdcf->payld; + qwords =QWORDS_RAW (tdcf); +while (qwords) { +int used_qwords; + +/* +* Bailout on any error since we are processing a supposedly +* stable snapshot at this point, so any further error is on +* firmware-side and NOT recoverable: this would mean having to +* drop the remaining part of the scan results after having +* cached the initial good snapshot: log this down below. +*/ +if (scmi_telemetry_snap_overflow (snap, next)) { +trace_scmi_tlm_access (0 ,"SHMTI_OVERFLOW" , snap->magic,0 ); + ret = -EPROTO ; +break ; + } + + used_qwords =scmi_telemetry_tdcf_line_parse (ti, next, snap, mode, + &active_bts, &active_uuid); +if (used_qwords <=0 || qwords < used_qwords) { +/* +* Stop scanning when qwords inconsistent; if the last +* line processed was sane in itself, though, keep it +* since we anyway previously checked for overflow. +*/ +trace_scmi_tlm_access (PAYLD_ID_RAW (next),"BAD_QWORDS" , + snap->magic,0 ); + ret = -EPROTO ; +break ; + } + + next += used_qwords *8 ; + qwords -= used_qwords; + } + +srcu_read_unlock (&shmti->srcu, idx); + +if (ret) +dev_err (ti->ph->dev,FW_BUG "Inconsistent TDCF. Scan aborted.\n" ); + +return ret; +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, .de_lookup = scmi_telemetry_de_lookup,
@@ -1337,6 +2125,12 @@ static void scmi_telemetry_tdes_cleanup(struct telemetry_info *ti)struct scmi_telemetry_de *de;unsigned long idx; +/* +* Unlinking all the BLK_TS/UUID lines related to a DE triggers also +* the deallocation of such lines when the embedded refcount hits zero. +* Note that the UUID lines are also referenced and refcounted by the +* global UUID RCU array, so they won't be deallocated here. +*/ xa_for_each (&ti->xa_des, idx, de) {struct telemetry_de *tde =to_tde (de);
@@ -1468,6 +2262,9 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)xa_init (&ti->xa_des); +atomic_set (&ti->des_enabled[ENA_STATE ],0 ); +atomic_set (&ti->des_enabled[ENA_TSTAMP ],0 ); +mutex_init (&ti->des_enabled_mtx);/* Setup resources lazy initialization */ atomic_set (&ti->rinfo_initializing,0 );init_completion (&ti->rinfo_initdone);
Signature · 1 line
2.54.0
Re: [PATCH v15 14/35] firmware: arm_scmi: Add Telemetry TDCF areas parsing support
Quoted 3 lines · On Mon, 9 Nov 2026 09:12:00 +0000 Cristian Marussi <[email protected]>…
Add logic to scan the SHMTI areas, parsing the TDCF descriptors and collect DataEvent contained in TDCF DATA lines. Ignore TDCF
BLK_TSand UUID lines.
Main comment on this one is various unused definitions at the top. I'd push those to where they get used so that reviewers can see all the relevant material in one patch.
May involve a couple of forwards declarations that get replaced in a future patch but I think that tiny bit of churn is worth it.
Quoted 25 lines
Signed-off-by Cristian Marussi <[email protected]>--- drivers/firmware/arm_scmi/telemetry.c | 799 +++++++++++++++++++++++++- 1 file changed, 798 insertions(+), 1 deletion(-)drivers/firmware/arm_scmi/telemetry.c +3
-0@@ -14,21 +14,29 @@#include <linux/err.h> #include <linux/io.h> #include <linux/limits.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/refcount.h> #include <linux/sizes.h> #include <linux/slab.h> #include <linux/sprintf.h> #include <linux/string.h> +#include <linux/time.h> #include <linux/uuid.h> #include <linux/xarray.h>
drivers/firmware/arm_scmi/telemetry.c line 37
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +3
-0#include "protocols.h" #include "notify.h" +#include <trace/events/scmi.h> +/* Updated only after ALL the mandatory features for that version are merged */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 +#define SCMI_TLM_TDCF_MAX_RETRIES 15
Add a comment on why this particular number. Even if that's 'it seems to work' :)
Quoted 20 lines
drivers/firmware/arm_scmi/telemetry.c +13
-0+#define SCMI_TLM_TDCF_RETRY_GAP_US 1234 +#define SCMI_TLM_DE_IMPL_NUM_DWORDS 4/* An enum listing the protocol specific commands definitions */ @@ -125,6 +133,158 @@ struct scmi_msg_resp_telemetry_update_intervals {#define _I (__a) (ioread32((void __iomem *)(__a))) +/* +* Define the behaviour of a SHMTI scan defining what information will +* be gathered and which Telemetry items can be updated. +*/ +enum scan_mode { +SCAN_LOOKUP ,/* Update only value/tstamp */ +SCAN_UPDATE ,/* Update also location offset */ +SCAN_DISCOVERY /* Update xa_des: allows for new DEs to be discovered */ +}; + +struct fc_line {
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_update_intervals { · line 156
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+__le32 data_low; +__le32 data_high; +}; + +struct fc_tsline { +__le32 data_low; +__le32 data_high; +__le32 ts_low; +__le32 ts_high; +};
Some of these defs can wait for the patch that needs them. Then we can sanity check them against the spec with the parsing code etc.
Quoted 15 lines
drivers/firmware/arm_scmi/telemetry.c +14
-0+ +struct line { +__le32 data_low; +__le32 data_high; +}; + +struct tsline { +__le32 data_low; +__le32 data_high; +__le32 ts_low; +__le32 ts_high; +}; + +struct telemetry_info ;
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_update_intervals { · line 223
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ +struct telemetry_line { +enum tdcf_line_types type; +u32 last_magic; +unsigned long offset; +refcount_t users; +struct telemetry_shmti *shmti; +/* Protect line accesses */ +struct mutex mtx; +};
Follows from below, I'd keep this for where it is used.
drivers/firmware/arm_scmi/telemetry.c struct telemetry_shmti { · line 392
Quoted 8 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+ +struct telemetry_block_ts { +u64 last_ts; +u32 last_rate; +struct telemetry_line line; +}; + +#define to_blkts (l) container_of(l, struct telemetry_block_ts, line)
I'd do similar for this to uuid. Effectively put a placeholder def in so that you can introduce this stuff with the code that uses it.
drivers/firmware/arm_scmi/telemetry.c struct telemetry_shmti { · line 401
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+ +struct telemetry_uuid { +u32 key; +struct hlist_node node_ht; +struct hlist_node node_lst; +uuid_t uuid; +}; + +#define to_uuid_from_uuid_t (u) container_of(u, struct telemetry_uuid, uuid)
No uuid usage in here yet... Push to patch where it comes in. I'd be tempted to use a forwards def for the struct for now and replace it in the patch where it becomes meaningful. In this patch it's just passed around as a cookie that is never set.
Quoted 20 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+#define IS_TDE_TSTAMP_TYPE (__t, __ttype) ((__t)->ts_type == (__ttype))#define IS_TDE_TS_NONE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_NONE))#define IS_TDE_TS_LINE (_t) (IS_TDE_TSTAMP_TYPE((_t), TSTAMP_LINE))@@ -220,7 +433,14 @@ struct telemetry_de {u32 offset;/* NOTE THAT DE data_sz is registered in scmi_telemetry_de */ u32 fc_size; +u64 last_val; +u64 last_ts; +u32 last_magic;struct list_head item; +struct telemetry_block_ts *bts; +#define BLK_TS_CHANGED (t, s, o) \ + (!((t)->bts) || (t)->bts->line.shmti != (s) || (t)->bts->line.offset != (o)) +struct telemetry_uuid *uuid;struct scmi_telemetry_de de;/* Protect last_val/ts/magic accesses - MUST BE KEPT LAST */ struct mutex mtx;
drivers/firmware/arm_scmi/telemetry.c struct telemetry_de { · line 457
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +7
-0@@ -228,10 +448,24 @@ struct telemetry_de {#define to_tde (d) container_of(d, struct telemetry_de, de) +static inline bool scmi_tde_has_tstamp (struct telemetry_de *t) +{ +return t->de.tstamp_support && t->de.tstamp_enabled; +} + +enum de_state { +ENA_STATE ,
See below for comments on this being confusing naming at best.
Quoted 16 lines
drivers/firmware/arm_scmi/telemetry.c +7
-0+ENA_TSTAMP , +ENA_MAX +}; +struct telemetry_info {bool streaming_mode;unsigned int num_shmti;unsigned int num_des_tstamp; +/* Mutex to protect des_enabled_mtx */ +struct mutex des_enabled_mtx; +atomic_t des_enabled[ENA_MAX ];unsigned int default_blk_ts_rate;const struct scmi_protocol_handle *ph;struct telemetry_shmti *shmti;@@ -251,6 +485,27 @@ struct telemetry_info { static struct scmi_telemetry_res_info *
drivers/firmware/arm_scmi/telemetry.c struct telemetry_info { · line 495
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0__scmi_telemetry_resources_get(struct telemetry_info *ti); +static inline struct tdcf *snap_tdcf (struct shmti_snap *s); + +static int scmi_telemetry_shmti_scan (struct telemetry_info *ti, +unsigned int shmti_id,enum scan_mode mode); + +static inline void +scmi_telemetry_de_state_update (struct telemetry_info *ti,enum de_state state, +bool *current_state,const bool next_state)
State is a bit vague for naming. Lots of state in here that isn't enabled/disabled Maybe can come up with something more specific? Also two states, one of which is called STATE and one of which is called something other than STATE is really confusing!
drivers/firmware/arm_scmi/telemetry.c struct telemetry_info { · line 502
Quoted 7 lines
drivers/firmware/arm_scmi/telemetry.c +7
-0+{ +guard (mutex)(&ti->des_enabled_mtx); +if (!current_state || *current_state != next_state) +atomic_add (next_state ?1 :-1 , &ti->des_enabled[state]); + +if (current_state) + *current_state = next_state;
I struggle a bit with understanding this function. Why do we pass in current state given it should always match ti->des_enabled[state]; If it differs and this
is doing some subtle form of refcounting then I think we need more documentation.
drivers/firmware/arm_scmi/telemetry.c struct telemetry_info {
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+ +dev_dbg (ti->ph->dev,"Telemetry des_enabled[%s]:%u\n" , + state ==ENA_STATE ?"STATE" :"TSTAMP" , +atomic_read (&ti->des_enabled[state])); +} +static void +scmi_telemetry_tde_cache_update (struct telemetry_de *tde,u64 val, +u64 *tstamp,u32 *magic)
To me, passing magic by pointer indicates that this function will update it. Whereas the use here is to indicate whether to ignore
it or not. You could mark it const, or break it into a parameter
to say if magic is ignored + magic by value.
Either way this got me a bit confused as I didn't understand why we went to effort passing in magic at the callsite below then doing nothing with the value.
Anyhow, just a small thing so ignore if you like. I may just need more coffee.
Quoted 30 lines
drivers/firmware/arm_scmi/telemetry.c +23
-0+{ +lockdep_assert_held (&tde->mtx); + +/* +* Update the cache only if stale compared to the current +* sample, so bail-out when last_magic >= *magic +*/ +if (magic && !scmi_telemetry_tde_cache_stale (tde, *magic)) +return ; + + tde->last_magic = magic ? *magic :TDCF_BAD_END_SEQ ; + tde->last_val = val; + tde->last_ts = tstamp &&scmi_tde_has_tstamp (tde) ? *tstamp :0 ; +if (tstamp) + *tstamp = tde->last_ts; +} +struct scmi_tlm_de_priv {struct telemetry_info *ti;void *next;@@ -1196,6 +1480,510 @@ scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id)return xa_load (&ti->xa_des, id); } +static inline struct payload __iomem * +scmi_telemetry_payld_offset_safe_calc (struct telemetry_shmti *shmti,u32 offs) +{ +struct payload __iomem *payld; +unsigned long p; +u32 end;
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1498
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ +/* Check if accessing payld->meta is safe. */ +if (check_add_overflow (offs,sizeof (u32), &end) || + end > shmti->info.len) +return IOMEM_ERR_PTR (-EPROTO ); + +if (check_add_overflow ((__force unsigned long )shmti->base, offs, &p)) +return IOMEM_ERR_PTR (-EPROTO ); + + payld = shmti->base + offs;
Use p rather than computing it again. Sure it needs casting and __force but none the less using a cast of p will make it clear the value is the one that was just checked for overflow.
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1508
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+/* Check line boundary */ +if (check_add_overflow (offs,LINE_LENGTH_BYTES (payld), &end) || + end > shmti->info.len) +return IOMEM_ERR_PTR (-EPROTO ); + +return payld; +} + +static u64 +scmi_telemetry_blkts_read (u32 magic,struct telemetry_de *tde)
See below, I'd bring full signature in here even though the actual implementation isn't until the next patch.
Quoted 13 lines
drivers/firmware/arm_scmi/telemetry.c +12
-0+{ +lockdep_assert_held (&tde->mtx); + +return 0 ; +} +static inline int +scmi_telemetry_line_data_parse (struct telemetry_de *tde,struct shmti_snap *snap, +u64 *val,u64 *tstamp,struct payload *payld, +struct payload *bts_payld,u32 magic) +{ +u32 meta =PAYLD_META_RAW (payld); +
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1564
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+lockdep_assert_held (&tde->mtx); + +/* Clear returned values in case of errors */ + *val =0 ; + +if (tstamp) { + *tstamp =0 ; +if (USE_BLK_TS_RAW (meta)) { +/* Read out the actual BLK_TS */ + *tstamp =scmi_telemetry_blkts_read (magic, tde);
The signature of this changes in the next patch. Given it doesn't do anything with the current parameters, it could not do anything with bts_payld and snap as well and therefore reduce churn a tiny bit.
Quoted 105 lines
drivers/firmware/arm_scmi/telemetry.c +105
-0+ }else if (LINE_TS_VALID_RAW (meta)) { +/* +* Note that LINE_TS_VALID implies HAS_LINE_EXT and that +* the per DE line_ts_rate is advertised in the DE +* descriptor, BUT we check anyway against buggy FW. +*/ +if (!HAS_LINE_EXT_RAW (meta)) { +trace_scmi_tlm_access (tde->de.info->id, +"SHMTI_BAD_TS_LINE" ,0 ,0 ); +return -EPROTO ; + } + + *tstamp =LINE_TSTAMP_GET_RAW (&payld->tsl); + } + } + +/* Data is always valid since we are NOT handling BLK TS lines here */ + *val =LINE_DATA_GET_RAW (&payld->l); + +trace_scmi_tlm_collect (tstamp ? *tstamp :0 , tde->de.info->id, + *val,"SHMTI_DE_READ" ); + +scmi_telemetry_tde_cache_update (tde, *val, tstamp, &magic); + +return 0 ; +} + +/** +* scmi_telemetry_tdcf_data_parse - TDCF DataLine parsing +* @ti: A reference to the telemetry info descriptor +* @payld: Line payload to parse +* @snap: A reference to the working SHMTI snapshot that will be processed +* @mode: A flag to determine the behaviour of the scan +* @active_bts: A pointer to keep track and report any found BLK timestamp line +* @active_uuid: A pointer to keep track and report any found UUID line +* +* This routine takes care to: +* - verify line consistency in relation to the used flags and the current +* context: e.g. is there an active preceding BLK_TS line if the DataLine +* sports a USE_BLKTS flag ? +* - verify the related Data Event ID exists OR create a brand new DE +* (depending on the @mode of operation) +* - links any active BLK_TS or UUID line to the current DE +* - read and save value/tstamp for the DE ONLY if anything has changed (by +* tracking the last TDCF magic) and update related magic: this allows to +* minimize future needs of single-DE reads +* +* Modes of operation. +* +* The scan behaviour depends on the chosen @mode: +* - SCAN_LOOKUP: the basic scan which aims to update value associated to +* existing DEs. Any discovered DataLine that could NOT be +* matched to an existing, previously discovered, DE is +* discarded. This is the normal scan behaviour. +* - SCAN_UPDATE: a more advanced scan which provides all the SCAN_LOOKUP +* features plus takes care to update the DEs location +* coordinates inside the SHMTI: note that the related DEs are +* still supposed to have been previously discovered when +* this scan runs. This is used to update location +* coordinates for DEs contained in a Group when such group +* is enabled. +* - SCAN_DISCOVERY: the most advanced scan available which provides all +* the SCAN_LOOKUP features plus discovery capabilities: +* any DataLine referring to a previously unknown DE leads +* to the allocation of a new DE descriptor. +* This mode is used on the first scan at init time, ONLY +* if Telemetry was found to be already enabled at boot on +* the platform side: this helps to maximize gathered +* information when dealing with out of spec firmwares. +* Any usage of this discovery mode other than in a boot-on +* enabled scenario is discouraged since it can easily +* lead to spurious DE discoveries. +*/ +static void scmi_telemetry_tdcf_data_parse (struct telemetry_info *ti, +struct payload *payld, +struct shmti_snap *snap, +enum scan_mode mode, +struct telemetry_block_ts *active_bts, +struct telemetry_uuid *active_uuid) +{ +struct telemetry_shmti *shmti = snap->shmti; +struct telemetry_de *tde; +u64 val, tstamp =0 ; +u32 de_id; + + de_id =PAYLD_ID_RAW (payld); +/* Is this DE ID known ? */ + tde =scmi_telemetry_tde_lookup (ti, de_id); +if (!tde) { +if (mode !=SCAN_DISCOVERY ) { +trace_scmi_tlm_access (de_id,"DE_INVALID" ,0 ,0 ); +return ; + } + +/* In SCAN_DISCOVERY mode we allocate new DEs for unknown IDs */ + tde =scmi_telemetry_tde_allocate (ti, de_id, payld); +if (!tde) { +dev_err (ti->ph->dev, +"Cannot allocate TDE for ID:0x%08X\n" , de_id); +return ; + } + } + +guard (mutex)(&tde->mtx); +/*
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1679
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+* Update DE location refs if requested: normally done only on group +* enable. +* Note that all the DE location information is relative to the +* original SHMTI device-memory area, NOT to the snapshot we are parsing. +*/ +if (mode >=SCAN_UPDATE ) { +u32 de_offs; +void __iomem *p; + +/* Offest is the same in the SHMTI and in the snapshot indeed */
Offset
Also, drop the word indeed as adds nothing useful
Quoted 47 lines
drivers/firmware/arm_scmi/telemetry.c +47
-0+ de_offs = (void __iomem *)payld - (void __iomem *)snap->base; +/* Validate location before update */ + p =scmi_telemetry_payld_offset_safe_calc (shmti, de_offs); +if (IS_ERR (p)) { +dev_err (ti->ph->dev, +"TDE 0x%08X updated location invalid - shmti:%u offs:%u\n" , + de_id, shmti->info.sid, de_offs); +trace_scmi_tlm_access (de_id,"DE_LOCATION_INVALID" ,0 ,0 ); +return ; + } + + tde->sid = shmti->info.sid; + tde->base = shmti->base; + tde->eplg =SHMTI_EPLG (shmti); + tde->offset = de_offs; + +trace_scmi_tlm_access (de_id,"DE_LOCATION_UPDATED" ,0 ,0 ); + +dev_dbg (ti->ph->dev, +"TDCF-updated DE_ID:0x%08X - shmti:%pK offset:%u\n" , + tde->de.info->id, tde->base, tde->offset); + } + +/* +* Has any value/tstamp really changed ? +* Note that, if the magic currently associated with this TDE has +* NOT changed compared to the current SHMTI magic, nothing could have +* been changed across the whole TDCF, including any relation to +* BLK_TS or UUID lines. +*/ +if (!scmi_telemetry_tde_cache_stale (tde, snap->magic)) +return ; + +/* +* Parse data words ... only for the sake of updating TDE cache, +* any error is logged by ignored. +*/ +scmi_telemetry_line_data_parse (tde, snap, &val, &tstamp, payld,NULL , + snap->magic); +} + +/** +* scmi_telemetry_tdcf_line_parse - Line parser +* @ti: A reference to the Telemetry instance +* @payld: A reference to the payload representing the line to parse +* @snap: A reference to the working SHMTI snapshot that will be processed +* @mode: Specification of the scanning mode
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1736
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+* @active_bts: A reference used to track the last BLK_TS line found +* @active_uuid: A reference used to track the last UUID line found +* +* Note that @active_bts and @active_uuid references are propagated back and +* forth from the line parsers to keep track of the nearest active BLK_TS or +* UUID line found in the SHMTI areas and, while doing that, such descriptors +* are refcounted and tracked in the xa_lines XArray indexed by @payld. +* BLK_TS and UUID descriptors are dropped when no more linked to any DATA line, +* during the normal system operation OR later on when the protocol is +* de-initialized following a driver removal.
Is this description premature given nothing writes active_bts or active_uuid yet.
Quoted 21 lines
drivers/firmware/arm_scmi/telemetry.c +21
-0+* +* Return: Number of QWORDS consumed from @payld by this line parsing; note +* that buggy firmware could return bad, out of range values, so this +* value MUST be carefully checked for SHMTI boundary overflows by the +* caller. +*/ +static int scmi_telemetry_tdcf_line_parse (struct telemetry_info *ti, +struct payload *payld, +struct shmti_snap *snap, +enum scan_mode mode, +struct telemetry_block_ts **active_bts, +struct telemetry_uuid **active_uuid) +{ +u32 meta =le32_to_cpu (payld->meta); +int used_qwords; + + used_qwords =LINE_LENGTH_QWORDS_RAW (meta); +/* Invalid lines are not an error, could simply be disabled DEs */ +if (DATA_INVALID_RAW (meta)) { +trace_scmi_tlm_access (le32_to_cpu (payld->id),"TDCF_INVALID" ,0 ,0 ); +return used_qwords;
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1767
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ } + +/* +* Problematic lines are ignored, their information discarded, and such +* issues are logged, but the scan process continues. +*/ +switch (LINE_TYPE_RAW (meta)) { +case TDCF_DATA_LINE : +scmi_telemetry_tdcf_data_parse (ti, payld, snap, mode, + *active_bts, *active_uuid);
I'm going to assume that *active_bts and *active_uuid become non null as a result of some future change and the odd current state of code here is just a result of patch break up. At the a copy of the pointer value which is NULL is simply passed in here I think, so no updates possible.
Quoted 57 lines
drivers/firmware/arm_scmi/telemetry.c +54
-0+break ; +default : +trace_scmi_tlm_access (PAYLD_ID_RAW (payld),"TDCF_LINE_UNKNOWN" ,0 ,0 ); +break ; + } + +return used_qwords; +} + +/** +* scmi_telemetry_shmti_scan - Full SHMTI scan +* @ti: A reference to the telemetry info descriptor +* @shmti_id: ID of the SHMTI area that has to be scanned +* @mode: A flag to determine the behaviour of the scan +* +* Return: 0 on Success +*/ +static int scmi_telemetry_shmti_scan (struct telemetry_info *ti, +unsigned int shmti_id,enum scan_mode mode) +{ +struct telemetry_shmti *shmti = &ti->shmti[shmti_id]; +struct telemetry_block_ts *active_bts =NULL ; +struct telemetry_uuid *active_uuid =NULL ; +unsigned int qwords; +struct shmti_snap *snap; +struct tdcf *tdcf; +int idx, ret =0 ; +void *next; + +if (!shmti->base) +return -ENODEV ; + + idx =srcu_read_lock (&shmti->srcu); + snap =srcu_dereference (shmti->cur, &shmti->srcu); +if (!snap || !scmi_telemetry_shmti_unchanged (shmti, snap->magic)) { +/* MUST drop the read lock: refresh calls synchronize_srcu() */ +srcu_read_unlock (&shmti->srcu, idx); + snap =scmi_telemetry_shmti_refresh (shmti, &idx); +if (IS_ERR (snap)) +return PTR_ERR (snap); + } + + tdcf =snap_tdcf (snap); + next = tdcf->payld; + qwords =QWORDS_RAW (tdcf); +while (qwords) { +int used_qwords; + +/* +* Bailout on any error since we are processing a supposedly +* stable snapshot at this point, so any further error is on +* firmware-side and NOT recoverable: this would mean having to +* drop the remaining part of the scan results after having +* cached the initial good snapshot: log this down below.
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1963
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+*/ +if (scmi_telemetry_snap_overflow (snap, next)) { +trace_scmi_tlm_access (0 ,"SHMTI_OVERFLOW" , snap->magic,0 ); + ret = -EPROTO ; +break ; + } + + used_qwords =scmi_telemetry_tdcf_line_parse (ti, next, snap, mode, + &active_bts, &active_uuid); +if (used_qwords <=0 || qwords < used_qwords) {
If you hit the second condition here didn't you just overflow in the line parser? Should that be prevented inside the parser? I think the only way this can happen is qwords is short. If that is the case I think all bets are off and bale out with no idea if the line was safe or not - ideally detect that in the overflow check before parsing? Maybe I'm missing some other reason that that can't happen an in practice that second condition doesn't happen.
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_lookup(const struct scmi_protocol_handle *ph, u32 id) · line 1975
Quoted 12 lines
drivers/firmware/arm_scmi/telemetry.c +12
-0+/* +* Stop scanning when qwords inconsistent; if the last +* line processed was sane in itself, though, keep it +* since we anyway previously checked for overflow. +*/ +trace_scmi_tlm_access (PAYLD_ID_RAW (next),"BAD_QWORDS" , + snap->magic,0 ); + ret = -EPROTO ; +break ; + } + + next += used_qwords *8 ;
Is next always aligned? If so could use a u64 * instead of void * as it is always manipulated in qwords.
Quoted 11 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ qwords -= used_qwords; + } + +srcu_read_unlock (&shmti->srcu, idx); + +if (ret) +dev_err (ti->ph->dev,FW_BUG "Inconsistent TDCF. Scan aborted.\n" ); + +return ret; +}
Cristian Marussi
reviewerPATCH v15 15/35 firmware: arm_scmi: Add Telemetry TDCF parsing for BLK_TS lines
In reply to Cristian MarussiAdd support to parse, track and link TDCF BLK_TS lines to the related discovered DataEvents.
---
v14 --> v15
- use proper cast on tlm_collect
- use snapshots to track and collect BLK_TS during full scan bulk reads
- fix too early invocation xa_destroy(xa_lines)
v13 --> v14
- split out of previous single patch implementing parse SHMTI routines
--- drivers/firmware/arm_scmi/telemetry.c | 317 +++++++++++++++++++++++++- 1 file changed, 313 insertions(+), 4 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +313 -4
@@ -12,6 +12,7 @@#include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/hash.h> #include <linux/io.h> #include <linux/limits.h> #include <linux/minmax.h>
@@ -160,6 +161,11 @@ struct line {__le32 data_high; }; +struct blk_tsline { +__le32 ts_low; +__le32 ts_high; +}; +struct tsline {__le32 data_low;__le32 data_high;
@@ -264,6 +270,7 @@ struct payload {union {struct line l;struct tsline tsl; +struct blk_tsline blk_tsl; }; };
@@ -490,6 +497,12 @@ static inline struct tdcf *snap_tdcf(struct shmti_snap *s);static int scmi_telemetry_shmti_scan (struct telemetry_info *ti,unsigned int shmti_id,enum scan_mode mode); +static inline +struct mutex *lock_for_payld (struct mutex *locks,unsigned long offset) +{ +return &locks[hash_long (offset,PAYLD_HASH_BITS )]; +} +static inline void scmi_telemetry_de_state_update (struct telemetry_info *ti,enum de_state state,bool *current_state,const bool next_state)
@@ -1381,7 +1394,10 @@ static void scmi_telemetry_shmti_cleanup(void *arg)kvfree (shmti->snaps[0 ]);kvfree (shmti->snaps[1 ]); -xa_destroy (&shmti->xa_lines); +/* +* Note that xa_lines will be destroyed later on instance cleanup +* since it has to be scanned to free any remaining unbound line. +*/ } static int scmi_telemetry_shmti_init(struct device *dev,
@@ -1505,11 +1521,235 @@ scmi_telemetry_payld_offset_safe_calc(struct telemetry_shmti *shmti, u32 offs) }static u64 -scmi_telemetry_blkts_read (u32 magic,struct telemetry_de *tde) +scmi_telemetry_blkts_read (u32 magic,struct telemetry_de *tde, +struct payload *bts_payld,struct shmti_snap *snap) { +struct telemetry_block_ts *bts; + +/* Acquiring order is: tde -> line */ lockdep_assert_held (&tde->mtx); +if (WARN_ON (!tde->bts || (!bts_payld && !snap))) +return 0 ; -return 0 ; + bts = tde->bts; +guard (mutex)(&bts->line.mtx); + +if (bts->line.last_magic == magic) +return bts->last_ts; + +/* Unless specified otherwise, use the snapshot BLK_TS paylod */ +if (!bts_payld) { +void *base =snap_tdcf (snap); + + bts_payld = base + bts->line.offset; + } + +/* Note that the bts->last_rate can change ONLY on creation */ + bts->last_ts =BLK_TS_STAMP_RAW (&bts_payld->blk_tsl); + bts->line.last_magic = magic; + +return bts->last_ts; +} + +static void +scmi_telemetry_blkts_update_snap (struct telemetry_info *ti, +struct telemetry_block_ts *bts, +struct payload *bts_payld,u32 magic) +{ +guard (mutex)(&bts->line.mtx); +if (bts->line.last_magic != magic) { + bts->last_ts =BLK_TS_STAMP_RAW (&bts_payld->blk_tsl); + bts->last_rate =BLK_TS_RATE_RAW (bts_payld); +/* BLK_TS clock rate value can change ONLY here on creation */ +if (!bts->last_rate) + bts->last_rate = ti->default_blk_ts_rate; + bts->line.last_magic = magic; + +trace_scmi_tlm_access (0 ,"BLK_TS_UPDATE" , magic,0 ); + } +} + +static inline void scmi_telemetry_line_free (struct telemetry_line *line) +{ +void *blob; + +switch (line->type) { +case TDCF_BLK_TS_LINE : + blob =to_blkts (line); +kfree (blob); +break ; +default : +WARN_ON (1 ); +break ; + } +} + +static void __scmi_telemetry_line_put (struct telemetry_line *line) +{ +lockdep_assert_held (&line->shmti->lines_mtx); + +mutex_lock (&line->mtx); +if (refcount_dec_and_test (&line->users)) { +xa_erase (&line->shmti->xa_lines, line->offset); + +/* Unlock and free the containing structure: blkts or uuid */ +mutex_unlock (&line->mtx); + +trace_scmi_tlm_access (0 ,"DROP_LINE" , line->type, line->last_magic); +scmi_telemetry_line_free (line); + +return ; + } +mutex_unlock (&line->mtx); +} + +static void scmi_telemetry_line_put (struct telemetry_line *line) +{ +/* Acquiring coarse grain first ... */ +guard (mutex)(&line->shmti->lines_mtx); +__scmi_telemetry_line_put (line); +} + +static void scmi_telemetry_blkts_unlink (struct telemetry_de *tde) +{ +/* Acquiring order is: tde -> line */ +lockdep_assert_held (&tde->mtx); +if (!tde->bts) +return ; + +scmi_telemetry_line_put (&tde->bts->line); + tde->bts =NULL ; + +trace_scmi_tlm_access (tde->de.info->id,"BLKTS_UNLINK" ,0 ,0 ); +} + +static void scmi_telemetry_de_unlink (struct scmi_telemetry_de *de) +{ +struct telemetry_de *tde =to_tde (de); + +lockdep_assert_held (&tde->mtx); + +/* Unlink all related lines triggering their deallocation */ +scmi_telemetry_blkts_unlink (tde); +} + +static struct telemetry_line * +scmi_telemetry_line_get (struct telemetry_shmti *shmti,unsigned long offset) +{ +struct telemetry_line *line; + +/* Acquiring coarse grain first ... */ +guard (mutex)(&shmti->lines_mtx); + line =xa_load (&shmti->xa_lines, offset); +if (!line) +return NULL ; + +guard (mutex)(&line->mtx); +refcount_inc (&line->users); + +return line; +} + +static int +scmi_telemetry_line_init (struct telemetry_shmti *shmti,struct telemetry_line *line, +unsigned long offset,enum tdcf_line_types type) +{ + line->type = type; +refcount_set (&line->users,1 ); + line->offset = offset; + line->shmti = shmti; +mutex_init (&line->mtx); + +guard (mutex)(&shmti->lines_mtx); +return xa_insert (&shmti->xa_lines, offset, line,GFP_KERNEL ); +} + +static struct telemetry_block_ts * +scmi_telemetry_blkts_create (struct telemetry_shmti *shmti,unsigned long offset) +{ +struct telemetry_block_ts *bts; +int ret; + + bts =kzalloc_obj (*bts); +if (!bts) +return NULL ; + + ret =scmi_telemetry_line_init (shmti, &bts->line, offset,TDCF_BLK_TS_LINE ); +if (ret) { +kfree (bts); +return NULL ; + } + +trace_scmi_tlm_collect (0 , shmti->info.sid, offset,"SHMTI_NEW_BLKTS" ); + +return bts; +} + +static struct telemetry_block_ts * +scmi_telemetry_blkts_get_or_create (struct telemetry_shmti *shmti, +unsigned long offset) +{ +struct telemetry_line *line; + +/* Avoid races on line creation for this payld */ +guard (mutex)(lock_for_payld (shmti->payld_locks, offset)); + + line =scmi_telemetry_line_get (shmti, offset); +if (line) +return to_blkts (line); + +return scmi_telemetry_blkts_create (shmti, offset); +} + +/** +* scmi_telemetry_tdcf_blkts_parse - A BLK_TS line parser +* +* @ti: A reference to the telemetry_info descriptor +* @bts_payld: TDCF payld line to process +* @snap: A reference to the working SHMTI snapshot that will be processed +* @active_bts: Input/output reference to keep track of the last blk_ts found +* +* Process a valid TDCF BLK_TS line and, after having looked up or created a +* blk_ts descriptor, update the related data and return it as the currently +* active blk_ts, given that it is effectively the last found during this +* scan. +*/ +static void scmi_telemetry_tdcf_blkts_parse (struct telemetry_info *ti, +struct payload *bts_payld, +struct shmti_snap *snap, +struct telemetry_block_ts **active_bts) +{ +struct telemetry_block_ts *bts, *previous = *active_bts; +struct telemetry_shmti *shmti = snap->shmti; +u32 meta =PAYLD_META_RAW (bts_payld); +unsigned long offset; + +/* Check for spec compliance */ +if (BLK_TS_INVALID_RAW (meta)) { +trace_scmi_tlm_access (0 ,"BLK_TS_INVALID" ,0 , meta); +return ; + } + + offset =TDCF_OFFS (snap->base, bts_payld); +/* A BLK_TS descriptor MUST be returned: it is found or it is created */ + bts =scmi_telemetry_blkts_get_or_create (shmti, offset); +/* +* On error processing the new BLK_TS bailout taking care to clear +* the previous last active BLK_TS to avoid mistakenly binding to the +* next data lines. +*/ +if (WARN_ON (!bts)) + goto out; + +/* Update the descriptor with the lastest TS */ +scmi_telemetry_blkts_update_snap (ti, bts, bts_payld, snap->magic); + +out: +/* Drop one refcount on the previous active since we found a new one */ +if (previous) +scmi_telemetry_line_put (&previous->line); + + *active_bts = bts; } static inline struct telemetry_de *
@@ -1561,7 +1801,8 @@ scmi_telemetry_line_data_parse(struct telemetry_de *tde, struct shmti_snap *snap *tstamp =0 ;if (USE_BLK_TS_RAW (meta)) {/* Read out the actual BLK_TS */ - *tstamp =scmi_telemetry_blkts_read (magic, tde); + *tstamp =scmi_telemetry_blkts_read (magic, tde, + bts_payld, snap); }else if (LINE_TS_VALID_RAW (meta)) {/* * Note that LINE_TS_VALID implies HAS_LINE_EXT and that
@@ -1589,6 +1830,26 @@ scmi_telemetry_line_data_parse(struct telemetry_de *tde, struct shmti_snap *snapreturn 0 ; } +static inline void scmi_telemetry_blkts_link (struct telemetry_de *tde, +struct telemetry_block_ts *bts) +{ +/* Acquiring order is: tde -> line */ +lockdep_assert_held (&tde->mtx); + +/* Unlink old stale BTS if existent */ +scmi_telemetry_blkts_unlink (tde); + +guard (mutex)(&bts->line.mtx); +refcount_inc (&bts->line.users); + +/* Update TS clock rate if provided by the BLK_TS */ +if (bts->last_rate) + tde->de.info->ts_rate = bts->last_rate; + + tde->bts = bts; +trace_scmi_tlm_access (tde->de.info->id,"BLKTS_LINK" ,0 ,0 ); +} +/** * scmi_telemetry_tdcf_data_parse - TDCF DataLine parsing * @ti: A reference to the telemetry info descriptor
@@ -1643,11 +1904,19 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti, struct telemetry_uuid *active_uuid) {struct telemetry_shmti *shmti = snap->shmti; +u32 meta =PAYLD_META_RAW (payld); +bool use_blk_ts =USE_BLK_TS_RAW (meta);struct telemetry_de *tde;u64 val, tstamp =0 ;u32 de_id; de_id =PAYLD_ID_RAW (payld); +/* Discard malformed lines...a preceding BLK_TS must exist */ +if (use_blk_ts && !active_bts) { +trace_scmi_tlm_access (de_id,"BAD_USE_BLK_TS" ,0 ,0 ); +return ; + } +/* Is this DE ID known ? */ tde =scmi_telemetry_tde_lookup (ti, de_id);if (!tde) {
@@ -1710,6 +1979,15 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,if (!scmi_telemetry_tde_cache_stale (tde, snap->magic))return ; +/* +* Link the related BTS when needed, it's unlinked on disable. +* Note that each DEs timestamp types is statically defined and +* enumerated once for all at init: a Data line cannot suddenly +* 'migrate' to a different timestamp type. +*/ +if (use_blk_ts &&BLK_TS_CHANGED (tde, shmti, active_bts->line.offset)) +scmi_telemetry_blkts_link (tde, active_bts); +/* * Parse data words ... only for the sake of updating TDE cache, * any error is logged by ignored.
@@ -1766,6 +2044,9 @@ static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,scmi_telemetry_tdcf_data_parse (ti, payld, snap, mode, *active_bts, *active_uuid);break ; +case TDCF_BLK_TS_LINE : +scmi_telemetry_tdcf_blkts_parse (ti, payld, snap, active_bts); +break ;default :trace_scmi_tlm_access (PAYLD_ID_RAW (payld),"TDCF_LINE_UNKNOWN" ,0 ,0 );break ;
@@ -1906,6 +2187,15 @@ scmi_telemetry_shmti_refresh(struct telemetry_shmti *shmti, int *idx)return work; } +static inline void +scmi_telemetry_reset_active_lines (struct telemetry_block_ts **active_bts) +{ +if (*active_bts) { +scmi_telemetry_line_put (&(*active_bts)->line); + *active_bts =NULL ; + } +} +/** * scmi_telemetry_shmti_scan - Full SHMTI scan * @ti: A reference to the telemetry info descriptor
@@ -1976,6 +2266,7 @@ static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, qwords -= used_qwords; } +scmi_telemetry_reset_active_lines (&active_bts);srcu_read_unlock (&shmti->srcu, idx);if (ret)
@@ -2120,6 +2411,22 @@ static void scmi_telemetry_resources_free(struct telemetry_info *ti)dev_dbg (ti->ph->dev,"SCMI Telemetry resources freed for instance\n" ); } +static void scmi_telemetry_lines_cleanup (struct telemetry_info *ti) +{ +for (int id =0 ; id < ti->num_shmti; id++) { +struct telemetry_shmti *shmti = &ti->shmti[id]; +struct telemetry_line *line; +unsigned long idx; + +guard (mutex)(&shmti->lines_mtx); +/* Drop all remaining currently unbound lines and their containers */ +xa_for_each (&shmti->xa_lines, idx, line) +__scmi_telemetry_line_put (line); + +xa_destroy (&shmti->xa_lines); + } +} +static void scmi_telemetry_tdes_cleanup (struct telemetry_info *ti) {struct scmi_telemetry_de *de;
@@ -2136,6 +2443,7 @@ static void scmi_telemetry_tdes_cleanup(struct telemetry_info *ti)guard (mutex)(&tde->mtx); +scmi_telemetry_de_unlink (&tde->de);scmi_telemetry_free_tde_put (ti, tde); }
@@ -2163,6 +2471,7 @@ static void scmi_telemetry_instance_cleanup(void *arg)scmi_telemetry_groups_free (ti->rinfo);scmi_telemetry_tdes_cleanup (ti); +scmi_telemetry_lines_cleanup (ti);scmi_telemetry_resources_free (ti); }
Signature · 1 line
2.54.0
Re: [PATCH v15 15/35] firmware: arm_scmi: Add Telemetry TDCF parsing for BLK_TS lines
Quoted 4 lines · On Mon, 9 Nov 2026 09:12:01 +0000 Cristian Marussi <[email protected]>…
Add support to parse, track and link TDCF
BLK_TSlines to the related discovered DataEvents.Signed-off-by Cristian Marussi <[email protected]>
A few minor things inline. + I think some of previous patch belongs in here. Getting patch break up perfect is sometimes a pain, but it really helps keep things logically together for reviewers and on a massive set like this, anything that keeps the reviewers happy is the way to go!
Jonathan
Quoted 9 lines
--- drivers/firmware/arm_scmi/telemetry.c | 317 +++++++++++++++++++++++++- 1 file changed, 313 insertions(+), 4 deletions(-)drivers/firmware/arm_scmi/telemetry.c +0
-0
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_payld_offset_safe_calc(struct telemetry_shmti *shmti, u32 offs) · line 1579
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+ +static inline void scmi_telemetry_line_free (struct telemetry_line *line) +{ +void *blob; + +switch (line->type) { +case TDCF_BLK_TS_LINE : + blob =to_blkts (line); +kfree (blob);
The local variable isn't adding much over
kfree(to_blkts(line));
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +6
-0+break ; +default : +WARN_ON (1 ); +break ; + } +}
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_payld_offset_safe_calc(struct telemetry_shmti *shmti, u32 offs) · line 1673
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +8
-0+ +static struct telemetry_block_ts * +scmi_telemetry_blkts_create (struct telemetry_shmti *shmti,unsigned long offset) +{ +struct telemetry_block_ts *bts; +int ret; + + bts =kzalloc_obj (*bts);
Use a __free() here and then no_free_ptr() on the return.
Quoted 50 lines
drivers/firmware/arm_scmi/telemetry.c +49
-0+if (!bts) +return NULL ; + + ret =scmi_telemetry_line_init (shmti, &bts->line, offset,TDCF_BLK_TS_LINE ); +if (ret) { +kfree (bts); +return NULL ; + } + +trace_scmi_tlm_collect (0 , shmti->info.sid, offset,"SHMTI_NEW_BLKTS" ); + +return bts; +} + +/** +* scmi_telemetry_tdcf_blkts_parse - A BLK_TS line parser +* +* @ti: A reference to the telemetry_info descriptor +* @bts_payld: TDCF payld line to process +* @snap: A reference to the working SHMTI snapshot that will be processed +* @active_bts: Input/output reference to keep track of the last blk_ts found +* +* Process a valid TDCF BLK_TS line and, after having looked up or created a +* blk_ts descriptor, update the related data and return it as the currently +* active blk_ts, given that it is effectively the last found during this +* scan. +*/ +static void scmi_telemetry_tdcf_blkts_parse (struct telemetry_info *ti, +struct payload *bts_payld, +struct shmti_snap *snap, +struct telemetry_block_ts **active_bts) +{ +struct telemetry_block_ts *bts, *previous = *active_bts; +struct telemetry_shmti *shmti = snap->shmti; +u32 meta =PAYLD_META_RAW (bts_payld); +unsigned long offset; + +/* Check for spec compliance */ +if (BLK_TS_INVALID_RAW (meta)) { +trace_scmi_tlm_access (0 ,"BLK_TS_INVALID" ,0 , meta); +return ; + } + + offset =TDCF_OFFS (snap->base, bts_payld); +/* A BLK_TS descriptor MUST be returned: it is found or it is created */ + bts =scmi_telemetry_blkts_get_or_create (shmti, offset); +/* +* On error processing the new BLK_TS bailout taking care to clear +* the previous last active BLK_TS to avoid mistakenly binding to the
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_payld_offset_safe_calc(struct telemetry_shmti *shmti, u32 offs) · line 1748
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+* next data lines. +*/ +if (WARN_ON (!bts)) + goto out; + +/* Update the descriptor with the lastest TS */ +scmi_telemetry_blkts_update_snap (ti, bts, bts_payld, snap->magic); + +out: +/* Drop one refcount on the previous active since we found a new one */
I'd have this mention where the reference was taken as then it's clear why it makes sense to drop one here. Something like
/*
* Drop one refcount on previous active to match with the one take
* in creation as no longer need to keep it around for potential new
* users.
*/
Quoted 8 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+if (previous) +scmi_telemetry_line_put (&previous->line); + + *active_bts = bts; } static inline struct telemetry_de *
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_line_data_parse(struct telemetry_de *tde, struct shmti_snap *snap · line 1805
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +2
-1@@ -1561,7 +1801,8 @@ scmi_telemetry_line_data_parse(struct telemetry_de *tde, struct shmti_snap *snap *tstamp =0 ;if (USE_BLK_TS_RAW (meta)) {/* Read out the actual BLK_TS */ - *tstamp =scmi_telemetry_blkts_read (magic, tde); + *tstamp =scmi_telemetry_blkts_read (magic, tde, + bts_payld, snap);
Noted in review of previous patch that I'd just have all the parameters back there rather than adding these two here. It isn't consistent in that some parameters were used and others not, as in that patch magic wasn't used either!
Quoted 21 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0}else if (LINE_TS_VALID_RAW (meta)) {/* * Note that LINE_TS_VALID implies HAS_LINE_EXT and that @@ -1906,6 +2187,15 @@ scmi_telemetry_shmti_refresh(struct telemetry_shmti *shmti, int *idx)return work; } +static inline void +scmi_telemetry_reset_active_lines (struct telemetry_block_ts **active_bts) +{ +if (*active_bts) { +scmi_telemetry_line_put (&(*active_bts)->line); + *active_bts =NULL ; + } +} +/** * scmi_telemetry_shmti_scan - Full SHMTI scan * @ti: A reference to the telemetry info descriptor
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 2269
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +1
-0@@ -1976,6 +2266,7 @@ static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, qwords -= used_qwords; } +scmi_telemetry_reset_active_lines (&active_bts);
It's only resetting one. Maybe reset_active_line?
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +0
-0srcu_read_unlock (&shmti->srcu, idx);if (ret)@@ -2120,6 +2411,22 @@ static void scmi_telemetry_resources_free(struct telemetry_info *ti)dev_dbg (ti->ph->dev,"SCMI Telemetry resources freed for instance\n" ); }
drivers/firmware/arm_scmi/telemetry.c static void scmi_telemetry_resources_free(struct telemetry_info *ti) · line 2422
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+static void scmi_telemetry_lines_cleanup (struct telemetry_info *ti) +{ +for (int id =0 ; id < ti->num_shmti; id++) { +struct telemetry_shmti *shmti = &ti->shmti[id]; +struct telemetry_line *line; +unsigned long idx; + +guard (mutex)(&shmti->lines_mtx); +/* Drop all remaining currently unbound lines and their containers */
I'd avoid the comment. It's a _put so should be clear there may be references held that keep it alive for now. The comment kind of invites questions about what it means to drop unbound ones here, and what the lifetimes of others look like.
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +6
-0+xa_for_each (&shmti->xa_lines, idx, line) +__scmi_telemetry_line_put (line); + +xa_destroy (&shmti->xa_lines); + } +}
Cristian Marussi
reviewerPATCH v15 16/35 firmware: arm_scmi: Add Telemetry TDCF parsing for UUID lines
In reply to Cristian MarussiAdd support to parse, track and link TDCF UUID lines to the related discovered DataEvents.
Maintain also a central list of discovered UUID lines.
---
v14 --> v15
- use proper casting in tlm_collect
- use snapshots to collect UUIDs during full scans
- stop tracking UUIDs lines singularly..it's useless
- use UUIDs line hashing for lookup
- clarified UUID/DE binding are permanent
- fixed WARN_ON invocation on missing active_uuid
v13 --> v14
- split out of previous single patch implementing parse SHMTI routines
--- drivers/firmware/arm_scmi/telemetry.c | 277 +++++++++++++++++++++++++- include/linux/scmi_protocol.h | 2 + 2 files changed, 277 insertions(+), 2 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +275 -2
@@ -13,11 +13,14 @@#include <linux/device.h> #include <linux/err.h> #include <linux/hash.h> +#include <linux/hashtable.h> +#include <linux/jhash.h> #include <linux/io.h> #include <linux/limits.h> #include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/rcupdate.h> #include <linux/refcount.h> #include <linux/sizes.h> #include <linux/slab.h>
@@ -173,6 +176,10 @@ struct tsline {__le32 ts_high; }; +struct uuid_line { +__be32 dwords[SCMI_TLM_DE_IMPL_NUM_DWORDS ]; +}; +#define LINE_DATA_GET_RAW (f) \ ({ \ typeof(f) _f = (f); \
@@ -271,6 +278,7 @@ struct payload {struct line l;struct tsline tsl;struct blk_tsline blk_tsl; +struct uuid_line uuid_l; }; };
@@ -399,7 +407,6 @@ struct telemetry_block_ts {#define to_blkts (l) container_of(l, struct telemetry_block_ts, line)struct telemetry_uuid { -u32 key;struct hlist_node node_ht;struct hlist_node node_lst;uuid_t uuid;
@@ -470,6 +477,12 @@ struct telemetry_info {bool streaming_mode;unsigned int num_shmti;unsigned int num_des_tstamp; +#define SCMI_UUID_DB_THRESH 3 +/* Protect uuids_len */ +struct mutex uuids_mtx; +unsigned int uuids_len; +#define SCMI_UUID_HASH_SZ 5 +DECLARE_HASHTABLE (uuids_ht,SCMI_UUID_HASH_SZ );/* Mutex to protect des_enabled_mtx */ struct mutex des_enabled_mtx;atomic_t des_enabled[ENA_MAX ];
@@ -478,6 +491,7 @@ struct telemetry_info {struct telemetry_shmti *shmti;struct telemetry_de *tdes;struct xarray xa_des; +struct telemetry_uuid *primary_uuid;/* Mutex to protect access to @free_des */ struct mutex free_mtx;struct list_head free_des;
@@ -497,6 +511,9 @@ static inline struct tdcf *snap_tdcf(struct shmti_snap *s);static int scmi_telemetry_shmti_scan (struct telemetry_info *ti,unsigned int shmti_id,enum scan_mode mode); +static inline void scmi_telemetry_uuid_link (struct telemetry_de *tde, +struct telemetry_uuid *uuid); +static inline struct mutex *lock_for_payld (struct mutex *locks,unsigned long offset) {
@@ -953,8 +970,10 @@ static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph, ti->num_des_tstamp += !!tde->de.tstamp_support;/* Add to FastChannels list */ -if (tde->de.fc_support) +if (tde->de.fc_support) { +scmi_telemetry_uuid_link (tde, ti->primary_uuid);list_add (&tde->item, &ti->fcs_des); + }/* Account for this DE in group num_de counter */ if (tde->de.grp)
@@ -1623,6 +1642,17 @@ static void scmi_telemetry_blkts_unlink(struct telemetry_de *tde)trace_scmi_tlm_access (tde->de.info->id,"BLKTS_UNLINK" ,0 ,0 ); } +static void scmi_telemetry_uuid_unlink (struct telemetry_de *tde) +{ +/* Acquiring order is: tde -> line */ +lockdep_assert_held (&tde->mtx); +if (!tde->uuid) +return ; + + tde->uuid =NULL ; +trace_scmi_tlm_access (tde->de.info->id,"UUID_UNLINK" ,0 ,0 ); +} +static void scmi_telemetry_de_unlink (struct scmi_telemetry_de *de) {struct telemetry_de *tde =to_tde (de);
@@ -1631,6 +1661,7 @@ static void scmi_telemetry_de_unlink(struct scmi_telemetry_de *de)/* Unlink all related lines triggering their deallocation */ scmi_telemetry_blkts_unlink (tde); +scmi_telemetry_uuid_unlink (tde); } static struct telemetry_line *
@@ -1701,6 +1732,149 @@ scmi_telemetry_blkts_get_or_create(struct telemetry_shmti *shmti,return scmi_telemetry_blkts_create (shmti, offset); } +static int scmi_telemetry_uuids_update (struct telemetry_info *ti, +struct telemetry_uuid *uuid) +{ +uuid_t **cur_uuids; + +/* Resize array if needed ... */ +if (ti->info.num_uuids +SCMI_UUID_DB_THRESH >= ti->uuids_len) { +uuid_t **new_uuids, **old_uuids; +unsigned int new_len = ti->uuids_len *2 ; + + new_uuids =kzalloc_objs (*new_uuids, new_len); +if (!new_uuids) +return -ENOMEM ; + + old_uuids =rcu_dereference_protected (ti->info.uuids, +lockdep_is_held (&ti->uuids_mtx)); +/* Copy/move old allocated UUIDs */ +for (int i =0 ; i < ti->info.num_uuids; i++) + new_uuids[i] = old_uuids[i]; + +/* Expose new UUID pointer */ +rcu_assign_pointer (ti->info.uuids, new_uuids); + ti->uuids_len = new_len; + +kfree_rcu_mightsleep (old_uuids); + } + +/* Store new UUID reference into the globally accessible UUIDS */ + cur_uuids =rcu_dereference_protected (ti->info.uuids, +lockdep_is_held (&ti->uuids_mtx)); + cur_uuids[ti->info.num_uuids] = &uuid->uuid; +rcu_assign_pointer (ti->info.uuids, cur_uuids); +/* Update UUIDs count */ +smp_store_release (&ti->info.num_uuids, ti->info.num_uuids +1 ); + +return 0 ; +} + +static int scmi_telemetry_uuid_register (struct telemetry_info *ti,u32 key, +struct telemetry_uuid *uuid) +{ +int ret; + +lockdep_assert_held (&ti->uuids_mtx); + + ret =scmi_telemetry_uuids_update (ti, uuid); +if (ret) +return ret; + +/* Register by key for easy lookup */ +hash_add (ti->uuids_ht, &uuid->node_ht, key); + +trace_scmi_tlm_collect (0 , key,0 ,"SHMTI_NEW_UUID" ); + +return 0 ; +} + +static struct telemetry_uuid * +scmi_telemetry_uuid_create (struct telemetry_info *ti,__be32 *dwords) +{ +u32 key; +int ret; + +lockdep_assert_held (&ti->uuids_mtx); + + struct telemetry_uuid *uuid __free(kfree) = kzalloc_obj(*uuid); +if (!uuid) +return NULL ; + +/* +* Fetch BigEndian in-memory UUID fields as per SCMIv4.0 +* specification 3.12.2.2 +*/ +import_uuid (&uuid->uuid, (__force const __u8 *)dwords); + key =jhash2 ((__force const u32 *)dwords,SCMI_TLM_DE_IMPL_NUM_DWORDS ,1 ); + ret =scmi_telemetry_uuid_register (ti, key, uuid); +if (ret) +return NULL ; + +return no_free_ptr (uuid); +} + +static struct telemetry_uuid * +scmi_telemetry_uuid_lookup (struct telemetry_info *ti,__be32 *dwords) +{ +struct telemetry_uuid *uuid; +u32 key; + +lockdep_assert_held (&ti->uuids_mtx); + + key =jhash2 ((__force const u32 *)dwords,SCMI_TLM_DE_IMPL_NUM_DWORDS ,1 ); +hash_for_each_possible (ti->uuids_ht, uuid, node_ht, key) +if (!memcmp (&uuid->uuid, dwords,sizeof (uuid->uuid))) +return uuid; + +return NULL ; +} + +static struct telemetry_uuid * +scmi_telemetry_uuid_get_or_create (struct telemetry_info *ti,__be32 *dwords) +{ +struct telemetry_uuid *uuid; + +guard (mutex)(&ti->uuids_mtx); + + uuid =scmi_telemetry_uuid_lookup (ti, dwords); +if (uuid) +return uuid; + +return scmi_telemetry_uuid_create (ti, dwords); +} + +/** +* scmi_telemetry_tdcf_uuid_parse - A UUID line parser +* +* @ti: A reference to the telemetry_info descriptor +* @uuid_payld: UUID payld line to process +* @snap: A reference to the working SHMTI snapshot that will be processed +* @active_uuid: Input/output reference to keep track of the last uuid found +* +* Note that by the SCMI v4.0 Telemetry specification the associations between +* DEs and UUIDs, both primary and secondary, are permanent across the same +* session (boot): once discovered they cannot change. +*/ +static void scmi_telemetry_tdcf_uuid_parse (struct telemetry_info *ti, +struct payload *uuid_payld, +struct shmti_snap *snap, +struct telemetry_uuid **active_uuid) +{ +__be32 *dwords = uuid_payld->uuid_l.dwords; +u32 meta =PAYLD_META_RAW (uuid_payld); + +if (UUID_INVALID_RAW (meta)) { +trace_scmi_tlm_access (0 ,"UUID_INVALID" , + snap->magic, snap->magic); +return ; + } + + *active_uuid =scmi_telemetry_uuid_get_or_create (ti, dwords); + +WARN_ON (!*active_uuid); +} +/** * scmi_telemetry_tdcf_blkts_parse - A BLK_TS line parser *
@@ -1850,6 +2024,18 @@ static inline void scmi_telemetry_blkts_link(struct telemetry_de *tde,trace_scmi_tlm_access (tde->de.info->id,"BLKTS_LINK" ,0 ,0 ); } +static inline void scmi_telemetry_uuid_link (struct telemetry_de *tde, +struct telemetry_uuid *uuid) +{ +lockdep_assert_held (&tde->mtx); +if (tde->uuid) +return ; + +/* Update UUID association */ + tde->uuid = uuid; +trace_scmi_tlm_access (tde->de.info->id,"UUID_LINK" ,0 ,0 ); +} +/** * scmi_telemetry_tdcf_data_parse - TDCF DataLine parsing * @ti: A reference to the telemetry info descriptor
@@ -1988,6 +2174,9 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,if (use_blk_ts &&BLK_TS_CHANGED (tde, shmti, active_bts->line.offset))scmi_telemetry_blkts_link (tde, active_bts); +/* Link the active UUID or the primary, it's unlinked on disable */ +scmi_telemetry_uuid_link (tde, active_uuid ?: ti->primary_uuid); +/* * Parse data words ... only for the sake of updating TDE cache, * any error is logged by ignored.
@@ -2047,6 +2236,9 @@ static int scmi_telemetry_tdcf_line_parse(struct telemetry_info *ti,case TDCF_BLK_TS_LINE :scmi_telemetry_tdcf_blkts_parse (ti, payld, snap, active_bts);break ; +case TDCF_UUID_LINE : +scmi_telemetry_tdcf_uuid_parse (ti, payld, snap, active_uuid); +break ;default :trace_scmi_tlm_access (PAYLD_ID_RAW (payld),"TDCF_LINE_UNKNOWN" ,0 ,0 );break ;
@@ -2362,6 +2554,7 @@ static int scmi_telemetry_resources_alloc(struct telemetry_info *ti) }INIT_LIST_HEAD (&ti->fcs_des); +hash_init (ti->uuids_ht); ti->shmti =no_free_ptr (shmtis); ti->info.shmtis =no_free_ptr (shmtis_info);
@@ -2427,6 +2620,37 @@ static void scmi_telemetry_lines_cleanup(struct telemetry_info *ti) } } +static void scmi_telemetry_uuids_cleanup (struct telemetry_info *ti) +{ +uuid_t **cur_uuids; + +/* Drop reference to UUID lines kept in the growable array */ + scoped_guard(mutex, &ti->uuids_mtx) { +int num_uuids; + +/* Ensure looking up an updated counter */ + num_uuids =smp_load_acquire (&ti->info.num_uuids); +/* Ensure zeroing is visible */ +smp_store_release (&ti->info.num_uuids,0 ); +/* Inhibit further accesses to cur_uuids */ + cur_uuids =rcu_replace_pointer (ti->info.uuids,NULL , +lockdep_is_held (&ti->uuids_mtx)); +synchronize_rcu (); + +for (int i =0 ; i < num_uuids; i++) { +struct telemetry_uuid *uuid; + + uuid =to_uuid_from_uuid_t (cur_uuids[i]); +hash_del (&uuid->node_ht); +kfree (uuid); + +trace_scmi_tlm_access (0 ,"DROP_LINE" ,TDCF_UUID_LINE ,0 ); + } + } + +kfree_rcu_mightsleep (cur_uuids); +} +static void scmi_telemetry_tdes_cleanup (struct telemetry_info *ti) {struct scmi_telemetry_de *de;
@@ -2471,6 +2695,7 @@ static void scmi_telemetry_instance_cleanup(void *arg)scmi_telemetry_groups_free (ti->rinfo);scmi_telemetry_tdes_cleanup (ti); +scmi_telemetry_uuids_cleanup (ti);scmi_telemetry_lines_cleanup (ti);scmi_telemetry_resources_free (ti); }
@@ -2548,6 +2773,23 @@ scmi_telemetry_resources_enumerate(struct telemetry_info *ti)return ti->rinfo; } +static int scmi_telemetry_primary_uuid_init (struct telemetry_info *ti) +{ +__be32 *dwords = (__be32 *)&ti->info.base.primary_revision.b; +struct telemetry_uuid *uuid; + + uuid =scmi_telemetry_uuid_get_or_create (ti, dwords); +if (!uuid) { +dev_err (ti->ph->dev,"Failed to register Primary UUID\n" ); +return -ENOMEM ; + } + +/* Ensure Primary UUID association is visible */ +smp_store_release (&ti->primary_uuid, uuid); + +return 0 ; +} +/** * scmi_telemetry_instance_init - Instance initializer * @ti: A reference to the telemetry info descriptor for this instance
@@ -2563,6 +2805,8 @@ scmi_telemetry_resources_enumerate(struct telemetry_info *ti)*/ static int scmi_telemetry_instance_init (struct telemetry_info *ti) { +unsigned int uuids_len; +uuid_t **cur_uuids;int ret; ret =scmi_telemetry_resources_alloc (ti);
@@ -2571,6 +2815,35 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)xa_init (&ti->xa_des); +/* +* Always allocate at least one slot for the primary and anyway at +* least enough to avoid immediate resizing, assuring uuids_len +* always greater or equal to one. +*/ + uuids_len =max (ti->num_shmti *2 ,SCMI_UUID_DB_THRESH +1 ); + cur_uuids =kzalloc_objs (*cur_uuids, uuids_len); +if (!cur_uuids) { +xa_destroy (&ti->xa_des); +scmi_telemetry_resources_free (ti); +return -ENOMEM ; + } + +mutex_init (&ti->uuids_mtx); +/* Expose new UUID pointer */ + scoped_guard(mutex, &ti->uuids_mtx) { +rcu_assign_pointer (ti->info.uuids, cur_uuids); + ti->uuids_len = uuids_len; + } + +/* Initialize the first UUIDs in the global list with the primary */ + ret =scmi_telemetry_primary_uuid_init (ti); +if (ret) { +scmi_telemetry_uuids_cleanup (ti); +xa_destroy (&ti->xa_des); +scmi_telemetry_resources_free (ti); +return ret; + } +atomic_set (&ti->des_enabled[ENA_STATE ],0 );atomic_set (&ti->des_enabled[ENA_TSTAMP ],0 );mutex_init (&ti->des_enabled_mtx);
include/linux/scmi_protocol.h +2 -0
@@ -981,6 +981,8 @@ struct scmi_telemetry_info {unsigned int active_update_interval;struct scmi_telemetry_intervals *intervals;struct scmi_telemetry_shmti_info **shmtis; +unsigned int num_uuids; + uuid_t * __rcu *uuids __counted_by_ptr(num_uuids); };/**
Signature · 1 line
2.54.0
Re: [PATCH v15 16/35] firmware: arm_scmi: Add Telemetry TDCF parsing for UUID lines
Quoted 6 lines · On Mon, 9 Nov 2026 09:12:02 +0000 Cristian Marussi <[email protected]>…
Add support to parse, track and link TDCF UUID lines to the related discovered DataEvents.
Maintain also a central list of discovered UUID lines.
Signed-off-by Cristian Marussi <[email protected]>
Trivial stuff + a follow on about how you are handling the devm_ cleanup for the de and it making your code more complex than it needs to be. I think a simple helper will get you to a better state (and my preferred approach of fine grained management is probably overkill!)
Jonathan
Quoted 30 lines
---
v14 --> v15
- use proper casting intlm_collect
- use snapshots to collect UUIDs during full scans
- stop tracking UUIDs lines singularly..it's useless
- use UUIDs line hashing for lookup
- clarified UUID/DE binding are permanent
- fixedWARN_ONinvocation on missingactive_uuid
v13 --> v14
- split out of previous single patch implementing parse SHMTI routines--- drivers/firmware/arm_scmi/telemetry.c | 277 +++++++++++++++++++++++++- include/linux/scmi_protocol.h | 2 + 2 files changed, 277 insertions(+), 2 deletions(-)drivers/firmware/arm_scmi/telemetry.c +9
-0+static int scmi_telemetry_uuids_update (struct telemetry_info *ti, +struct telemetry_uuid *uuid) +{ +uuid_t **cur_uuids; + +/* Resize array if needed ... */ +if (ti->info.num_uuids +SCMI_UUID_DB_THRESH >= ti->uuids_len) { +uuid_t **new_uuids, **old_uuids; +unsigned int new_len = ti->uuids_len *2 ;
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_blkts_get_or_create(struct telemetry_shmti *shmti, · line 1753
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ + new_uuids =kzalloc_objs (*new_uuids, new_len); +if (!new_uuids) +return -ENOMEM ; + + old_uuids =rcu_dereference_protected (ti->info.uuids, +lockdep_is_held (&ti->uuids_mtx)); +/* Copy/move old allocated UUIDs */ +for (int i =0 ; i < ti->info.num_uuids; i++) + new_uuids[i] = old_uuids[i];
memcpy?
Quoted 22 lines
drivers/firmware/arm_scmi/telemetry.c +18
-0+ +/* Expose new UUID pointer */ +rcu_assign_pointer (ti->info.uuids, new_uuids); + ti->uuids_len = new_len; + +kfree_rcu_mightsleep (old_uuids); + } + +/* Store new UUID reference into the globally accessible UUIDS */ + cur_uuids =rcu_dereference_protected (ti->info.uuids, +lockdep_is_held (&ti->uuids_mtx)); + cur_uuids[ti->info.num_uuids] = &uuid->uuid; +rcu_assign_pointer (ti->info.uuids, cur_uuids); +/* Update UUIDs count */ +smp_store_release (&ti->info.num_uuids, ti->info.num_uuids +1 ); + +return 0 ; +}
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_resources_alloc(struct telemetry_info *ti) · line 2557
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +1
-0@@ -2362,6 +2554,7 @@ static int scmi_telemetry_resources_alloc(struct telemetry_info *ti) }INIT_LIST_HEAD (&ti->fcs_des); +hash_init (ti->uuids_ht);
Why init the hash here but the mutex at the caller? I guess maybe you wanted it to be obvious the mutex was valid, but to me just doing mutex_init up here is easier.
Quoted 11 lines
drivers/firmware/arm_scmi/telemetry.c +0
-0ti->shmti =no_free_ptr (shmtis); ti->info.shmtis =no_free_ptr (shmtis_info);@@ -2427,6 +2620,37 @@ static void scmi_telemetry_lines_cleanup(struct telemetry_info *ti) } }static void scmi_telemetry_tdes_cleanup (struct telemetry_info *ti) {struct scmi_telemetry_de *de;
drivers/firmware/arm_scmi/telemetry.c static void scmi_telemetry_instance_cleanup(void *arg) · line 2698
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +1
-0@@ -2471,6 +2695,7 @@ static void scmi_telemetry_instance_cleanup(void *arg)scmi_telemetry_groups_free (ti->rinfo);scmi_telemetry_tdes_cleanup (ti); +scmi_telemetry_uuids_cleanup (ti);
See below. The monolithic approach to devm cleanup is making your setup code harder to read. Suggestions down there on how to avoid that.
Quoted 20 lines
drivers/firmware/arm_scmi/telemetry.c +0
-0scmi_telemetry_lines_cleanup (ti);scmi_telemetry_resources_free (ti); }/**
*scmi_telemetry_instance_init- Instance initializer
* @ti: A reference to the telemetry info descriptor for this instancedrivers/firmware/arm_scmi/telemetry.c +2
-0@@ -2563,6 +2805,8 @@ scmi_telemetry_resources_enumerate(struct telemetry_info *ti)*/ static int scmi_telemetry_instance_init (struct telemetry_info *ti) { +unsigned int uuids_len; +uuid_t **cur_uuids;int ret; ret =scmi_telemetry_resources_alloc (ti);@@ -2571,6 +2815,35 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)xa_init (&ti->xa_des);
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_instance_init(struct telemetry_info *ti) · line 2827
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+/* +* Always allocate at least one slot for the primary and anyway at +* least enough to avoid immediate resizing, assuring uuids_len +* always greater or equal to one. +*/ + uuids_len =max (ti->num_shmti *2 ,SCMI_UUID_DB_THRESH +1 ); + cur_uuids =kzalloc_objs (*cur_uuids, uuids_len); +if (!cur_uuids) { +xa_destroy (&ti->xa_des); +scmi_telemetry_resources_free (ti);
This sort of dance of not using a goto block because we
have unified cleanup at the end always suggests to me one of two things. Finer grained devm_ callbacks, or a helper that can have a clean return to this outer function with no side effects, those having all been cleaned up via goto based unified error handling in the helper.
I personally prefer fine grained devm with each element clearly being paired with the undo, but the helper is perhaps easier to introduce here.
Quoted 35 lines
drivers/firmware/arm_scmi/telemetry.c +19
-0+return -ENOMEM ; + } + +mutex_init (&ti->uuids_mtx); +/* Expose new UUID pointer */ + scoped_guard(mutex, &ti->uuids_mtx) { +rcu_assign_pointer (ti->info.uuids, cur_uuids); + ti->uuids_len = uuids_len; + } + +/* Initialize the first UUIDs in the global list with the primary */ + ret =scmi_telemetry_primary_uuid_init (ti); +if (ret) { +scmi_telemetry_uuids_cleanup (ti); +xa_destroy (&ti->xa_des); +scmi_telemetry_resources_free (ti); +return ret; + } +atomic_set (&ti->des_enabled[ENA_STATE ],0 );atomic_set (&ti->des_enabled[ENA_TSTAMP ],0 );mutex_init (&ti->des_enabled_mtx);include/linux/scmi_protocol.h +2
-0@@ -981,6 +981,8 @@ struct scmi_telemetry_info {unsigned int active_update_interval;struct scmi_telemetry_intervals *intervals;struct scmi_telemetry_shmti_info **shmtis; +unsigned int num_uuids; + uuid_t * __rcu *uuids __counted_by_ptr(num_uuids); };/**
Cristian Marussi
reviewerPATCH v15 17/35 firmware: arm_scmi: Add Telemetry configuration operations
In reply to Cristian MarussiAdd support for basic Telemetry configuration operations to selectively enable or disable DataEvents monitoring.
---
v14 --> v15
- unlink BLK_TS on DE disable
- update linked BLK_TS on enable
- update TDE location ONLY after successfull linked lines processing
- skip tstamp state update when tstamp NOT supported
- fix TDE location returned value on error path
- trigger resource enumeration on state_get
- trigger resource enumeration on colletion configure only for groups
- use rinfo->num_groups to check groups sanity
- fix per-group collection_configure calls
- fix streaming_mode variable handling
- widen tde->mtx locking section on state_set
- refactor UUID/BLK_TS lines processing in enable
- introduce BLK_TS localized snapshot during enable
- check line type at de_offs
- sanitize requested mode on collection conf
- missing err init
- moved away definition of scmi_telemetry_payload_offset_safe_calc
v13 --> v14
- move BLK_ST_CHANGED check when bts_payld is really known
- check lso LINE_TYPE when processing BLK_TS on enable
- grab individual tde->mtx while updating group state
- clear returned sid/offset/uid on error path
- do NOT mutate global config when configuring groups
- call state_update on do_xfer success even if response process fails
- moved some definitions from earlier split patch
- renamed some fields to more strictly adhere to spec nomenclature
v12 --> v13
- refactored cleanup path
- generalize payload_offset_safe_calc
- harden nearest_line_type scanner
- full refactoring of SET/GET helpers: keep DE and group in distinct code path
- relocate tde->mtx guards higher up and add lockdep assertions
- cleanup xa_lines under mutex_lock on cleanup
- unlink stale BTS line when finding a new actibe for the TDE
- use *alloc_objs
v11 --> v12
- added a few locking order acquisition comments
- added more checks on wraparounds and alignment
- added proper locking around TDE access on state_get
- added a config_mtx to serialize configuration accesses between collection configure and state_set
- refactored state_cache_update
- added locking around sid/offset/uuid updates on enable
v10 --> v11
- refactor reply processing code on DE enable/disable
- use the new des_ennabled mutex to protect des cache updates
- clear sid on group disable for each composing DEs
- add dev_warn_once on ZERO blk_ts offset
v8 --> v9
- use new scmi_telemetry_shmti_overflow() inline
- review DE offsets boundary and wraparound checks (Sashiko v8
- update per-DE ts_rate for DE using BLK_TS too
v7 --> v8
- Fixes for some Sashiko issues on v7
- re-add functions unused in previous patch
- add proper barriers to TDE/UUID binding process
- update tde->sid on SCAN_UPDATE too
- check wrap-arounds on shmti_de_offset manipulation
- fix GROUP enable pollutes status update (was polluting global state)
- remove .all_disable telemetry_operation since unused
- add WARN on if a valid SHMTI ID is reported on enabling a FastChannel/DE
v6 --> v7
- expose per-DE tracked sid/offset/UUIDs
- trigger a full SHMTI scan when a SHMTI-based is enabled and it still has NO associated UUID
v5 --> v6
- constify a few booleans used as inpurt in telemetry_operations
- fixes (some) Sashiko reviews
https://sashiko.dev/#/patchset/20260703123601.381275-1-cristian.marussi%40arm.com?part=9
- underflow on de_offs
- SHMTI overflow checks
- bail out on failed group scan
- unlink comprising DEs on group disable
- add boudnary check on message response parsing
v4 --> v5
- use renamed grp_id instead of old id in group descriptors
- remove useless grp_ignore param from collection_configure
- fixes sparse warnings
v3 --> v4
- track configured Telemetry DE states
- add aggregate Telemetry state query
v2 --> v3
- split from monolithic Telemetry patch
- simplify clenaup with scmi_telemetry_de_unlink
--- drivers/firmware/arm_scmi/telemetry.c | 907 +++++++++++++++++++++++++- include/linux/scmi_protocol.h | 27 + 2 files changed, 932 insertions(+), 2 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +905 -2
@@ -133,6 +133,49 @@ struct scmi_msg_resp_telemetry_update_intervals {__le32 intervals[]; }; +struct scmi_msg_telemetry_de_configure { +__le32 identifier; +__le32 flags; +#define CFG_ENABLE_NO_TSTAMP BIT(0) +#define CFG_ENABLE_WTH_TSTAMP BIT(1) +#define CFG_DISABLE_ALL BIT(2) +#define CFG_GROUP_SELECTOR BIT(3) +#define CFG_EVENT_DE 0 +#define CFG_EVENT_GROUP 1 +#define CFG_DISABLE_ONE 0x0 +}; + +struct scmi_msg_resp_telemetry_de_configure { +__le32 shmti_id; +#define SHMTI_ID_INVALID GENMASK(31, 0) +#define IS_SHMTI_ID_VALID (x) ((x) != SHMTI_ID_INVALID) +__le32 shmti_de_offset; +__le32 blk_ts_offset; +}; + +struct scmi_msg_telemetry_config_set { +__le32 group_identifier; +__le32 control; +#define TELEMETRY_ENABLE (BIT(0)) +#define TELEMETRY_MODE GENMASK(4, 1) +#define TELEMETRY_MODE_SET (x) (FIELD_PREP(TELEMETRY_MODE, (x))) +#define TLM_ONDEMAND (0) +#define TLM_NOTIFS (1) +#define TLM_SINGLE (2) +#define TELEMETRY_MODE_ONDEMAND TELEMETRY_MODE_SET(TLM_ONDEMAND) +#define TELEMETRY_MODE_NOTIFS TELEMETRY_MODE_SET(TLM_NOTIFS) +#define TELEMETRY_MODE_SINGLE TELEMETRY_MODE_SET(TLM_SINGLE) + +#define TLM_ORPHANS (0) +#define TLM_GROUP (1) +#define TLM_ALL (2) +#define TELEMETRY_SET_SELECTOR (x) (FIELD_PREP(GENMASK(8, 5), (x))) +#define TELEMETRY_SET_SELECTOR_ORPHANS TELEMETRY_SET_SELECTOR(TLM_ORPHANS) +#define TELEMETRY_SET_SELECTOR_GROUP TELEMETRY_SET_SELECTOR(TLM_GROUP) +#define TELEMETRY_SET_SELECTOR_ALL TELEMETRY_SET_SELECTOR(TLM_ALL) +__le32 sampling_rate; +}; +/* TDCF */ #define _I (__a) (ioread32((void __iomem *)(__a)))
@@ -435,8 +478,6 @@ enum timestamps { TSTAMP_BLK = 2, }; -#define SHMTI_ID_INVALID GENMASK(31, 0) -struct telemetry_de {enum timestamps ts_type;u32 ts_rate;
@@ -496,6 +537,8 @@ struct telemetry_info {struct mutex free_mtx;struct list_head free_des;struct list_head fcs_des; +/* Protect global and per-group configuration data */ +struct mutex config_mtx;struct scmi_telemetry_info info;atomic_t rinfo_initializing;struct completion rinfo_initdone;
@@ -1875,6 +1918,70 @@ static void scmi_telemetry_tdcf_uuid_parse(struct telemetry_info *ti,WARN_ON (!*active_uuid); } +static struct payload __iomem * +scmi_telemetry_nearest_line_by_type (struct telemetry_shmti *shmti, +void __iomem *last,enum tdcf_line_types ltype) +{ +struct tdcf __iomem *tdcf = shmti->base; +void __iomem *next, *found =NULL ; + +/* Scan from start of the first TDCF payload up to last payld */ + next = tdcf->payld; +while (next < last) { +struct payload __iomem *payld; +enum tdcf_line_types cur_type; +u32 meta, offs; + +if (check_sub_overflow ((__force unsigned long )next, + (__force unsigned long )tdcf, &offs)) { +trace_scmi_tlm_access (ltype,"LINE_OFFSET_OUT_OF_RANGE" ,0 ,0 ); +return NULL ; + } + + payld =scmi_telemetry_payld_offset_safe_calc (shmti, offs); +if (IS_ERR (payld)) { +trace_scmi_tlm_access (ltype,"LINE_PAYLD_MALFORMED" ,0 ,0 ); +return NULL ; + } + + meta =PAYLD_META (payld); + cur_type =LINE_TYPE_RAW (meta); + +/* +* Bail-out on malformed lines, since the whole TDCF scan +* could be affected. +*/ +if ((cur_type ==TDCF_BLK_TS_LINE &&BLK_TS_INVALID_RAW (meta)) || + (cur_type ==TDCF_UUID_LINE &&UUID_INVALID_RAW (meta))) { +trace_scmi_tlm_access (ltype,"LINE_TYPE_MALFORMED" ,0 ,0 ); +return NULL ; + } + +if (cur_type == ltype) + found = next; + + next +=LINE_LENGTH_BYTES_RAW (meta); + } + +return found; +} + +static struct telemetry_block_ts * +scmi_telemetry_blkts_bind (struct telemetry_info *ti, +struct telemetry_shmti *shmti, +struct payload *bts_payld, +unsigned long offset,u32 magic) +{ +struct telemetry_block_ts *bts; + + bts =scmi_telemetry_blkts_get_or_create (shmti, offset); +/* Update the descriptor with the lastest TS */ +if (bts) +scmi_telemetry_blkts_update_snap (ti, bts, bts_payld, magic); + +return bts; +} +/** * scmi_telemetry_tdcf_blkts_parse - A BLK_TS line parser *
@@ -2036,6 +2143,32 @@ static inline void scmi_telemetry_uuid_link(struct telemetry_de *tde,trace_scmi_tlm_access (tde->de.info->id,"UUID_LINK" ,0 ,0 ); } +static struct telemetry_uuid * +scmi_telemetry_uuid_bind (struct telemetry_info *ti, +struct telemetry_shmti *shmti, +struct payload __iomem *payld) +{ +__be32 dwords[SCMI_TLM_DE_IMPL_NUM_DWORDS ]; +struct payload __iomem *uuid_payld; +struct uuid_line __iomem *uuid_l; + + uuid_payld =scmi_telemetry_nearest_line_by_type (shmti, payld, +TDCF_UUID_LINE ); +if (!uuid_payld) +return NULL ; + +/* +* Use proper mem accessors BUT no swapping, maintain UUID +* in memory BE layout. +*/ + uuid_l = &uuid_payld->uuid_l; +for (int i =0 ; i <SCMI_TLM_DE_IMPL_NUM_DWORDS ; i++) + dwords[i] = (__force __be32 )__raw_readl (&uuid_l->dwords[i]); + +/* Here the nearest UUID line just found has been already validated */ +return scmi_telemetry_uuid_get_or_create (ti, dwords); +} +/** * scmi_telemetry_tdcf_data_parse - TDCF DataLine parsing * @ti: A reference to the telemetry info descriptor
@@ -2467,10 +2600,779 @@ static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,return ret; } +static void scmi_telemetry_group_state_update (struct telemetry_info *ti, +struct scmi_telemetry_group *grp, +const bool *enable,const bool *tstamp) +{ +struct scmi_telemetry_res_info *rinfo; + + rinfo = ti->res_get (ti); +for (int i =0 ; i < grp->info->num_des; i++) { +struct scmi_telemetry_de *de = rinfo->des[grp->des[i]]; + +guard (mutex)(&(to_tde (de)->mtx)); +if (enable) +scmi_telemetry_de_state_update (ti,ENA_STATE , + &de->enabled, *enable); + +if (tstamp && de->tstamp_support) +scmi_telemetry_de_state_update (ti,ENA_TSTAMP , + &de->tstamp_enabled, *tstamp); + } +} + +static void scmi_telemetry_group_disable (struct telemetry_info *ti, +struct scmi_telemetry_group *grp) +{ +struct scmi_telemetry_res_info *rinfo; + + rinfo = ti->res_get (ti); +/* +* Unlink the related BLK_TS/UUID lines of DEs composing the group +* and invalidate SHMTI reference. +*/ +for (int i =0 ; i < grp->info->num_des; i++) { +struct telemetry_de *tde =to_tde (rinfo->des[grp->des[i]]); + +guard (mutex)(&tde->mtx); + +scmi_telemetry_blkts_unlink (tde); + + tde->sid =SHMTI_ID_INVALID ; + tde->offset =0 ; + } +} + +static struct payload __iomem * +scmi_telemetry_blkts_payld_locate (struct device *dev, +struct telemetry_shmti *shmti,u32 *bts_offs, +struct payload __iomem *payld,u32 de_id) +{ +struct payload __iomem *bts_payld =NULL ; + +if (!bts_offs) +return IOMEM_ERR_PTR (-EINVAL ); + +if (*bts_offs && +check_add_overflow ((__force unsigned long )shmti->base, *bts_offs, + (__force unsigned long *)&bts_payld)) +return IOMEM_ERR_PTR (-EPROTO ); + +/* Check SHMTI overflow on returned offset */ +if (bts_payld) { +if (scmi_telemetry_shmti_overflow (shmti, bts_payld)) { +trace_scmi_tlm_access (de_id,"SHMTI_OVERFLOW" ,0 ,0 ); +return IOMEM_ERR_PTR (-EINVAL ); + } + }else { +dev_warn_once (dev,"Received BLK_TS with zero offset.\n" ); +trace_scmi_tlm_access (de_id,"BLKTS_WITH_ZERO_OFFSET" ,0 ,0 ); + +/* +* Find the BLK_TS immediately preceding this DE payld by +* scanning. +*/ + bts_payld =scmi_telemetry_nearest_line_by_type (shmti, payld, +TDCF_BLK_TS_LINE ); +if (WARN_ON (!bts_payld)) +return IOMEM_ERR_PTR (-EPROTO ); + +/* Calculate offset of found blkts */ + *bts_offs = (void __iomem *)bts_payld - shmti->base; + } + +return bts_payld; +} + +static int +scmi_telemetry_tdcf_bts_payld_check (struct payload *bts_snap,u32 de_id) +{ +u32 meta =PAYLD_META_RAW (bts_snap); + +if (!IS_BLK_TS_LINE_RAW (meta)) { +trace_scmi_tlm_access (de_id,"BAD_BLK_TS_LINE_OFFSET" ,0 , meta); +return -EPROTO ; + } + +/* Check for spec compliance */ +if (BLK_TS_INVALID_RAW (meta)) { +trace_scmi_tlm_access (de_id,"BLK_TS_INVALID" ,0 , meta); +return -EINVAL ; + } + +return 0 ; +} + +static int scmi_telemetry_blkts_snapshot (struct telemetry_shmti *shmti, +struct payload __iomem *bts_payld, +struct payload *bts_snap,u32 *magic, +u32 de_id) +{ +int retries =SCMI_TLM_TDCF_MAX_RETRIES ; +struct tdcf __iomem *tdcf = shmti->base; +u32 startm, endm; +int err =0 ; + +do { +/* A bit of exponential backoff between retries */ +fsleep ((SCMI_TLM_TDCF_MAX_RETRIES - retries) *SCMI_TLM_TDCF_RETRY_GAP_US ); + + startm =TDCF_START_SEQ_GET (tdcf); +/* Build a default endm certainly different from startm */ + endm = startm +1 ; +if (IS_BAD_START_SEQ (startm)) { +trace_scmi_tlm_access (de_id,"MSEQ_BADSTART" , startm,0 ); +continue ; + } + +memcpy_fromio (bts_snap, bts_payld,LINE_LENGTH_BYTES (bts_payld)); +/* +* In this context any anomaly detected on the payload could +* due to a torn read, and it is worth retrying a snapshot. +*/ + err =scmi_telemetry_tdcf_bts_payld_check (bts_snap, de_id); +if (err) +continue ; + +/* Assure the snapshot has been fully taken before checking seqnum */ +rmb (); + + endm =TDCF_END_SEQ_GET (SHMTI_EPLG (shmti)); +if (startm != endm) +trace_scmi_tlm_access (de_id,"MSEQ_MISMATCH" , startm, endm); + }while ((startm != endm || err) && --retries); + +if (startm != endm || err) { +trace_scmi_tlm_access (de_id,"TDCF_SNAPSHOT_BLKTS_FAIL" , startm, endm); +return err ?: -EPROTO ; + } + +/* Ensure any snapshot is visible before updating magic */ +smp_store_release (magic, startm); + +trace_scmi_tlm_access (de_id,"TDCF_SNAPSHOT_BLKTS_OK" , startm, endm); + +return 0 ; +} + +static int +scmi_telemetry_tde_linked_lines_process (struct telemetry_info *ti, +struct telemetry_de *tde, +struct telemetry_shmti *shmti, +struct payload __iomem *payld, +u32 bts_offs) +{ +struct payload __iomem *bts_payld; +struct telemetry_block_ts *bts; +struct payload bts_snap = {}; +struct telemetry_uuid *uuid; +u32 magic =0 ; +int ret; + +lockdep_assert_held (&ti->config_mtx); +lockdep_assert_held (&tde->mtx); + +/* +* On enable, if a UUID was NOT already assigned, scan looking for +* possible Secondary UUID to bind: if NO secondary could be found +* embedded in the preceding SHMTI lines, install the primary UUID +* for this DE. +* NOTE that UUID lines content is immutable once created and no +* concurrent configuration request is possible at this point since +* config_mtx is held, so while the platform can still concurrently +* update all the existing SHMTI lines variable values, existing lines +* CANNOT be shuffled around: as a consequence a full snapshot is NOT +* needed as a pre-requisite to perform a scan when looking for +* UUID lines. +*/ +if (!tde->uuid) { + uuid =scmi_telemetry_uuid_bind (ti, shmti, payld); +if (uuid) + tde->uuid = uuid; +else +scmi_telemetry_uuid_link (tde, ti->primary_uuid); + } + +/* Process related BLK_TS lines if any */ +if (!USE_BLK_TS (payld)) { +/* Unlink old stale BTS if existent */ +if (IS_TDE_TS_BLK (tde) && tde->bts) +scmi_telemetry_blkts_unlink (tde); +return 0 ; + } + +if (!IS_TDE_TS_BLK (tde)) { +trace_scmi_tlm_access (tde->de.info->id,"BAD_DE_TSTAMP_TYPE" ,0 ,0 ); +return -EPROTO ; + } + +/* Has associated BLK_TS line changed at all ? */ +if (!BLK_TS_CHANGED (tde, shmti, bts_offs)) +return 0 ; + + bts_payld =scmi_telemetry_blkts_payld_locate (ti->ph->dev, shmti, + &bts_offs, payld, + tde->de.info->id); +if (IS_ERR (bts_payld)) +return PTR_ERR (bts_payld); + + ret =scmi_telemetry_blkts_snapshot (shmti, bts_payld, + &bts_snap, &magic, tde->de.info->id); +if (ret) +return ret; + +/* Look for a viable BLK_TS line */ + bts =scmi_telemetry_blkts_bind (ti, shmti, &bts_snap, bts_offs, magic); +if (WARN_ON (!bts)) { +trace_scmi_tlm_access (tde->de.info->id,"INVALID_USE_BLK_TS" ,0 ,0 ); +return -EPROTO ; + } + +/* Unlink old stale BTS if existent */ +scmi_telemetry_blkts_unlink (tde); + +/* Store freshly discovered sane BLK_TS */ + tde->bts = bts; + +/* Update per-DE BLK_TS if any */ +if (tde->bts->last_rate) + tde->de.info->ts_rate = tde->bts->last_rate; + +return 0 ; +} + +static int +scmi_telemetry_state_set_tde_resp (struct telemetry_info *ti, +struct scmi_telemetry_de *de, +u32 sid,u32 de_offs,u32 bts_offs) +{ +struct telemetry_de *tde =to_tde (de); +struct telemetry_shmti *shmti; +struct payload __iomem *payld; +int ret; + +lockdep_assert_held (&tde->mtx); + +/* A FastChannel DE MUST return an INVALID SHMTI ID on enable */ +if (WARN_ON (de->fc_support)) { +trace_scmi_tlm_access (de->info->id,"INVALID_DE_FC_ON_SHMTI" ,0 ,0 ); +return -EPROTO ; + } + +/* Update SHMTI/offset while skipping non-SHMTI based DES */ + shmti = &ti->shmti[sid]; +/* Check sanity of received location... */ + payld =scmi_telemetry_payld_offset_safe_calc (shmti, de_offs); +if (IS_ERR (payld)) +return PTR_ERR (payld); + +/* +* Note that here we are processing a configuration request on a DE +* while holding @config_mtx: in this scenario platform cannot relocate +* or mutate this line freely, so we can directly access the TDCF and +* lookup the line sitting at @de_offs to check its line type without +* having to worry about consistency or taking snapshots. +*/ +if (!IS_DATA_LINE (payld)) +return -EPROTO ; + + ret =scmi_telemetry_tde_linked_lines_process (ti, tde, shmti, payld, + bts_offs); +if (ret) +return ret; + +/* ...then assign once validated */ + tde->sid = sid; + tde->base = shmti->base; + tde->offset = de_offs; +/* A handy reference to the Epilogue updated */ + tde->eplg =SHMTI_EPLG (shmti); + +return 0 ; +} + +static int +scmi_telemetry_tde_state_set_resp_process (struct telemetry_info *ti, +struct scmi_telemetry_de *de, +struct scmi_msg_resp_telemetry_de_configure *r) +{ +u32 sid =le32_to_cpu (r->shmti_id); + +/* +* 3.12.4.8 TELEMETRY_DE_CONFIGURE - SHMTI ID +* This field is optional and is set to 0xFFFFFFFF if any of the +* following are true: +* - no DE or event group is being enabled. +* - No SHMTI is supported. +* - SHMTI is not used for this DE or event group. This could +* be the case for example, if this DE is associated with a FastChannel. +* the platform does not support returning this information. +*/ +if (IS_SHMTI_ID_VALID (sid)) { +u32 de_offs, bts_offs; + +if (sid >= ti->num_shmti) +return -EPROTO ; + + de_offs =le32_to_cpu (r->shmti_de_offset); + bts_offs =le32_to_cpu (r->blk_ts_offset); +if (!IS_ALIGNED (de_offs,4 ) || !IS_ALIGNED (bts_offs,4 )) { +dev_err (ti->ph->dev, +"Returned DE/BTS offsets are NOT 32-bit aligned - de_offs:%X bts_offs:%X\n" , + de_offs, bts_offs); +return -EINVAL ; + } + +return scmi_telemetry_state_set_tde_resp (ti, de, sid, + de_offs, bts_offs); + }else { +/* Unlink the related BLK_TS/UUID lines on disable */ +struct telemetry_de *tde =to_tde (de); + +lockdep_assert_held (&tde->mtx); + + tde->sid =SHMTI_ID_INVALID ; + tde->offset =0 ; + +/* UUID is never unlinked once discovered */ +scmi_telemetry_blkts_unlink (tde); + } + +return 0 ; +} + +static int +scmi_telemetry_group_state_set_resp_process (struct telemetry_info *ti, +struct scmi_telemetry_group *grp, +struct scmi_msg_resp_telemetry_de_configure *r) +{ +u32 sid =le32_to_cpu (r->shmti_id); +int ret; + +/* +* 3.12.4.8 TELEMETRY_DE_CONFIGURE - SHMTI ID +* This field is optional and is set to 0xFFFFFFFF if any of the +* following are true: +* - no DE or event group is being enabled. +* - No SHMTI is supported. +* - SHMTI is not used for this DE or event group. This could +* be the case for example, if this DE is associated with a FastChannel. +* the platform does not support returning this information. +*/ +if (IS_SHMTI_ID_VALID (sid)) { +if (sid >= ti->num_shmti) +return -EPROTO ; + +/* +* A full SHMTI scan is needed when enabling a +* group or its timestamps in order to retrieve +* offsets: note that when group-timestamp is +* enabled for composing DEs a re-scan is needed +* since some DEs could have been relocated due +* to lack of space in the TDCF. +*/ + ret =scmi_telemetry_shmti_scan (ti, sid,SCAN_UPDATE ); +if (ret) { +dev_err (ti->ph->dev, +"Failed group-scan of SHMTI ID:%d - ret:%d\n" , + sid, ret); +return ret; + } + }else { +/* Unlink the related BLK_TS/UUID lines on disable */ +scmi_telemetry_group_disable (ti, grp); + } + +return 0 ; +} + +static void +scmi_telemetry_state_cache_update (struct telemetry_info *ti, +const bool *enable,bool *enabled_state, +const bool *tstamp,bool *tstamp_enabled_state, +struct scmi_telemetry_group *grp, +struct scmi_telemetry_de *de) +{ +if ((!grp && !de) || (grp && de)) +return ; + +if (enable) { +if (!grp) +scmi_telemetry_de_state_update (ti,ENA_STATE , + enabled_state, + *enable); +else + *enabled_state = *enable; + } + +if (tstamp) { +if (!grp) { +if (de->tstamp_support) +scmi_telemetry_de_state_update (ti,ENA_TSTAMP , + tstamp_enabled_state, + *tstamp); + }else { + *tstamp_enabled_state = *tstamp; + } + } + +if (grp) +scmi_telemetry_group_state_update (ti, grp, enable, tstamp); +} + +static inline bool +scmi_telemetry_remote_state_update_needed (struct telemetry_info *ti, +const bool *enable,bool *enabled_state, +const bool *tstamp, +bool *tstamp_enabled_state, +bool is_group,void *obj) +{ +/* Is anything to do at all on this object ? */ +if ((!enable || *enable == *enabled_state) && + (!tstamp || *tstamp == *tstamp_enabled_state)) +return false; + +/* +* When the object is currently disabled AND no enable state change was +* requested, BUT timestamp is being changed, update only local state... +* no need to send a message now, it will be sent later on enable. +*/ +if (!enable && !*enabled_state) { +scmi_telemetry_state_cache_update (ti, enable, + enabled_state, tstamp, + tstamp_enabled_state, + is_group ? obj :NULL , + !is_group ? obj :NULL ); + +return false; + } + +return true; +} + +static inline void +scmi_telemetry_state_message_prepare (struct scmi_msg_telemetry_de_configure *msg, +unsigned int id,bool is_group, +const bool *enable,bool *enabled_state, +const bool *tstamp,bool *tstamp_enabled_state) +{ + msg->identifier =cpu_to_le32 (id); +/* Default to disable mode */ + msg->flags =CFG_DISABLE_ONE ; + msg->flags |=cpu_to_le32 (FIELD_PREP (GENMASK (3 ,3 ), + !is_group ?CFG_EVENT_DE :CFG_EVENT_GROUP )); + +if ((!enable && *enabled_state) || (enable && *enable)) { +/* Already enabled but tstamp_enabled state changed */ +if (tstamp) { +/* Here, tstamp cannot be NULL too */ + msg->flags |=cpu_to_le32 (*tstamp ? +CFG_ENABLE_WTH_TSTAMP :CFG_ENABLE_NO_TSTAMP ); + }else { + msg->flags |=cpu_to_le32 (*tstamp_enabled_state ? +CFG_ENABLE_WTH_TSTAMP :CFG_ENABLE_NO_TSTAMP ); + } + } +} + +static inline void +scmi_telemetry_tde_location_update (struct telemetry_de *tde, +u32 *sid,u32 *offset,uuid_t *uuid) +{ +if (tde) +lockdep_assert_held (&tde->mtx); + +if (sid) + *sid = tde ? tde->sid :SHMTI_ID_INVALID ; +if (offset) + *offset = tde ? tde->offset :0 ; +if (uuid) { +if (tde && tde->uuid) +uuid_copy (uuid, &tde->uuid->uuid); +else +memset (uuid,0x0 ,sizeof (*uuid)); + } +} + +static int scmi_telemetry_tde_state_set (const struct scmi_protocol_handle *ph, +const bool *enable,const bool *tstamp, +struct scmi_telemetry_de *de, +u32 *sid,u32 *offset,uuid_t *uuid) +{ +struct scmi_msg_resp_telemetry_de_configure *resp; +struct scmi_msg_telemetry_de_configure *msg; +struct telemetry_info *ti = ph->get_priv (ph); +struct telemetry_de *tde =to_tde (de); +struct scmi_xfer *t; +int ret; + +/* Stall concurrent configuration request */ +guard (mutex)(&ti->config_mtx); + +/* +* Note that the TDE lock must be taken early on, before the SCMI +* configuration command is even issued to avoid a de-sync between the +* TDE state and the effective HW state enforced by the platform in +* response to our request. +*/ +guard (mutex)(&tde->mtx); + +/* Initialize return values with current TDE in case of error */ +scmi_telemetry_tde_location_update (tde, sid, offset, uuid); + +if (!scmi_telemetry_remote_state_update_needed (ti, enable, &de->enabled, + tstamp, &de->tstamp_enabled, + false, de)) +return 0 ; + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_DE_CONFIGURE , +sizeof (*msg),sizeof (*resp), &t); +if (ret) +return ret; + + msg = t->tx.buf; +scmi_telemetry_state_message_prepare (msg, de->info->id, false, + enable, &de->enabled, + tstamp, &de->tstamp_enabled); + + resp = t->rx.buf; + ret = ph->xops->do_xfer (ph, t); +if (!ret) { +/* +* Update cached state on command success anyway, despite any +* possible later response validation issue: once the command +* has succedeed keep our state in sync with HW. +*/ +scmi_telemetry_state_cache_update (ti, enable, &de->enabled, + tstamp, &de->tstamp_enabled,NULL , de); +/* Process response, update DEs location tracking */ + ret =scmi_telemetry_tde_state_set_resp_process (ti, de, resp); +if (!ret) +scmi_telemetry_tde_location_update (tde, sid, offset, uuid); +else +dev_err (ph->dev, +"Could NOT process DE ID 0x%08X state response - ret:%d\n" , + tde->de.info->id, ret); + } + + ph->xops->xfer_put (ph, t); + +return ret; +} + +static int scmi_telemetry_group_state_set (const struct scmi_protocol_handle *ph, +const bool *enable,const bool *tstamp, +struct scmi_telemetry_group *grp) +{ +struct scmi_msg_resp_telemetry_de_configure *resp; +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_msg_telemetry_de_configure *msg; +struct scmi_xfer *t; +int ret; + +/* Stall concurrent configuration request */ +guard (mutex)(&ti->config_mtx); + +if (!scmi_telemetry_remote_state_update_needed (ti, enable, &grp->enabled, + tstamp, &grp->tstamp_enabled, + true, grp)) +return 0 ; + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_DE_CONFIGURE , +sizeof (*msg),sizeof (*resp), &t); +if (ret) +return ret; + + msg = t->tx.buf; +scmi_telemetry_state_message_prepare (msg, grp->info->grp_id, true, + enable, &grp->enabled, + tstamp, &grp->tstamp_enabled); + resp = t->rx.buf; + ret = ph->xops->do_xfer (ph, t); +if (!ret) { +/* +* Update cached state on command success anyway, despite any +* possible later response validation issue: once the command +* has succedeed keep our state in sync with HW. +*/ +scmi_telemetry_state_cache_update (ti, enable, &grp->enabled, + tstamp, &grp->tstamp_enabled, + grp,NULL ); + ret =scmi_telemetry_group_state_set_resp_process (ti, grp, resp); +if (ret) +dev_err (ph->dev, +"Could NOT process GROUP %u state response - ret:%d\n" , + grp->info->grp_id, ret); + } + + ph->xops->xfer_put (ph, t); + +return ret; +} + +static int scmi_telemetry_state_get (const struct scmi_protocol_handle *ph, +const u32 *id,bool *enabled,bool *tstamp_enabled, +u32 *sid,u32 *offset,uuid_t *uuid) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_telemetry_de *de; +struct telemetry_de *tde; + +if (!enabled || !tstamp_enabled) +return -EINVAL ; + + ti->res_get (ti); +if (!id) { +guard (mutex)(&ti->des_enabled_mtx); +/* Returning the all_des_* state */ + *enabled = + (atomic_read (&ti->des_enabled[ENA_STATE ]) == ti->info.base.num_des); + *tstamp_enabled = + (atomic_read (&ti->des_enabled[ENA_TSTAMP ]) == ti->num_des_tstamp); + +return 0 ; + } + + de =xa_load (&ti->xa_des, *id); +if (!de) +return -ENODEV ; + + tde =to_tde (de); +guard (mutex)(&tde->mtx); + + *enabled = de->enabled; + *tstamp_enabled = de->tstamp_enabled; + +scmi_telemetry_tde_location_update (tde, sid, offset, uuid); + +return 0 ; +} + +static int scmi_telemetry_state_set (const struct scmi_protocol_handle *ph, +bool is_group,u32 id,const bool *enable, +const bool *tstamp,u32 *sid,u32 *offset, +uuid_t *uuid) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_telemetry_res_info *rinfo; + + rinfo = ti->res_get (ti); +if (!is_group) { +struct scmi_telemetry_de *de; + + de =xa_load (&ti->xa_des, id); +if (!de) +return -ENODEV ; + +/* Filter out unplausible timestamp requests */ +return scmi_telemetry_tde_state_set (ph, enable, + de->tstamp_support ? tstamp :NULL , + de, sid, offset, uuid); + }else { +struct scmi_telemetry_group *grp; + +if (id >= rinfo->num_groups) +return -EINVAL ; + + grp = &rinfo->grps[id]; +return scmi_telemetry_group_state_set (ph, enable, tstamp, grp); + } +} + +static int +scmi_telemetry_collection_configure (const struct scmi_protocol_handle *ph, +unsigned int res_id,const bool *enable, +const unsigned int *update_interval_ms, +const enum scmi_telemetry_collection *mode) +{ +enum scmi_telemetry_collection *current_mode, next_mode; +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_msg_telemetry_config_set *msg; +bool tlm_enable, *current_state, is_group; +struct scmi_telemetry_res_info *rinfo; +unsigned int *active_update_interval; +struct scmi_xfer *t; +__le32 interval; +u32 control; +int ret; + +/* Stall concurrent configuration request */ +guard (mutex)(&ti->config_mtx); + +if (mode && (*mode <=SCMI_TLM_SINGLE_READ ) && + ((*mode ==SCMI_TLM_NOTIFICATION && !ti->info.continuous_update_support) || + (*mode ==SCMI_TLM_SINGLE_READ && !ti->info.single_read_support))) +return -EINVAL ; + + is_group = res_id !=SCMI_TLM_GRP_INVALID ; +if (is_group) { + rinfo = ti->res_get (ti); + +if (res_id >= rinfo->num_groups || + !ti->info.per_group_config_support) +return -EINVAL ; + } + +if (!is_group) { + active_update_interval = &ti->info.active_update_interval; + current_mode = &ti->info.current_mode; + current_state = &ti->info.enabled; + }else { + active_update_interval = + &rinfo->grps[res_id].active_update_interval; + current_mode = &rinfo->grps[res_id].current_mode; + current_state = &rinfo->grps[res_id].enabled; + } + +if (!enable && !update_interval_ms && (!mode || *mode == *current_mode)) +return 0 ; + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_CONFIG_SET , +sizeof (*msg),0 , &t); +if (ret) +return ret; + +if (!update_interval_ms) + interval =cpu_to_le32 (*active_update_interval); +else + interval =cpu_to_le32 (*update_interval_ms); + + tlm_enable = enable ? *enable : *current_state; + next_mode = mode ? *mode : *current_mode; + + msg = t->tx.buf; + msg->group_identifier =cpu_to_le32 (res_id); + control = tlm_enable ?TELEMETRY_ENABLE :0 ; + control |= !is_group ?TELEMETRY_SET_SELECTOR_ALL :TELEMETRY_SET_SELECTOR_GROUP ; + control |=TELEMETRY_MODE_SET (next_mode); + msg->control =cpu_to_le32 (control); + msg->sampling_rate = interval; + ret = ph->xops->do_xfer (ph, t); +if (!ret) { + *current_mode = next_mode; + *current_state = tlm_enable; +/* +* Enable streaming mode when notifications were explicitly +* requested as a global config, not on a group. +*/ +if (!is_group) + ti->streaming_mode = *current_mode ==SCMI_TLM_NOTIFICATION ; +if (update_interval_ms) + *active_update_interval =le32_to_cpu (interval); + } + + ph->xops->xfer_put (ph, t); + +return ret; +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, .de_lookup = scmi_telemetry_de_lookup, .res_get = scmi_telemetry_resources_get, + .state_get = scmi_telemetry_state_get, + .state_set = scmi_telemetry_state_set, + .collection_configure = scmi_telemetry_collection_configure, };/**
@@ -2814,6 +3716,7 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)return ret;xa_init (&ti->xa_des); +mutex_init (&ti->config_mtx);/* * Always allocate at least one slot for the primary and anyway at
include/linux/scmi_protocol.h +27 -0
@@ -888,6 +888,12 @@ enum scmi_telemetry_compo_type {#define SCMI_TLM_BUILD_UPDATE_INTERVAL (s, e) \ (FIELD_PREP(GENMASK(20, 5), (s)) | FIELD_PREP(GENMASK(4, 0), (e))) +enum scmi_telemetry_collection { +SCMI_TLM_ONDEMAND , +SCMI_TLM_NOTIFICATION , +SCMI_TLM_SINGLE_READ , +}; +#define SCMI_TLM_GRP_INVALID 0xFFFFFFFFstruct scmi_telemetry_intervals {
@@ -914,6 +920,7 @@ struct scmi_telemetry_group {struct scmi_telemetry_grp_info *info;unsigned int active_update_interval;struct scmi_telemetry_intervals *intervals; +enum scmi_telemetry_collection current_mode; };struct scmi_telemetry_de_info {
@@ -983,6 +990,8 @@ struct scmi_telemetry_info {struct scmi_telemetry_shmti_info **shmtis;unsigned int num_uuids; uuid_t * __rcu *uuids __counted_by_ptr(num_uuids); +bool enabled; +enum scmi_telemetry_collection current_mode; };/**
@@ -992,6 +1001,13 @@ struct scmi_telemetry_info {* @info_get: get the general Telemetry information. * @de_lookup: get a specific DE descriptor from the DE id. * @res_get: get a reference to the Telemetry resources descriptor. +* @state_get: retrieve the specific DE state, if NULL returns the +* cumulative state of all DEs. Global group state can be looked-up +* directly from @scmi_telemetry_info via @info_get. +* @state_set: enable/disable the specific DE or GROUP with or without timestamps. +* @collection_configure: choose a sampling rate and enable SHMTI/FC sampling +* for on demand collection via @de_data_read or async +* notificatioins for all the enabled DEs. */ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get)
@@ -1000,6 +1016,17 @@ struct scmi_telemetry_proto_ops { (const struct scmi_protocol_handle *ph, u32 id);const struct scmi_telemetry_res_info *(*res_get) (const struct scmi_protocol_handle *ph); +int (*state_get)(const struct scmi_protocol_handle *ph, +const u32 *id,bool *enabled,bool *tstamp_enabled, +u32 *sid,u32 *offset,uuid_t *uuid); +int (*state_set)(const struct scmi_protocol_handle *ph, +bool is_group,u32 id,const bool *enable, +const bool *tstamp,u32 *sid,u32 *offset, +uuid_t *uuid); +int (*collection_configure)(const struct scmi_protocol_handle *ph, +unsigned int res_id,const bool *enable, +const unsigned int *update_interval_ms, +const enum scmi_telemetry_collection *mode); };/**
Signature · 1 line
2.54.0
Re: [PATCH v15 17/35] firmware: arm_scmi: Add Telemetry configuration operations
Quoted 5 lines · On Mon, 9 Nov 2026 09:12:03 +0000 Cristian Marussi <[email protected]>…
Add support for basic Telemetry configuration operations to selectively enable or disable DataEvents monitoring.
Signed-off-by Cristian Marussi <[email protected]>---
Various comments inline. Includes a suggestion that some __iomem pointer specific overflow checkers might make for neater code here and in earlier patches.
drivers/firmware/arm_scmi/telemetry.c struct scmi_msg_resp_telemetry_update_intervals { · line 167
Quoted 13 lines
drivers/firmware/arm_scmi/telemetry.c +13
-0+ +struct scmi_msg_telemetry_config_set { +__le32 group_identifier; +__le32 control; +#define TELEMETRY_ENABLE (BIT(0)) +#define TELEMETRY_MODE GENMASK(4, 1) +#define TELEMETRY_MODE_SET (x) (FIELD_PREP(TELEMETRY_MODE, (x))) +#define TLM_ONDEMAND (0) +#define TLM_NOTIFS (1) +#define TLM_SINGLE (2) +#define TELEMETRY_MODE_ONDEMAND TELEMETRY_MODE_SET(TLM_ONDEMAND) +#define TELEMETRY_MODE_NOTIFS TELEMETRY_MODE_SET(TLM_NOTIFS) +#define TELEMETRY_MODE_SINGLE TELEMETRY_MODE_SET(TLM_SINGLE)
I'd just put the FIELD_PREP inline. It is self explanatory on only a char or two longer..
Quoted 17 lines
(quoted diff) +14
-0+ +#define TLM_ORPHANS (0) +#define TLM_GROUP (1) +#define TLM_ALL (2) +#define TELEMETRY_SET_SELECTOR(x) (FIELD_PREP(GENMASK(8, 5), (x))) +#define TELEMETRY_SET_SELECTOR_ORPHANS TELEMETRY_SET_SELECTOR(TLM_ORPHANS) +#define TELEMETRY_SET_SELECTOR_GROUP TELEMETRY_SET_SELECTOR(TLM_GROUP) +#define TELEMETRY_SET_SELECTOR_ALL TELEMETRY_SET_SELECTOR(TLM_ALL) + __le32 sampling_rate; +}; + +static struct payload __iomem * +scmi_telemetry_blkts_payld_locate(struct device *dev, + struct telemetry_shmti *shmti, u32 *bts_offs,
Review
Quoted 10 lines
(quoted diff) +10
-0+ struct payload __iomem *payld, u32 de_id) +{ + struct payload __iomem *bts_payld = NULL; + + if (!bts_offs) + return IOMEM_ERR_PTR(-EINVAL); + + if (*bts_offs && + check_add_overflow((__force unsigned long)shmti->base, *bts_offs, + (__force unsigned long *)&bts_payld))
This code has quit a lot of checks on pointer overflow and many need some ugly casts. Maybe we can have some local helpers for this?
bool check_add_overflow_iomemptr(void __iomem* start, size_t off, void __iomem **out)
{
unsigned long _start, _out;
bool overflow;
_start = (__force unsigned long)start;
overflow = check_add_overflow(_start, off, &_out);
*out = (__force void __iomem *)_out;
return overflow;
}or something like that?
Quoted 50 lines
drivers/firmware/arm_scmi/telemetry.c +48
-0+return IOMEM_ERR_PTR (-EPROTO ); + +/* Check SHMTI overflow on returned offset */ +if (bts_payld) { +if (scmi_telemetry_shmti_overflow (shmti, bts_payld)) { +trace_scmi_tlm_access (de_id,"SHMTI_OVERFLOW" ,0 ,0 ); +return IOMEM_ERR_PTR (-EINVAL ); + } + }else { +dev_warn_once (dev,"Received BLK_TS with zero offset.\n" ); +trace_scmi_tlm_access (de_id,"BLKTS_WITH_ZERO_OFFSET" ,0 ,0 ); + +/* +* Find the BLK_TS immediately preceding this DE payld by +* scanning. +*/ + bts_payld =scmi_telemetry_nearest_line_by_type (shmti, payld, +TDCF_BLK_TS_LINE ); +if (WARN_ON (!bts_payld)) +return IOMEM_ERR_PTR (-EPROTO ); + +/* Calculate offset of found blkts */ + *bts_offs = (void __iomem *)bts_payld - shmti->base; + } + +return bts_payld; +} + +static int scmi_telemetry_group_state_set (const struct scmi_protocol_handle *ph, +const bool *enable,const bool *tstamp, +struct scmi_telemetry_group *grp) +{ +struct scmi_msg_resp_telemetry_de_configure *resp; +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_msg_telemetry_de_configure *msg; +struct scmi_xfer *t; +int ret; + +/* Stall concurrent configuration request */ +guard (mutex)(&ti->config_mtx); + +if (!scmi_telemetry_remote_state_update_needed (ti, enable, &grp->enabled, + tstamp, &grp->tstamp_enabled, + true, grp)) +return 0 ; + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_DE_CONFIGURE , +sizeof (*msg),sizeof (*resp), &t);
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti,
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+if (ret) +return ret; + + msg = t->tx.buf; +scmi_telemetry_state_message_prepare (msg, grp->info->grp_id, true, + enable, &grp->enabled, + tstamp, &grp->tstamp_enabled); + resp = t->rx.buf; + ret = ph->xops->do_xfer (ph, t); +if (!ret) {
So this is doing the out of line good path just to keep the ability to use guard. I'd be tempted to not use guard() or use a wrapper function just for that. guard() magic is lovely but only until it makes us write harder to read code. The challenge here is we can't easily do __free magic for the xfer.
Quoted 13 lines
drivers/firmware/arm_scmi/telemetry.c +13
-0+/* +* Update cached state on command success anyway, despite any +* possible later response validation issue: once the command +* has succedeed keep our state in sync with HW. +*/ +scmi_telemetry_state_cache_update (ti, enable, &grp->enabled, + tstamp, &grp->tstamp_enabled, + grp,NULL ); + ret =scmi_telemetry_group_state_set_resp_process (ti, grp, resp); +if (ret) +dev_err (ph->dev, +"Could NOT process GROUP %u state response - ret:%d\n" , + grp->info->grp_id, ret);
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3215
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ } + + ph->xops->xfer_put (ph, t); + +return ret; +} + +static int scmi_telemetry_state_get (const struct scmi_protocol_handle *ph, +const u32 *id,bool *enabled,bool *tstamp_enabled, +u32 *sid,u32 *offset,uuid_t *uuid)
bonus space before *uuid.
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +6
-0+{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_telemetry_de *de; +struct telemetry_de *tde; + +if (!enabled || !tstamp_enabled)
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3231
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+return -EINVAL ; + + ti->res_get (ti); +if (!id) { +guard (mutex)(&ti->des_enabled_mtx); +/* Returning the all_des_* state */ + *enabled = + (atomic_read (&ti->des_enabled[ENA_STATE ]) == ti->info.base.num_des); + *tstamp_enabled = + (atomic_read (&ti->des_enabled[ENA_TSTAMP ]) == ti->num_des_tstamp);
Ah. These are reference counters. I had misunderstood that earlier.
I'd add some docs for this function though as isn't immediately obvious what should be returned if these don't match.
Quoted 26 lines
drivers/firmware/arm_scmi/telemetry.c +26
-0+ +return 0 ; + } + + de =xa_load (&ti->xa_des, *id); +if (!de) +return -ENODEV ; + + tde =to_tde (de); +guard (mutex)(&tde->mtx); + + *enabled = de->enabled; + *tstamp_enabled = de->tstamp_enabled; + +scmi_telemetry_tde_location_update (tde, sid, offset, uuid); + +return 0 ; +} + +static int scmi_telemetry_state_set (const struct scmi_protocol_handle *ph, +bool is_group,u32 id,const bool *enable, +const bool *tstamp,u32 *sid,u32 *offset, +uuid_t *uuid) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_telemetry_res_info *rinfo;
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3267
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ + rinfo = ti->res_get (ti); +if (!is_group) { +struct scmi_telemetry_de *de; + + de =xa_load (&ti->xa_des, id); +if (!de) +return -ENODEV ; + +/* Filter out unplausible timestamp requests */
Rather than error out if we get one? That sounds fragile.
Quoted 25 lines
drivers/firmware/arm_scmi/telemetry.c +25
-0+return scmi_telemetry_tde_state_set (ph, enable, + de->tstamp_support ? tstamp :NULL , + de, sid, offset, uuid); + }else { +struct scmi_telemetry_group *grp; + +if (id >= rinfo->num_groups) +return -EINVAL ; + + grp = &rinfo->grps[id]; +return scmi_telemetry_group_state_set (ph, enable, tstamp, grp); + } +} + +static int +scmi_telemetry_collection_configure (const struct scmi_protocol_handle *ph, +unsigned int res_id,const bool *enable, +const unsigned int *update_interval_ms, +const enum scmi_telemetry_collection *mode) +{ +enum scmi_telemetry_collection *current_mode, next_mode; +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_msg_telemetry_config_set *msg; +bool tlm_enable, *current_state, is_group; +struct scmi_telemetry_res_info *rinfo;
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3302
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+unsigned int *active_update_interval; +struct scmi_xfer *t; +__le32 interval; +u32 control; +int ret; + +/* Stall concurrent configuration request */ +guard (mutex)(&ti->config_mtx); + + if (mode && (*mode <= SCMI_TLM_SINGLE_READ) &&
This first line checks if mode is in range of allowed values. When would it not be? Feels like a confusing thing to check here as either of the next two conditions required it to be specific values that meet that requirement. Was that meant to be an out of range check?
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+ ((*mode ==SCMI_TLM_NOTIFICATION && !ti->info.continuous_update_support) || + (*mode == SCMI_TLM_SINGLE_READ && !ti->info.single_read_support))) + return -EINVAL; +
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3316
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ is_group = res_id !=SCMI_TLM_GRP_INVALID ; +if (is_group) { + rinfo = ti->res_get (ti); + +if (res_id >= rinfo->num_groups || + !ti->info.per_group_config_support) +return -EINVAL ; + } + +if (!is_group) {
Maybe this makes sense in later patches, but for now, it looks like you can just flip this condition and combine the else below iwth the if (is_group) just above. That is...
if (is_group) {
rinfo = ti->res_get(ti);
if (res_id >= rinfo->num_groups ||
!ti->info.per_group_config_support)
return -EINVAL;
active_update_interval = &rinfo->grps[res_id].active_update_interval;
current_mode = &rinfo->grps[res_id].current_mode;
current_state = &rinfo->grps[res_id].enabled;
} else {
active_update_interval = &ti->info.active_update_interval;
current_mode = &ti->info.current_mode;
current_state = &ti->info.enabled;
}Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +9
-0+ active_update_interval = &ti->info.active_update_interval; + current_mode = &ti->info.current_mode; + current_state = &ti->info.enabled; + }else { + active_update_interval = + &rinfo->grps[res_id].active_update_interval; + current_mode = &rinfo->grps[res_id].current_mode; + current_state = &rinfo->grps[res_id].enabled; + }
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3335
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ +if (!enable && !update_interval_ms && (!mode || *mode == *current_mode)) +return 0 ; + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_CONFIG_SET , +sizeof (*msg),0 , &t); +if (ret) +return ret; + +if (!update_interval_ms)
Flip logic then it's visually obvious that if you have it you use the passed in active_interval_ms
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3347
Quoted 12 lines
drivers/firmware/arm_scmi/telemetry.c +12
-0+ interval =cpu_to_le32 (*active_update_interval); + else + interval =cpu_to_le32 (*update_interval_ms); + + tlm_enable = enable ? *enable : *current_state; + next_mode = mode ? *mode : *current_mode; + + msg = t->tx.buf; + msg->group_identifier =cpu_to_le32 (res_id); + control = tlm_enable ?TELEMETRY_ENABLE :0 ; + control |= !is_group ?TELEMETRY_SET_SELECTOR_ALL :TELEMETRY_SET_SELECTOR_GROUP ; + control |=TELEMETRY_MODE_SET (next_mode);
control = FIELD_PREP(TELEMETRY_ENABLE, tlm_emable ? 1 : 0) |
FIELD_PREP(TELEMETRY_SET_SELECTOR, TLM_SET_SELECTOR_ALL) | FIELD_PREP(TELMETRY_MODE_SET, next_mode);
With adjusted defines is clearer to read than via the defines at the top of file. I'm never sure why people feel like wrappers around FIELD_PREP make things more readable - I want to see that this is building a register value and what goes in what named field. The macros like TELEMETRY_SET_SELECTOR() obscure that.
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_shmti_scan(struct telemetry_info *ti, · line 3351
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+ msg->control =cpu_to_le32 (control); + msg->sampling_rate = interval; + ret = ph->xops->do_xfer (ph, t); +if (!ret) {
I'd do
if (ret) {
ph->xops->xfer_put(ph, t);
return ret;
}Even though it duplicates that unwind, I think it is worth it to avoid out of line good paths. Or just don't use guard and use a goto.
Quoted 22 lines
drivers/firmware/arm_scmi/telemetry.c +16
-0+ *current_mode = next_mode; + *current_state = tlm_enable; +/* +* Enable streaming mode when notifications were explicitly +* requested as a global config, not on a group. +*/ +if (!is_group) + ti->streaming_mode = *current_mode ==SCMI_TLM_NOTIFICATION ; +if (update_interval_ms) + *active_update_interval =le32_to_cpu (interval); + } + + ph->xops->xfer_put (ph, t); + +return ret; +}include/linux/scmi_protocol.h +0
-0
include/linux/scmi_protocol.h enum scmi_telemetry_compo_type { · line 894
Quoted 7 lines
include/linux/scmi_protocol.h +4
-0@@ -888,6 +888,12 @@ enum scmi_telemetry_compo_type {#define SCMI_TLM_BUILD_UPDATE_INTERVAL (s, e) \ (FIELD_PREP(GENMASK(20, 5), (s)) | FIELD_PREP(GENMASK(4, 0), (e))) +enum scmi_telemetry_collection { +SCMI_TLM_ONDEMAND , +SCMI_TLM_NOTIFICATION , +SCMI_TLM_SINGLE_READ ,
Given these are called out a 0 1 2 in the spec I'd assign the values to them just to make that association clear.
Quoted 1 line
include/linux/scmi_protocol.h +1
-0+};
Cristian Marussi
reviewerPATCH v15 18/35 firmware: arm_scmi: Add Telemetry DataEvent read capabilities
In reply to Cristian MarussiAdd support for Telemetry operations needed to read DataEvent values and timestamps as single entities or all together in a single bulk buffer. The returned values are effectiely retrieved from the platform only when strictly needed, i.e. when no fresh recent cached value was already available.
The DataEvent values are fetched transparently from the platform origins using the proper synchronization and consistency primitives, directly from the SHMTIs areas or the FastChannels memory depending on the configuration.
---
v14 --> v15
- use IS_TDE_TS_LINE macro
- use NOT IS_DATA_LINE in tdcf_de_payld_get
- use better startm/endm initial markers in tdcf_de_payld_get
- retry scmi_telemetry_tdcf_de_parse on error too
- trigger resource enumeration also on de_data_read
- skip unitialized FCs on reads and return -EPROTO
- introduce localized single DE snapshot on single DE data read
- widen tde->mtx locking in msg_payld_process
- use unsigned int samples
- better des_sample_get sanity checks
- refactor msg_payld_process
v13 --> v14
- report scmi_telemetry_line_data_parse errors
- add traces for CACHED_DE_UPDATED
- moved some definitions from earlier patches
- using a less regular time interval for TDCF scan retries
- renamed some fields to more strictly adhere to spec nomenclature
v12 --> v13
- use new RAW macros in line_data_parse and check for line validity
- handle PARTIAL_ERROR by logging and carrying on
- relocate tde->mtx guards higher up and add lockdep assertions
- better tdcf/eplg init within tde->mtx
- do not cache tde->base between DE read retries
- retry scan on payld sanity errors
v11 --> v12
- using new TDCF_INVALID_MATCH_SEQ macro
- reporting transient DATA_INVALID lines as -EINVAL
- added cacheable param to msg_payld_process
v10 --> v11
- split out and relocate unrelated code to previous patches
- add TDE mutex_lock when getting payload address
- check LINE_TYPE while parsing payloads
- check overflows and boundary on single sample replies
v8 -->v9
- Fix Sashiko reports for v8
- check if TS supported on FC line before using it
- check for unexpected shorter reply on sample_get
- use non-LE accessors in msg_payld where appropriate
v7 --> v8
- Fixes for some Sashiko issues on v7
- added CACHED_DE_READ trace
- reworked locking around line handling: blkts/uuid
v6 -->v7
- usse proper le32 accesors, fix sparse warns
v5 --> v6
- fixed some Sashiko review
- add new macros parse TDCF-like content on regular ram (non IO)
https://sashiko.dev/#/patchset/20260703123601.381275-1-cristian.marussi%40arm.com?part=10
- fix retry loop in DE parse to avoid exiting on transient errors
- use proper non-IO accessors for msg payld parsing
- add msg payld boundary check
v4 --> v5
- removed UAPI structures dependency
- cleaned up sample_get logic around groups
- fixes sparse warnings
v3 --> v4
- fix typos in comments
- extend TDE cache helpers with cache lookup routine and use cumulative scmi_telemetry_de_sample param
v2 --> v3
- split from monolithic Telemetry patch
- simplity using a few assignement using ternary ops
- remove useless ts param from scanning function
- use a compound literal to simplify samples init
- add a missing __must_check on telemetry_ops
- changed errno on DE read troubles:
- ENODEV/ENOENT: DE is UNKNOWN
- EINVAL: DE is marked as DATA_INVALID
- ENODATA: TLM susbsystem or the specific DE is OFF
--- drivers/firmware/arm_scmi/telemetry.c | 678 ++++++++++++++++++++++++-- include/linux/scmi_protocol.h | 24 + 2 files changed, 666 insertions(+), 36 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +642 -36
@@ -176,6 +176,11 @@ struct scmi_msg_telemetry_config_set {__le32 sampling_rate; }; +struct scmi_msg_resp_telemetry_reading_complete { +__le32 num_dwords; + __le32 array[] __counted_by_le(num_dwords); +}; +/* TDCF */ #define _I (__a) (ioread32((void __iomem *)(__a)))
@@ -384,6 +389,12 @@ struct tdcf {#define TDCF_END_SIGNATURE (e) (_I(&((e)->sign_end)))#define TDCF_BAD_END_SEQ GENMASK(31, 0) +struct telemetry_de_snapshot { +u32 magic; +struct payload payld; +struct payload bts_payld; +}; +struct telemetry_shmti ;struct shmti_snap {
@@ -482,6 +493,7 @@ struct telemetry_de {enum timestamps ts_type;u32 ts_rate;bool enumerated; +bool cached_msg;void __iomem *base;struct eplg __iomem *eplg;u32 sid;
@@ -695,8 +707,9 @@ scmi_telemetry_tde_cache_stale(struct telemetry_de *tde, u32 magic) }static void -scmi_telemetry_tde_cache_update (struct telemetry_de *tde,u64 val, -u64 *tstamp,u32 *magic) +scmi_telemetry_tde_cache_update (struct telemetry_de *tde, +struct scmi_telemetry_de_sample *sample, +u32 *magic) {lockdep_assert_held (&tde->mtx);
@@ -708,10 +721,37 @@ scmi_telemetry_tde_cache_update(struct telemetry_de *tde, u64 val,return ; tde->last_magic = magic ? *magic :TDCF_BAD_END_SEQ ; - tde->last_val = val; - tde->last_ts = tstamp &&scmi_tde_has_tstamp (tde) ? *tstamp :0 ; -if (tstamp) - *tstamp = tde->last_ts; + tde->last_val = sample->val; + tde->last_ts =scmi_tde_has_tstamp (tde) ? sample->tstamp :0 ; +/* Update returned value too */ + sample->tstamp = tde->last_ts; + +trace_scmi_tlm_collect (tde->last_ts, tde->de.info->id, + tde->last_val,"CACHED_DE_UPDATED" ); +} + +static int +scmi_telemetry_tde_cache_lookup (struct telemetry_de *tde, +struct scmi_telemetry_de_sample *sample, +u32 *magic) +{ +lockdep_assert_held (&tde->mtx); + +/* +* Bailout without providing any sample when the cache is +* stale, i.e. last_magic < *magic +*/ +if (magic &&scmi_telemetry_tde_cache_stale (tde, *magic)) +return -EAGAIN ; + + sample->id = tde->de.info->id; + sample->val = tde->last_val; + sample->tstamp = tde->last_ts; + +trace_scmi_tlm_collect (sample->tstamp, sample->id, + sample->val,"CACHED_DE_READ" ); + +return 0 ; }struct scmi_tlm_de_priv {
@@ -2068,45 +2108,45 @@ scmi_telemetry_tde_allocate(struct telemetry_info *ti, u32 de_id,static inline int scmi_telemetry_line_data_parse (struct telemetry_de *tde,struct shmti_snap *snap, -u64 *val,u64 *tstamp,struct payload *payld, -struct payload *bts_payld,u32 magic) +struct scmi_telemetry_de_sample *sample, +struct payload *payld,struct payload *bts_payld, +u32 magic) {u32 meta =PAYLD_META_RAW (payld);lockdep_assert_held (&tde->mtx);/* Clear returned values in case of errors */ - *val =0 ; - -if (tstamp) { - *tstamp =0 ; -if (USE_BLK_TS_RAW (meta)) { -/* Read out the actual BLK_TS */ - *tstamp =scmi_telemetry_blkts_read (magic, tde, - bts_payld, snap); - }else if (LINE_TS_VALID_RAW (meta)) { -/* -* Note that LINE_TS_VALID implies HAS_LINE_EXT and that -* the per DE line_ts_rate is advertised in the DE -* descriptor, BUT we check anyway against buggy FW. -*/ -if (!HAS_LINE_EXT_RAW (meta)) { -trace_scmi_tlm_access (tde->de.info->id, -"SHMTI_BAD_TS_LINE" ,0 ,0 ); -return -EPROTO ; - } - - *tstamp =LINE_TSTAMP_GET_RAW (&payld->tsl); + sample->tstamp =0 ; + sample->val =0 ; + +if (USE_BLK_TS_RAW (meta)) { +/* Read out the actual BLK_TS */ + sample->tstamp =scmi_telemetry_blkts_read (magic, tde, + bts_payld, snap); + }else if (LINE_TS_VALID_RAW (meta)) { +/* +* Note that LINE_TS_VALID implies HAS_LINE_EXT and that +* the per DE line_ts_rate is advertised in the DE +* descriptor, BUT we check anyway against buggy FW. +*/ +if (!HAS_LINE_EXT_RAW (meta)) { +trace_scmi_tlm_access (tde->de.info->id, +"SHMTI_BAD_TS_LINE" ,0 ,0 ); +return -EPROTO ; } + + sample->tstamp =LINE_TSTAMP_GET_RAW (&payld->tsl); }/* Data is always valid since we are NOT handling BLK TS lines here */ - *val =LINE_DATA_GET_RAW (&payld->l); + sample->val =LINE_DATA_GET_RAW (&payld->l); -trace_scmi_tlm_collect (tstamp ? *tstamp :0 , tde->de.info->id, - *val,"SHMTI_DE_READ" ); +/* Trace originally read tstamp */ +trace_scmi_tlm_collect (sample->tstamp, tde->de.info->id, + sample->val,"SHMTI_DE_READ" ); -scmi_telemetry_tde_cache_update (tde, *val, tstamp, &magic); +scmi_telemetry_tde_cache_update (tde, sample, &magic);return 0 ; }
@@ -2223,10 +2263,10 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti, struct telemetry_uuid *active_uuid) {struct telemetry_shmti *shmti = snap->shmti; +struct scmi_telemetry_de_sample sample = {};u32 meta =PAYLD_META_RAW (payld);bool use_blk_ts =USE_BLK_TS_RAW (meta);struct telemetry_de *tde; -u64 val, tstamp =0 ;u32 de_id; de_id =PAYLD_ID_RAW (payld);
@@ -2314,8 +2354,7 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,* Parse data words ... only for the sake of updating TDE cache, * any error is logged by ignored. */ -scmi_telemetry_line_data_parse (tde, snap, &val, &tstamp, payld,NULL , - snap->magic); +scmi_telemetry_line_data_parse (tde, snap, &sample, payld,NULL , snap->magic); }/**
@@ -3366,6 +3405,570 @@ scmi_telemetry_collection_configure(const struct scmi_protocol_handle *ph,return ret; } +static inline int +scmi_telemetry_de_data_fc_read (struct telemetry_de *tde, +struct scmi_telemetry_de_sample *sample) +{ +struct fc_tsline __iomem *fc; + +lockdep_assert_held (&tde->mtx); + +/* +* A FastChannel DE that was discovered as enabled at boot but later +* failed to be enumerated cannot be accessed. +*/ +if (!tde->base) +return -EPROTO ; + + fc = tde->base + tde->offset; + sample->val =LINE_DATA_GET (fc); + sample->tstamp =IS_TDE_TS_LINE (tde) ?LINE_TSTAMP_GET (fc) :0 ; + +/* Trace originally read tstamp */ +trace_scmi_tlm_collect (sample->tstamp, tde->de.info->id, sample->val, +"FC_READ" ); + +/* Cache anyway since TDE caches are on the read path anyway */ +scmi_telemetry_tde_cache_update (tde, sample,NULL ); + +return 0 ; +} + +static void scmi_telemetry_scan_update (struct telemetry_info *ti) +{ +struct telemetry_de *tde; + +/* Scan all SHMTIs ... */ +for (int id =0 ; id < ti->num_shmti; id++) { +int ret; + + ret =scmi_telemetry_shmti_scan (ti, id,SCAN_LOOKUP ); +if (ret) +dev_warn (ti->ph->dev, +"Failed update-scan of SHMTI ID:%d - ret:%d\n" , + id, ret); + } + +if (!ti->info.fc_support) +return ; + +/* Need to enumerate resources to access fastchannels */ + ti->res_get (ti); +list_for_each_entry (tde, &ti->fcs_des, item) { +struct scmi_telemetry_de_sample sample = {}; + +guard (mutex)(&tde->mtx); +if (!tde->de.enabled) +continue ; + +/* +* Only for the sake of updating TDE cache. +* Ignoring errors deriving from malformed FCs. +*/ +scmi_telemetry_de_data_fc_read (tde, &sample); + } +} + +/* +* TDCF and TS Line Management Notes +* --------------------------------- +* +* TDCF Payload Metadata notable bits: +* - Bit[3]: USE BLK Tstamp +* - Bit[2]: Line-Extension Field present (LineTstamp) +* - Bit[1]: Tstamp VALID +* - Bit[0]: Data INVALID +* +* CASE_1: +* ------- +* + A DE is enabled with timestamp disabled, so the TS fields are +* NOT present +* -> BIT[3:0] = 0000b +* +* - 1/A LINE_TSTAMP +* ------------------ +* + that DE is then 're-enabled' with TS: so it was ON, it remains +* ON but using DE_CONFIGURE I now enabled also TS, so the +* platform relocates it at the end of the SHMTI and return the +* new offset +* -> BIT[3:0] = 0110b +* +* - 1/B BLK_TSTAMP +* ------------------ +* + that DE is then 're-enabled' with BLK TS: so it was ON, it +* remains ON but using DE_CONFIGURE, we now also enabled the TS, +* so the platform will: +* - IF a preceding BLK_TS line exist (with same clk rate) +* it relocates the DE at the end of the SHMTI and return the +* new offset (if there is enough room, if not in another SHMTI) +* - IF a preceding BLK_TS line DOES NOT exist (with same clk rate) +* it creates a new BLK_TS line at the end of the SHMTI and then +* relocates the DE after the new BLK_TS and return the +* new offset (if there is enough room, if not in another SHMTI) +* -> BIT[3:1] = 1010b +* +* + the hole left from the relocated DE can be reused by the platform +* to fit another equally sized DE. (i.e. without shuffling around any +* other enabled DE, since that would cause a change of the known offset) +* anyway it will be marked as: +* -> BIT[3:0] = 0101b iff it was a LINE_TSTAMP +* -> BIT[3:0] = 0001b iff it was a BLK_TSTAMP +* +* CASE_2: +* ------- +* + A DE is enabled with LINE timestamp enabled, so the TS_Line is there +* -> BIT[3:0] = 0110b +* + that DE has its timestamp disabled: again, you can do this without +* disabling it fully but just disabling the TS, so now that TS_line +* fields are still physically there but NOT valid +* -> BIT[3:0] = 0100b +* + the hole from the timestamp remain there unused until +* - you enable again the TS so the hole is used again +* -> BIT[3:0] = 0110b +* OR +* - you disable fully the DE and then re-enable it with the TS +* -> potentially CASE_1 the DE is relocated on enable +* + same kind of dynamic applies if the DE had a BLK_TS line +*/ +static int scmi_telemetry_tdcf_de_payld_check (struct telemetry_de *tde, +struct payload *payld) +{ +u32 meta =PAYLD_META_RAW (payld); + +lockdep_assert_held (&tde->mtx); + +if (DATA_INVALID_RAW (meta)) { +trace_scmi_tlm_access (PAYLD_ID_RAW (payld),"DE_DATA_INVALID" ,0 ,0 ); +return -EINVAL ; + } + +if (!IS_DATA_LINE_RAW (meta)) { +trace_scmi_tlm_access (tde->de.info->id,"BAD_DE_LINE_TYPE" ,0 ,0 ); +return -EPROTO ; + } + +if (PAYLD_ID_RAW (payld) != tde->de.info->id) { +trace_scmi_tlm_access (tde->de.info->id,"DE_ID_MISMATCH" ,0 ,0 ); +return -ENODEV ; + } + +/* +* A valid line using BLK_TS should have been initialized with the +* related BLK_TS when enabled, unless is a torn read. +*/ +if ((USE_BLK_TS_RAW (meta) && !tde->bts)) { +trace_scmi_tlm_access (tde->de.info->id,"BAD_USE_BLK_TS" ,0 ,0 ); +return -EPROTO ; + } + +return 0 ; +} + +static int scmi_telemetry_de_snapshot (struct tdcf __iomem *tdcf, +struct telemetry_de *tde, +struct telemetry_de_snapshot *snap) +{ +int retries =SCMI_TLM_TDCF_MAX_RETRIES ; +u32 startm, endm; +int err =0 ; + +lockdep_assert_held (&tde->mtx); + +do { +struct payload __iomem *payld, *bts_payld; + +/* A bit of exponential backoff between retries */ +fsleep ((SCMI_TLM_TDCF_MAX_RETRIES - retries) *SCMI_TLM_TDCF_RETRY_GAP_US ); + + startm =TDCF_START_SEQ_GET (tdcf); +/* Build a default endm certainly different from startm */ + endm = startm +1 ; +if (IS_BAD_START_SEQ (startm)) { +trace_scmi_tlm_access (tde->de.info->id,"MSEQ_BADSTART" , + startm,0 ); +continue ; + } + +/* +* In this context any anomaly detected on the payload could +* due to a torn read, and it is worth retrying a snapshot. +* +* Note that TDE base/offset have been sanitized when +* initialized and cannot have changed: accessing them here +* is safe. +*/ + payld = tde->base + tde->offset; +memcpy_fromio (&snap->payld, payld,LINE_LENGTH_BYTES (payld)); + err =scmi_telemetry_tdcf_de_payld_check (tde, &snap->payld); +if (err) +continue ; + +/* +* Here the associated BLK_TS line, if any, has been verified +* as sane when linked/bound: no need of further validation. +*/ +if (tde->bts) { + bts_payld = tde->base + tde->bts->line.offset; +memcpy_fromio (&snap->bts_payld, bts_payld, +LINE_LENGTH_BYTES (bts_payld)); + } + +/* Assure the snapshot has been fully taken before checking seqnum */ +rmb (); + + endm =TDCF_END_SEQ_GET (tde->eplg); +if (startm != endm) +trace_scmi_tlm_access (tde->de.info->id,"MSEQ_MISMATCH" , + startm, endm); + }while ((startm != endm || err) && --retries); + +if (startm != endm || err) { +trace_scmi_tlm_access (tde->de.info->id, +"TDCF_SNAPSHOT_DE_FAIL" , startm, endm); +return err ?: -EPROTO ; + } + +/* Ensure any snapshot is visible before updating magic */ +smp_store_release (&snap->magic, startm); + +trace_scmi_tlm_access (tde->de.info->id,"TDCF_SNAPSHOT_DE_OK" , + startm, endm); + +return 0 ; +} + +static int +scmi_telemetry_tdcf_de_sample_cached (struct tdcf __iomem *tdcf, +struct telemetry_de *tde, +struct scmi_telemetry_de_sample *sample) +{ +lockdep_assert_held (&tde->mtx); + +u32 startm; + +/* If updating cannot be cached */ + startm =TDCF_START_SEQ_GET (tdcf); +if (IS_BAD_START_SEQ (startm)) +return -EAGAIN ; + +return scmi_telemetry_tde_cache_lookup (tde, sample, &startm); +} + +/** +* scmi_telemetry_tdcf_de_parse - Helper to read one SHMTI/DE +* @tde: A reference to the DE descriptor to read +* @sample: A reference to the sample used as a container for output +* +* This helper can be used to retrieve the data value of a SHMTI based DE +* and any related BLK_TS line: unless the values are found already available +* and up to date in the TDE cache, a limited snapshot of the involved lines +* is taken at first so that all the subsequent DE parsing and processing can +* happen on a stable in RAM copy. +* +* Taking a limited snapshot does not carry relevant overhead and has the +* benefit of allowing the reuse of the same RAM-baased data accessors used +* during a full scan and related full snapshot, instead of a set of dedicated +* __iomem helpers. +* +* NOTE that since each TDE has its own cache, which is accessed and updated +* both during a single DE read and while performing a bulk read, while +* caching consistency assures to provide always the 'freshest' value +* retrieved, mixing single and bulk reads can lead to the values produced by +* the bulk reads to contain 'fresher' late values derived from concurrent +* single DE reads: IOW mixing single and bulk reads can produce a set of bulk +* DEs values that are NOT all from the same moment in time. +* +* Moreover in general, since no assumption can be made about the TDCF sequence +* numbers across different SHMTIs and related optional DE timestamps, it is NOT +* possible to guarantee anyway that data values retrieved from DEs located in +* distinct SHMTIs areas comes from the same platform update cycle at all. +*/ +static int +scmi_telemetry_tdcf_de_parse (struct telemetry_de *tde, +struct scmi_telemetry_de_sample *sample) +{ +struct telemetry_de_snapshot snap = {}; +struct tdcf __iomem *tdcf; +int ret; + +lockdep_assert_held (&tde->mtx); + +/* +* TDEs can be relocated by platform only during enable phase, +* thing that cannot happen until tde->mtx is released. +*/ + tdcf = tde->base; +if (!tdcf) +return -ENODEV ; + +if (!scmi_telemetry_tdcf_de_sample_cached (tdcf, tde, sample)) +return 0 ; + +/* Attempt a localized DE snapshot: DATA_LINE + optionally BLK_TS */ + ret =scmi_telemetry_de_snapshot (tdcf, tde, &snap); +if (ret) +return ret; + +/* Now parse the stable line snapshot */ +return scmi_telemetry_line_data_parse (tde,NULL , sample, &snap.payld, + &snap.bts_payld, snap.magic); +} + +static int scmi_telemetry_de_access (struct telemetry_de *tde, +struct scmi_telemetry_de_sample *sample) +{ +lockdep_assert_held (&tde->mtx); + +if (!tde->de.fc_support) +return scmi_telemetry_tdcf_de_parse (tde, sample); + +return scmi_telemetry_de_data_fc_read (tde, sample); +} + +static int scmi_telemetry_de_collect (struct telemetry_info *ti, +struct scmi_telemetry_de *de, +struct scmi_telemetry_de_sample *sample) +{ +struct telemetry_de *tde =to_tde (de); + +guard (mutex)(&tde->mtx); + +if (!de->enabled) +return -ENODATA ; + +/* +* DE readings returns cached values when: +* - DE data value was retrieved via notification/delayed_response +*/ +if (tde->cached_msg) +return scmi_telemetry_tde_cache_lookup (tde, sample,NULL ); + +return scmi_telemetry_de_access (tde, sample); +} + +static int scmi_telemetry_de_data_read (const struct scmi_protocol_handle *ph, +struct scmi_telemetry_de_sample *sample) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_telemetry_de *de; + +if (!ti->info.enabled || !sample) +return -ENODATA ; + + ti->res_get (ti); + de =xa_load (&ti->xa_des, sample->id); +if (!de) +return -ENOENT ; + +return scmi_telemetry_de_collect (ti, de, sample); +} + +static int +scmi_telemetry_samples_collect (struct telemetry_info *ti,int grp_id, +unsigned int *num_samples, +struct scmi_telemetry_de_sample *samples) +{ +struct scmi_telemetry_res_info *rinfo; +unsigned int max_samples; + + max_samples = *num_samples; + *num_samples =0 ; + + rinfo = ti->res_get (ti); +for (int i =0 ; i < rinfo->num_des; i++) { +struct telemetry_de *tde =to_tde (rinfo->des[i]); + +guard (mutex)(&tde->mtx); + +/* Skip disabled DEs and not belonging to the selected Group */ +if (!tde->de.enabled || + (grp_id !=SCMI_TLM_GRP_INVALID && + (!tde->de.grp || tde->de.grp->info->grp_id != grp_id))) +continue ; + +/* Must be checked here to skip disabled and non selected */ +if (*num_samples == max_samples) +return -ENOSPC ; + +/* Note that this cannot fail when passing a NULL magic */ +scmi_telemetry_tde_cache_lookup (tde, &samples[(*num_samples)++],NULL ); + } + +return 0 ; +} + +static int scmi_telemetry_des_bulk_read (const struct scmi_protocol_handle *ph, +int grp_id,unsigned int *num_samples, +struct scmi_telemetry_de_sample *samples) +{ +struct telemetry_info *ti = ph->get_priv (ph); + +if (!ti->info.enabled || !num_samples || !samples) +return -EINVAL ; + +/* Trigger a full SHMTIs & FCs scan */ +scmi_telemetry_scan_update (ti); + +/* Collect all last cached values */ +return scmi_telemetry_samples_collect (ti, grp_id, num_samples, samples); +} + +static void +scmi_telemetry_msg_payld_process (struct telemetry_info *ti, +unsigned int num_dwords,__le32 *dwords, +size_t payld_bytes,bool cacheable) +{ +struct scmi_telemetry_res_info *rinfo; +u32 next =0 ; + + rinfo = ti->res_get (ti); +if (!rinfo->fully_enumerated) { +dev_warn_once (ti->ph->dev, +"Cannot process DEs in message payload. Drop.\n" ); +return ; + } + +while (next < num_dwords) { +struct payload *payld = (struct payload *)&dwords[next]; +struct scmi_telemetry_de_sample sample = {}; +struct scmi_telemetry_de *de; +struct telemetry_de *tde; +u32 de_id, meta =le32_to_cpu (payld->meta); +unsigned int wlen; + + wlen =LINE_LENGTH_WORDS_RAW (meta); +/* Check Boundary and prepare next iteration */ +if (check_add_overflow (next, wlen, &next) || + next > payld_bytes /sizeof (u32)) { +trace_scmi_tlm_access (0 ,"MSG_OVERFLOW" ,0 ,0 ); +return ; + } + + de_id =le32_to_cpu (payld->id); +if (DATA_INVALID_RAW (meta)) { +trace_scmi_tlm_access (de_id,"MSG_INVALID" ,0 ,0 ); +continue ; + } + +if (LINE_TYPE_RAW (meta) !=TDCF_DATA_LINE ) { +trace_scmi_tlm_access (de_id,"MSG_BAD_TYPE" ,0 ,0 ); +continue ; + } + +if (USE_BLK_TS_RAW (meta)) { +trace_scmi_tlm_access (de_id,"MSG_BAD_USE_BLKTS" ,0 ,0 ); +continue ; + } + + de =xa_load (&ti->xa_des, de_id); +if (!de) { +trace_scmi_tlm_access (de_id,"MSG_DE_UNKNOWN" ,0 ,0 ); +continue ; + } + + tde =to_tde (de); +guard (mutex)(&tde->mtx); +if (!de->enabled) { +trace_scmi_tlm_access (de_id,"MSG_DE_DISABLED" ,0 ,0 ); +continue ; + } + + tde->cached_msg = cacheable; + + sample.val =LINE_DATA_GET_RAW (&payld->tsl); + sample.tstamp =HAS_LINE_EXT_RAW (meta) && +LINE_TS_VALID_RAW (meta) ?LINE_TSTAMP_GET_RAW (&payld->tsl) :0 ; + +/* Update the cache holding the TDE mutex */ +scmi_telemetry_tde_cache_update (tde, &sample,NULL ); + +trace_scmi_tlm_collect (sample.tstamp, de->info->id, sample.val, +"MESSAGE" ); + } +} + +static int scmi_telemetry_des_sample_get (const struct scmi_protocol_handle *ph, +int grp_id,unsigned int *num_samples, +struct scmi_telemetry_de_sample *samples) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_msg_telemetry_config_set *msg; +struct scmi_xfer *t; +bool is_group; +u32 control; +int ret; + +if (!ti->info.enabled || !num_samples || !samples || + !ti->info.single_read_support) +return -EINVAL ; + + is_group = grp_id !=SCMI_TLM_GRP_INVALID ; +if (is_group && grp_id >= ti->info.base.num_groups) +return -EINVAL ; + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_CONFIG_SET , +sizeof (*msg),0 , &t); +if (ret) +return ret; + + msg = t->tx.buf; + msg->group_identifier =cpu_to_le32 (grp_id); + control =TELEMETRY_ENABLE ; + control |= !is_group ?TELEMETRY_SET_SELECTOR_ALL : +TELEMETRY_SET_SELECTOR_GROUP ; + control |=TELEMETRY_MODE_SINGLE ; + msg->control =cpu_to_le32 (control); + msg->sampling_rate =0 ; + + ret = ph->xops->do_xfer_with_response (ph, t); +/* +* PARTIAL_ERRORs are reported as -EBADMSG and related lines, +* marked as DATA_INVALID, will be properly skipped by +* scmi_telemetry_msg_payld_process. +*/ +if (!ret || ret == -EBADMSG ) { +struct scmi_msg_resp_telemetry_reading_complete *r = t->rx.buf; +u32 num_dwords; + +if (ret == -EBADMSG ) +trace_scmi_tlm_access (0 ,"BAD_SAMPLE_PARTIAL_ERROR" ,0 ,0 ); + +if (t->rx.len <sizeof (*r)) +return -EPROTO ; + + num_dwords =le32_to_cpu (r->num_dwords); +/* Update cached DEs values from payload */ +if (num_dwords) { +size_t payld_sz =0 , dwords_sz; + +if (check_mul_overflow (num_dwords,sizeof (r->array[0 ]), &dwords_sz) || +check_add_overflow (sizeof (*r), dwords_sz, &payld_sz) || + payld_sz > t->rx.len) { +dev_err (ph->dev, +"Malformed Telemetry delayed response - num_dwords:%u payld_sz:%zd rx_len:%zd\n" , + num_dwords, payld_sz, t->rx.len); + ret = -EPROTO ; + goto out; + } + +scmi_telemetry_msg_payld_process (ti, num_dwords, r->array, + payld_sz -sizeof (*r), false); + } + +/* Scan and update SMHTIs and FCs */ +scmi_telemetry_scan_update (ti); + +/* Collect all last cached values, override any PARTIAL_ERROR */ + ret =scmi_telemetry_samples_collect (ti, grp_id, num_samples, + samples); + } + +out: + ph->xops->xfer_put (ph, t); + +return ret; +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, .de_lookup = scmi_telemetry_de_lookup,
@@ -3373,6 +3976,9 @@ static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .state_get = scmi_telemetry_state_get, .state_set = scmi_telemetry_state_set, .collection_configure = scmi_telemetry_collection_configure, + .de_data_read = scmi_telemetry_de_data_read, + .des_bulk_read = scmi_telemetry_des_bulk_read, + .des_sample_get = scmi_telemetry_des_sample_get, };/**
include/linux/scmi_protocol.h +24 -0
@@ -994,6 +994,13 @@ struct scmi_telemetry_info {enum scmi_telemetry_collection current_mode; }; +struct scmi_telemetry_de_sample { +unsigned int id; +unsigned int pad; +unsigned long long tstamp; +unsigned long long val; +}; +/** * struct scmi_telemetry_proto_ops - represents the various operations provided * by SCMI Telemetry Protocol
@@ -1008,6 +1015,15 @@ struct scmi_telemetry_info {* @collection_configure: choose a sampling rate and enable SHMTI/FC sampling * for on demand collection via @de_data_read or async * notificatioins for all the enabled DEs. +* @de_data_read: on-demand read of a single DE and related optional timestamp: +* the value will be retrieved at the proper SHMTI offset OR +* from the dedicated FC area (if supported by that DE). +* @des_bulk_read: on-demand read of all the currently enabled DEs, or just +* the ones belonging to a specific group when provided. +* @des_sample_get: on-demand read of all the currently enabled DEs, or just +* the ones belonging to a specific group when provided. +* This causes an immediate update platform-side of all the +* enabled DEs. */ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get)
@@ -1027,6 +1043,14 @@ struct scmi_telemetry_proto_ops { unsigned int res_id, const bool *enable, const unsigned int *update_interval_ms, const enum scmi_telemetry_collection *mode); +int __must_check (*de_data_read)(const struct scmi_protocol_handle *ph, +struct scmi_telemetry_de_sample *sample); +int __must_check (*des_bulk_read)(const struct scmi_protocol_handle *ph, +int grp_id,unsigned int *num_samples, +struct scmi_telemetry_de_sample *samples); +int __must_check (*des_sample_get)(const struct scmi_protocol_handle *ph, +int grp_id,unsigned int *num_samples, +struct scmi_telemetry_de_sample *samples); };/**
Signature · 1 line
2.54.0
Re: [PATCH v15 18/35] firmware: arm_scmi: Add Telemetry DataEvent read capabilities
Quoted 10 lines · On Mon, 9 Nov 2026 09:12:04 +0000 Cristian Marussi <[email protected]>…
Add support for Telemetry operations needed to read DataEvent values and timestamps as single entities or all together in a single bulk buffer. The returned values are effectiely retrieved from the platform only when strictly needed, i.e. when no fresh recent cached value was already available.
The DataEvent values are fetched transparently from the platform origins using the proper synchronization and consistency primitives, directly from the SHMTIs areas or the FastChannels memory depending on the configuration.Signed-off-by Cristian Marussi <[email protected]>
As far as I can spot there is nothing stopping you introducing the struct scmi_telemetry_de_sample much earlier in the set and reducing
the churn here. Does looks like it would be too bad to do so
I'd suggest you do so.
Quoted 12 lines
--- drivers/firmware/arm_scmi/telemetry.c | 678 ++++++++++++++++++++++++-- include/linux/scmi_protocol.h | 24 + 2 files changed, 666 insertions(+), 36 deletions(-)drivers/firmware/arm_scmi/telemetry.c +0
-0
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_tde_cache_stale(struct telemetry_de *tde, u32 magic) · line 711
Quoted 7 lines
drivers/firmware/arm_scmi/telemetry.c +2
-2@@ -695,8 +707,9 @@ scmi_telemetry_tde_cache_stale(struct telemetry_de *tde, u32 magic) } static void -scmi_telemetry_tde_cache_update (struct telemetry_de *tde,u64 val, -u64 *tstamp,u32 *magic) +scmi_telemetry_tde_cache_update(struct telemetry_de *tde, + struct scmi_telemetry_de_sample *sample,
Better to push this back to the patch that introduced this function. I don't think it does any harm to always use the structure for passing in the val and tstamp. Do this earlier will reduce churn here and doesn't look that painful to do.
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +1
-0+ u32 *magic) {lockdep_assert_held (&tde->mtx);@@ -708,10 +721,37 @@ scmi_telemetry_tde_cache_update(struct telemetry_de *tde, u64 val,return ;
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_tde_cache_update(struct telemetry_de *tde, u64 val, · line 727
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +4
-4tde->last_magic = magic ? *magic :TDCF_BAD_END_SEQ ; - tde->last_val = val; - tde->last_ts = tstamp &&scmi_tde_has_tstamp (tde) ? *tstamp :0 ; -if (tstamp) - *tstamp = tde->last_ts; + tde->last_val = sample->val; + tde->last_ts =scmi_tde_has_tstamp (tde) ? sample->tstamp :0 ; +/* Update returned value too */ + sample->tstamp = tde->last_ts;
This is currently rather circular. I guess the aim is to squash any stale timestamps. Probably want a comment about that.
Quoted 5 lines
drivers/firmware/arm_scmi/telemetry.c +5
-0+ +trace_scmi_tlm_collect (tde->last_ts, tde->de.info->id, + tde->last_val,"CACHED_DE_UPDATED" ); +} +
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_tde_cache_update(struct telemetry_de *tde, u64 val, · line 742
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+static int +scmi_telemetry_tde_cache_lookup (struct telemetry_de *tde, +struct scmi_telemetry_de_sample *sample, +u32 *magic) +{ +lockdep_assert_held (&tde->mtx); + +/* +* Bailout without providing any sample when the cache is +* stale, i.e. last_magic < *magic
Short line wrap.
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_tde_cache_update(struct telemetry_de *tde, u64 val, · line 749
Quoted 7 lines
drivers/firmware/arm_scmi/telemetry.c +7
-0+*/ +if (magic &&scmi_telemetry_tde_cache_stale (tde, *magic)) +return -EAGAIN ; + + sample->id = tde->de.info->id; + sample->val = tde->last_val; + sample->tstamp = tde->last_ts;
sample = (struct scmi_telemetry_de_sample) {
.id = tde.info->id,
.val = tde->last_val,
.tstamp = tde->last_ts,
};perhaps so it is clear we are setting it all (clearing the padding seems fine anyway...
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +5
-0+ +trace_scmi_tlm_collect (sample->tstamp, sample->id, + sample->val,"CACHED_DE_READ" ); + +return 0 ; }struct scmi_tlm_de_priv {
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_tde_allocate(struct telemetry_info *ti, u32 de_id, · line 2111
Quoted 6 lines
drivers/firmware/arm_scmi/telemetry.c +1
-2@@ -2068,45 +2108,45 @@ scmi_telemetry_tde_allocate(struct telemetry_info *ti, u32 de_id, static inline int scmi_telemetry_line_data_parse(struct telemetry_de *tde, struct shmti_snap *snap, -u64 *val,u64 *tstamp,struct payload *payld, -struct payload *bts_payld,u32 magic) + struct scmi_telemetry_de_sample *sample,
Similar to above. Maybe makes sense to push the existence of the structure earlier in the patch set to avoid churn at this point.
Quoted 21 lines
drivers/firmware/arm_scmi/telemetry.c +2
-13+ struct payload *payld, struct payload *bts_payld, + u32 magic) { u32 meta = PAYLD_META_RAW(payld);lockdep_assert_held (&tde->mtx);/* Clear returned values in case of errors */ - *val =0 ; - -if (tstamp) { - *tstamp =0 ; -if (USE_BLK_TS_RAW (meta)) { -/* Read out the actual BLK_TS */ - *tstamp =scmi_telemetry_blkts_read (magic, tde, - bts_payld, snap); - }else if (LINE_TS_VALID_RAW (meta)) { -/* -* Note that LINE_TS_VALID implies HAS_LINE_EXT and that -* the per DE line_ts_rate is advertised in the DE -* descriptor, BUT we check anyway against buggy FW.
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_tde_allocate(struct telemetry_info *ti, u32 de_id, · line 2121
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +2
-8-*/ -if (!HAS_LINE_EXT_RAW (meta)) { -trace_scmi_tlm_access (tde->de.info->id, -"SHMTI_BAD_TS_LINE" ,0 ,0 ); -return -EPROTO ; - } - - *tstamp =LINE_TSTAMP_GET_RAW (&payld->tsl); + sample->tstamp =0 ; + sample->val =0 ;
If we are clearing for errors, then maybe do the whole thing? Generally I'd hope we never use values in error paths and the clearing is unnecessary but if doing it seems like doing it general makes most sense.
Quoted 40 lines
drivers/firmware/arm_scmi/telemetry.c +23
-4+ +if (USE_BLK_TS_RAW (meta)) { +/* Read out the actual BLK_TS */ + sample->tstamp =scmi_telemetry_blkts_read (magic, tde, + bts_payld, snap); + }else if (LINE_TS_VALID_RAW (meta)) { +/* +* Note that LINE_TS_VALID implies HAS_LINE_EXT and that +* the per DE line_ts_rate is advertised in the DE +* descriptor, BUT we check anyway against buggy FW. +*/ +if (!HAS_LINE_EXT_RAW (meta)) { +trace_scmi_tlm_access (tde->de.info->id, +"SHMTI_BAD_TS_LINE" ,0 ,0 ); +return -EPROTO ; } + + sample->tstamp =LINE_TSTAMP_GET_RAW (&payld->tsl); }/* Data is always valid since we are NOT handling BLK TS lines here */ - *val =LINE_DATA_GET_RAW (&payld->l); + sample->val =LINE_DATA_GET_RAW (&payld->l); -trace_scmi_tlm_collect (tstamp ? *tstamp :0 , tde->de.info->id, - *val,"SHMTI_DE_READ" ); +/* Trace originally read tstamp */ +trace_scmi_tlm_collect (sample->tstamp, tde->de.info->id, + sample->val,"SHMTI_DE_READ" ); -scmi_telemetry_tde_cache_update (tde, *val, tstamp, &magic); +scmi_telemetry_tde_cache_update (tde, sample, &magic);return 0 ; } +
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_tde_allocate(struct telemetry_info *ti, u32 de_id,
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+static int scmi_telemetry_des_sample_get (const struct scmi_protocol_handle *ph, +int grp_id,unsigned int *num_samples, +struct scmi_telemetry_de_sample *samples) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_msg_telemetry_config_set *msg; +struct scmi_xfer *t; +bool is_group; +u32 control; +int ret;
I guess it is prevented at a higher level, but perhaps some indication of this one not needing to take the config_mtx and why would be helpful. It's not locally obvious why other config messages can race with each other to need the defense on those config_set calls but not this one.
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ +if (!ti->info.enabled || !num_samples || !samples || + !ti->info.single_read_support) +return -EINVAL ; + + is_group = grp_id !=SCMI_TLM_GRP_INVALID ; +if (is_group && grp_id >= ti->info.base.num_groups) +return -EINVAL ; + + ret = ph->xops->xfer_get_init(ph, TELEMETRY_CONFIG_SET,
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_collection_configure(const struct scmi_protocol_handle *ph, · line 3919
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ sizeof(*msg), 0, &t); +if (ret) +return ret; + + msg = t->tx.buf; + msg->group_identifier =cpu_to_le32 (grp_id); + control =TELEMETRY_ENABLE ; + control |= !is_group ?TELEMETRY_SET_SELECTOR_ALL : +TELEMETRY_SET_SELECTOR_GROUP ; + control |=TELEMETRY_MODE_SINGLE ;
Similar to earlier. I think this would be a lot more readable as appropriate FIELD_PREP()
Quoted 41 lines
drivers/firmware/arm_scmi/telemetry.c +41
-0+ msg->control =cpu_to_le32 (control); + msg->sampling_rate =0 ; + + ret = ph->xops->do_xfer_with_response (ph, t); +/* +* PARTIAL_ERRORs are reported as -EBADMSG and related lines, +* marked as DATA_INVALID, will be properly skipped by +* scmi_telemetry_msg_payld_process. +*/ +if (!ret || ret == -EBADMSG ) { +struct scmi_msg_resp_telemetry_reading_complete *r = t->rx.buf; +u32 num_dwords; + +if (ret == -EBADMSG ) +trace_scmi_tlm_access (0 ,"BAD_SAMPLE_PARTIAL_ERROR" ,0 ,0 ); + +if (t->rx.len <sizeof (*r)) +return -EPROTO ; + + num_dwords =le32_to_cpu (r->num_dwords); +/* Update cached DEs values from payload */ +if (num_dwords) { +size_t payld_sz =0 , dwords_sz; + +if (check_mul_overflow (num_dwords,sizeof (r->array[0 ]), &dwords_sz) || +check_add_overflow (sizeof (*r), dwords_sz, &payld_sz) || + payld_sz > t->rx.len) { +dev_err (ph->dev, +"Malformed Telemetry delayed response - num_dwords:%u payld_sz:%zd rx_len:%zd\n" , + num_dwords, payld_sz, t->rx.len); + ret = -EPROTO ; + goto out; + } + +scmi_telemetry_msg_payld_process (ti, num_dwords, r->array, + payld_sz -sizeof (*r), false); + } + +/* Scan and update SMHTIs and FCs */ +scmi_telemetry_scan_update (ti); +
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_collection_configure(const struct scmi_protocol_handle *ph, · line 3970
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+/* Collect all last cached values, override any PARTIAL_ERROR */ + ret =scmi_telemetry_samples_collect (ti, grp_id, num_samples, + samples); + } + +out: + ph->xops->xfer_put (ph, t); + +return ret; +}
;
Quoted 7 lines
/**
include/linux/scmi_protocol.h +0
-0
include/linux/scmi_protocol.h struct scmi_telemetry_info { · line 1001
Quoted 8 lines
include/linux/scmi_protocol.h +5
-0@@ -994,6 +994,13 @@ struct scmi_telemetry_info {enum scmi_telemetry_collection current_mode; }; +struct scmi_telemetry_de_sample { +unsigned int id; +unsigned int pad; +unsigned long long tstamp; +unsigned long long val;
Why not the fixed size kernel types?
Quoted 1 line
include/linux/scmi_protocol.h +1
-0+};
Cristian Marussi
reviewerPATCH v15 19/35 firmware: arm_scmi: Add Telemetry reset logic
In reply to Cristian MarussiAdd support for Telemetry operations needed to request platform to reset telemetry current configuration and data events values.
---
v14 --> v15
- clear cached_msg on RESET
- clearstreaming_mode instead of notif_enabled
v13 --> v14
- add a barrier to acquire rinfo->num_des on reset
- avoid lock inversion on config_mtx
v12 --> v13
- relocate tde->mtx guards higher up and add lockdep assertions
- unlink BLKTS under tde->mtx on local free
v11 --> v12
- using config_mtx to sync with ongoing configurations
- use proper helpers to update cache state
v10 --> v11
- protect TDE updates on reset
- use des_enable mutex when clearing des cache
- zero also active_update_interval
v8 --> v9
- add a commment on why NOT unlinking UUID
v6 --> v7
- add proper cleanup of sid/offs/uuids and caches on RESET
v5 --> v6
- added missing reset of global state
v3 --> v4
- reset tracked Telemetry DE state
v2 --> v3
- split from monolithic Telemetry patch
- use scmi_telemetry_de_unlink
--- drivers/firmware/arm_scmi/telemetry.c | 81 +++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 2 + 2 files changed, 83 insertions(+)
drivers/firmware/arm_scmi/telemetry.c +81 -0
@@ -3969,6 +3969,86 @@ static int scmi_telemetry_des_sample_get(const struct scmi_protocol_handle *ph,return ret; } +static void scmi_telemetry_local_resources_reset (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; +unsigned int num_des; + + ti->info.enabled = false; + ti->info.current_mode =SCMI_TLM_ONDEMAND ; + ti->streaming_mode = false; + ti->info.active_update_interval =0 ; + +/* Get rinfo without triggering an enumeration, cannot be NULL anyway */ + rinfo =__scmi_telemetry_resources_get (ti); +/* Make sure to see the latest num_des */ + num_des =smp_load_acquire (&rinfo->num_des); +/* Clear all local state...*/ +for (int i =0 ; i < num_des; i++) { +struct telemetry_de *tde =to_tde (rinfo->des[i]); + + scoped_guard(mutex, &tde->mtx) { + tde->last_ts =0 ; + tde->last_val =0 ; + tde->last_magic =0 ; + tde->cached_msg = false; +if (!tde->de.fc_support) { + tde->sid =SHMTI_ID_INVALID ; + tde->base =NULL ; + tde->offset =0 ; + tde->eplg =NULL ; + } + +scmi_telemetry_de_state_update (ti,ENA_STATE , + &tde->de.enabled, false); + +if (tde->de.tstamp_support) +scmi_telemetry_de_state_update (ti,ENA_TSTAMP , + &tde->de.tstamp_enabled, + false); + +/* +* Unlinking ONLY BLK_TS lines since, per the SCMI spec, +* the UUID/DE associations are permanent within the same +* session boot: after a reset has cleared the server-side +* configuration the next time SHMTIs will be re-populated, +* these same UUID/DE associations will be re-instated. +*/ +scmi_telemetry_blkts_unlink (tde); + } + } + +for (int i =0 ; i < rinfo->num_groups; i++) { + rinfo->grps[i].enabled = false; + rinfo->grps[i].tstamp_enabled = false; + rinfo->grps[i].current_mode =SCMI_TLM_ONDEMAND ; + rinfo->grps[i].active_update_interval =0 ; + } +} + +static int scmi_telemetry_reset (const struct scmi_protocol_handle *ph) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_xfer *t; +int ret; + +/* Stall concurrent configuration request */ +guard (mutex)(&ti->config_mtx); + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_RESET ,sizeof (u32),0 , &t); +if (ret) +return ret; + +put_unaligned_le32 (0 , t->tx.buf); + ret = ph->xops->do_xfer (ph, t); +if (!ret) +scmi_telemetry_local_resources_reset (ti); + + ph->xops->xfer_put (ph, t); + +return ret; +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, .de_lookup = scmi_telemetry_de_lookup,
@@ -3979,6 +4059,7 @@ static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .de_data_read = scmi_telemetry_de_data_read, .des_bulk_read = scmi_telemetry_des_bulk_read, .des_sample_get = scmi_telemetry_des_sample_get, + .reset = scmi_telemetry_reset, };/**
include/linux/scmi_protocol.h +2 -0
@@ -1024,6 +1024,7 @@ struct scmi_telemetry_de_sample {* the ones belonging to a specific group when provided. * This causes an immediate update platform-side of all the * enabled DEs. +* @reset: reset configuration and telemetry data. */ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get)
@@ -1051,6 +1052,7 @@ struct scmi_telemetry_proto_ops {int __must_check (*des_sample_get)(const struct scmi_protocol_handle *ph,int grp_id,unsigned int *num_samples,struct scmi_telemetry_de_sample *samples); +int (*reset)(const struct scmi_protocol_handle *ph); };/**
Signature · 1 line
2.54.0
Re: [PATCH v15 19/35] firmware: arm_scmi: Add Telemetry reset logic
Quoted 4 lines · On Mon, 9 Nov 2026 09:12:05 +0000 Cristian Marussi <[email protected]>…
Add support for Telemetry operations needed to request platform to reset telemetry current configuration and data events values.
Signed-off-by Cristian Marussi <[email protected]>
Just one trivial thing that might be wrong anyway.
Quoted 22 lines
--- drivers/firmware/arm_scmi/telemetry.c | 81 +++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 2 + 2 files changed, 83 insertions(+)drivers/firmware/arm_scmi/telemetry.c +9
-0@@ -3969,6 +3969,86 @@ static int scmi_telemetry_des_sample_get(const struct scmi_protocol_handle *ph,return ret; } +static void scmi_telemetry_local_resources_reset (struct telemetry_info *ti) +{ +struct scmi_telemetry_res_info *rinfo; +unsigned int num_des; + + ti->info.enabled = false; + ti->info.current_mode =SCMI_TLM_ONDEMAND ; + ti->streaming_mode = false; + ti->info.active_update_interval =0 ;
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_des_sample_get(const struct scmi_protocol_handle *ph, · line 3990
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+ +/* Get rinfo without triggering an enumeration, cannot be NULL anyway */ + rinfo =__scmi_telemetry_resources_get (ti); +/* Make sure to see the latest num_des */ + num_des =smp_load_acquire (&rinfo->num_des); +/* Clear all local state...*/ +for (int i =0 ; i < num_des; i++) { +struct telemetry_de *tde =to_tde (rinfo->des[i]); + + scoped_guard(mutex, &tde->mtx) {
Unless this gets more complex later,
guard(mutex)(&tde->mtx);
and resulting drop in indent.
If it does get more complex ignore this.
Quoted 92 lines
drivers/firmware/arm_scmi/telemetry.c +62
-0+ tde->last_ts =0 ; + tde->last_val =0 ; + tde->last_magic =0 ; + tde->cached_msg = false; +if (!tde->de.fc_support) { + tde->sid =SHMTI_ID_INVALID ; + tde->base =NULL ; + tde->offset =0 ; + tde->eplg =NULL ; + } + +scmi_telemetry_de_state_update (ti,ENA_STATE , + &tde->de.enabled, false); + +if (tde->de.tstamp_support) +scmi_telemetry_de_state_update (ti,ENA_TSTAMP , + &tde->de.tstamp_enabled, + false); + +/* +* Unlinking ONLY BLK_TS lines since, per the SCMI spec, +* the UUID/DE associations are permanent within the same +* session boot: after a reset has cleared the server-side +* configuration the next time SHMTIs will be re-populated, +* these same UUID/DE associations will be re-instated. +*/ +scmi_telemetry_blkts_unlink (tde); + } + } + +for (int i =0 ; i < rinfo->num_groups; i++) { + rinfo->grps[i].enabled = false; + rinfo->grps[i].tstamp_enabled = false; + rinfo->grps[i].current_mode =SCMI_TLM_ONDEMAND ; + rinfo->grps[i].active_update_interval =0 ; + } +} + +static int scmi_telemetry_reset (const struct scmi_protocol_handle *ph) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct scmi_xfer *t; +int ret; + +/* Stall concurrent configuration request */ +guard (mutex)(&ti->config_mtx); + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_RESET ,sizeof (u32),0 , &t); +if (ret) +return ret; + +put_unaligned_le32 (0 , t->tx.buf); + ret = ph->xops->do_xfer (ph, t); +if (!ret) +scmi_telemetry_local_resources_reset (ti); + + ph->xops->xfer_put (ph, t); + +return ret; +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, .de_lookup = scmi_telemetry_de_lookup,@@ -3979,6 +4059,7 @@ static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .de_data_read = scmi_telemetry_de_data_read, .des_bulk_read = scmi_telemetry_des_bulk_read, .des_sample_get = scmi_telemetry_des_sample_get, + .reset = scmi_telemetry_reset, };/** include/linux/scmi_protocol.h +2
-0@@ -1024,6 +1024,7 @@ struct scmi_telemetry_de_sample {* the ones belonging to a specific group when provided. * This causes an immediate update platform-side of all the * enabled DEs. +* @reset: reset configuration and telemetry data. */ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get)@@ -1051,6 +1052,7 @@ struct scmi_telemetry_proto_ops {int __must_check (*des_sample_get)(const struct scmi_protocol_handle *ph,int grp_id,unsigned int *num_samples,struct scmi_telemetry_de_sample *samples); +int (*reset)(const struct scmi_protocol_handle *ph); };/**
Cristian Marussi
reviewerPATCH v15 20/35 firmware: arm_scmi: Add Telemetry notification support
In reply to Cristian MarussiAdd support for notifications to Telemetry protocol and register an internal notifier during protocol initialization: any DE value received inside a notification payload will be cached for future user consumption.
---
v14 --> v15
- add an overflow check on payload lines parsing
- convert to Linux errno before building notification report
- add traces and comments
v13 --> v14
- moved some definitions from earlier split patch
v12 --> v13
- handle PARTIAL_ERROR: log and ignore
- unlink UUID under mutex
v11 --> v12
- marking notifs TDE as cacheable
- updating cache within TDE lock when processing msg_payld
v10 --> v11
- link DE received by notif to the primary UUID when first seen
v8 --> v9
- fix notif boundary and wraparound checks
- rollback notif report payload in __le32 format before parsing to align with single read payload parsing
v6 --> v7
- fix sparse warn on notof payload handling
v5 -> v6
- use protocol instance notifier interface
- use new RAW macros to access payload
- add proper boundary checks on notification payload
- fix mutex scope in msg payld parsing
- add notification payload consistency check (Sahiko)
v3 -> v4
- use TDE cache to save DEs received via msg payload
v2 --> v3
- changed a few dev_err into traces
- split from monolithic telemetry protocol patch
- use memcpy_from_le32
--- drivers/firmware/arm_scmi/telemetry.c | 184 +++++++++++++++++++++++++- include/linux/scmi_protocol.h | 9 ++ 2 files changed, 190 insertions(+), 3 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +181 -3
@@ -30,6 +30,7 @@#include <linux/uuid.h> #include <linux/xarray.h> +#include "errors.h" #include "protocols.h" #include "notify.h"
@@ -69,6 +70,13 @@ struct scmi_msg_resp_telemetry_protocol_attributes {__le32 default_blk_ts_rate; }; +struct scmi_telemetry_update_notify_payld { +__le32 agent_id; +__le32 status; +__le32 num_dwords; + __le32 array[] __counted_by_le(num_dwords); +}; +struct scmi_shmti_desc {__le32 id;__le32 addr_low;
@@ -552,12 +560,16 @@ struct telemetry_info {/* Protect global and per-group configuration data */ struct mutex config_mtx;struct scmi_telemetry_info info; +struct notifier_block telemetry_nb;atomic_t rinfo_initializing;struct completion rinfo_initdone;struct scmi_telemetry_res_info *rinfo;struct scmi_telemetry_res_info *(*res_get)(struct telemetry_info *ti); }; +#define telemetry_nb_to_info (x) \ + container_of(x, struct telemetry_info, telemetry_nb) +static struct scmi_telemetry_res_info *__scmi_telemetry_resources_get (struct telemetry_info *ti);
@@ -3831,9 +3843,9 @@ scmi_telemetry_msg_payld_process(struct telemetry_info *ti,while (next < num_dwords) {struct payload *payld = (struct payload *)&dwords[next];struct scmi_telemetry_de_sample sample = {}; +u32 de_id, meta =le32_to_cpu (payld->meta);struct scmi_telemetry_de *de;struct telemetry_de *tde; -u32 de_id, meta =le32_to_cpu (payld->meta);unsigned int wlen; wlen =LINE_LENGTH_WORDS_RAW (meta);
@@ -3874,7 +3886,6 @@ scmi_telemetry_msg_payld_process(struct telemetry_info *ti, } tde->cached_msg = cacheable; - sample.val =LINE_DATA_GET_RAW (&payld->tsl); sample.tstamp =HAS_LINE_EXT_RAW (meta) &&LINE_TS_VALID_RAW (meta) ?LINE_TSTAMP_GET_RAW (&payld->tsl) :0 ;
@@ -3882,6 +3893,10 @@ scmi_telemetry_msg_payld_process(struct telemetry_info *ti,/* Update the cache holding the TDE mutex */ scmi_telemetry_tde_cache_update (tde, &sample,NULL ); +/* Link notifications to Primary UUID */ +if (cacheable) +scmi_telemetry_uuid_link (tde, ti->primary_uuid); +trace_scmi_tlm_collect (sample.tstamp, de->info->id, sample.val,"MESSAGE" ); }
@@ -4062,6 +4077,148 @@ static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .reset = scmi_telemetry_reset, }; +static bool +scmi_telemetry_notify_supported (const struct scmi_protocol_handle *ph, +u8 evt_id,u32 src_id) +{ +struct telemetry_info *ti = ph->get_priv (ph); + +return ti->info.continuous_update_support; +} + +static int +scmi_telemetry_set_notify_enabled (const struct scmi_protocol_handle *ph, +u8 evt_id,u32 src_id,bool enable) +{ +/* +* As defined by the specification, SCMI Telemetry notifications are a +* kind of their own: the enable process deviates slightly from usual +* notifications behaviour and a notifier has to be registered anyway +* right after protocol initialization if such notifications are +* supported at all by the platform. +* IOW, SCMI Telemetry notifications are in fact always enabled when +* supported, so this helper needs to do nothing but succeed. +*/ +return 0 ; +} + +static void * +scmi_telemetry_fill_custom_report (const struct scmi_protocol_handle *ph, +u8 evt_id,ktime_t timestamp, +const void *payld,size_t payld_sz, +void *report,u32 *src_id) +{ +const struct scmi_telemetry_update_notify_payld *p = payld; +struct scmi_telemetry_update_report *r = report; +size_t dwords_sz, msg_payld_sz; + +/* At least sized as an empty notification */ +if (payld_sz <sizeof (*p)) +return NULL ; + + r->timestamp = timestamp; + r->agent_id =le32_to_cpu (p->agent_id); + r->status =scmi_to_linux_errno (le32_to_cpu (p->status)); + r->num_dwords =le32_to_cpu (p->num_dwords); + +/*Check boundaries and wraparounds */ +if (check_mul_overflow (r->num_dwords,sizeof (r->dwords[0 ]), &dwords_sz)) +return NULL ; + +if (check_add_overflow (sizeof (*p), dwords_sz, &msg_payld_sz)) +return NULL ; + +if (msg_payld_sz > payld_sz) +return NULL ; + +/* +* Allocated dwords and report are sized as max_msg_size, so as +* to allow for the maximum payload permitted by the configured +* transport. Overflow is not possible since out-of-size messages +* are dropped at the transport layer. +*/ +if (r->num_dwords) +memcpy_from_le32 (r->dwords, p->array, r->num_dwords); + + *src_id =0 ; + +return r; +} + +static const struct scmi_event tlm_events[] = { + { + .id =SCMI_EVENT_TELEMETRY_UPDATE , + .max_payld_sz =0 , + .max_report_sz =0 , + }, +}; + +static const struct scmi_event_ops tlm_event_ops = { + .is_notify_supported = scmi_telemetry_notify_supported, + .set_notify_enabled = scmi_telemetry_set_notify_enabled, + .fill_custom_report = scmi_telemetry_fill_custom_report, +}; + +static const struct scmi_protocol_events tlm_protocol_events = { + .queue_sz =SCMI_PROTO_QUEUE_SZ , + .ops = &tlm_event_ops, + .evts = tlm_events, + .num_events =ARRAY_SIZE (tlm_events), + .num_sources =1 , +}; + +static int scmi_telemetry_notifier (struct notifier_block *nb, +unsigned long event,void *data) +{ +struct scmi_telemetry_update_report *er = data; +struct telemetry_info *ti =telemetry_nb_to_info (nb); + +/* +* PARTIAL_ERRORs are reported as -EBADMSG and related lines, +* marked as DATA_INVALID, will be properly skipped by +* scmi_telemetry_msg_payld_process. +*/ +if (er->status && er->status != -EBADMSG ) { +trace_scmi_tlm_access (0 ,"BAD_NOTIF_MSG" ,0 ,0 ); +return NOTIFY_DONE ; + }else if (er->status == -EBADMSG ) { +trace_scmi_tlm_access (0 ,"BAD_NOTIF_PARTIAL_ERROR" ,0 ,0 ); + } + +trace_scmi_tlm_access (0 ,"TLM_UPDATE_MSG" ,0 ,0 ); +/* Lookup the embedded DEs in the notification payload ... */ +if (er->num_dwords) { +size_t payld_bytes =sizeof (er->dwords[0 ]) * er->num_dwords; + + __le32 *dwords __free(kvfree) = +kvzalloc_objs (*dwords, er->num_dwords); +if (!dwords) { +trace_scmi_tlm_access (0 ,"TLM_UPDATE_MSG_FAILED" ,0 ,0 ); +return NOTIFY_DONE ; + } + +/* The report payload size was bound checked on rx */ +memcpy_to_le32 (dwords, er->dwords, er->num_dwords); +/* +* Note that the DEs received via Notifications can only be +* updated that way, and as such are cached at each reception +* and never read on-demand. +*/ +scmi_telemetry_msg_payld_process (ti, er->num_dwords, dwords, + payld_bytes, true); + } + +/* +* ...scan the SHMTI/FCs for any other DE updates if streaming mode +* is enabled since it is an indication that someone explcitly requested +* collection by notification. +*/ +if (ti->streaming_mode) +scmi_telemetry_scan_update (ti); + +return NOTIFY_OK ; +} +/** * scmi_telemetry_resources_alloc - Resources allocation * @ti: A reference to the telemetry info descriptor for this instance
@@ -4488,7 +4645,27 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph) ti->info.base.version = ph->version; -return ph->set_priv (ph, ti); + ret = ph->set_priv (ph, ti); +if (ret) +return ret; + +/* +* Register a notifier anyway straight upon protocol initialization +* since there could be some DEs that are ONLY reported by notifications +* even though the chosen collection method was SHMTI/FCs. +*/ +if (ti->info.continuous_update_support) { + ti->telemetry_nb.notifier_call = &scmi_telemetry_notifier; + ret = ph->instance_notifier_register (ph,SCMI_EVENT_TELEMETRY_UPDATE , +NULL , &ti->telemetry_nb); +if (ret) { +dev_err (ph->dev, +"Could NOT register Telemetry notifier\n" ); +return ret; + } + } + +return 0 ; }static const struct scmi_protocol scmi_telemetry = {
@@ -4496,6 +4673,7 @@ static const struct scmi_protocol scmi_telemetry = { .owner = THIS_MODULE, .instance_init = &scmi_telemetry_protocol_init, .ops = &tlm_proto_ops, + .events = &tlm_protocol_events, .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, };
include/linux/scmi_protocol.h +9 -0
@@ -1255,6 +1255,7 @@ enum scmi_notification_events {SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER =0x0 ,SCMI_EVENT_POWERCAP_CAP_CHANGED =0x0 ,SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED =0x1 , + SCMI_EVENT_TELEMETRY_UPDATE = 0x0, }; struct scmi_power_state_changed_report {
@@ -1342,4 +1343,12 @@ struct scmi_powercap_meas_changed_report {unsigned int domain_id; unsigned int power; }; + +struct scmi_telemetry_update_report { +ktime_t timestamp; +unsigned int agent_id; +int status; +unsigned int num_dwords; +unsigned int dwords[]; +};#endif /* _LINUX_SCMI_PROTOCOL_H */
Signature · 1 line
2.54.0
Re: [PATCH v15 20/35] firmware: arm_scmi: Add Telemetry notification support
Quoted 5 lines · On Mon, 9 Nov 2026 09:12:06 +0000 Cristian Marussi <[email protected]>…
Add support for notifications to Telemetry protocol and register an internal notifier during protocol initialization: any DE value received inside a notification payload will be cached for future user consumption.
Signed-off-by Cristian Marussi <[email protected]>
Trivial things inline.
Quoted 24 lines
drivers/firmware/arm_scmi/telemetry.c | 184 +++++++++++++++++++++++++- include/linux/scmi_protocol.h | 9 ++ 2 files changed, 190 insertions(+), 3 deletions(-)drivers/firmware/arm_scmi/telemetry.c +1
-1static struct scmi_telemetry_res_info *__scmi_telemetry_resources_get (struct telemetry_info *ti);@@ -3831,9 +3843,9 @@ scmi_telemetry_msg_payld_process(struct telemetry_info *ti,while (next < num_dwords) {struct payload *payld = (struct payload *)&dwords[next];struct scmi_telemetry_de_sample sample = {}; +u32 de_id, meta =le32_to_cpu (payld->meta);struct scmi_telemetry_de *de;struct telemetry_de *tde; -u32 de_id, meta =le32_to_cpu (payld->meta);unsigned int wlen; wlen =LINE_LENGTH_WORDS_RAW (meta);
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_msg_payld_process(struct telemetry_info *ti, · line 3877
Quoted 4 lines
drivers/firmware/arm_scmi/telemetry.c +0
-1@@ -3874,7 +3886,6 @@ scmi_telemetry_msg_payld_process(struct telemetry_info *ti, } tde->cached_msg = cacheable; -
If you want this, figure out which patch to push it back to.
Quoted 20 lines
drivers/firmware/arm_scmi/telemetry.c +5
-0sample.val =LINE_DATA_GET_RAW (&payld->tsl); sample.tstamp =HAS_LINE_EXT_RAW (meta) &&LINE_TS_VALID_RAW (meta) ?LINE_TSTAMP_GET_RAW (&payld->tsl) :0 ;@@ -3882,6 +3893,10 @@ scmi_telemetry_msg_payld_process(struct telemetry_info *ti,/* Update the cache holding the TDE mutex */ scmi_telemetry_tde_cache_update (tde, &sample,NULL ); +/* Link notifications to Primary UUID */ +if (cacheable) +scmi_telemetry_uuid_link (tde, ti->primary_uuid); +trace_scmi_tlm_collect (sample.tstamp, de->info->id, sample.val,"MESSAGE" ); }@@ -4062,6 +4077,148 @@ static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .reset = scmi_telemetry_reset, }; +
drivers/firmware/arm_scmi/telemetry.c static const struct scmi_telemetry_proto_ops tlm_proto_ops = { · line 4091
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+static int scmi_telemetry_notifier (struct notifier_block *nb, +unsigned long event,void *data) +{ +struct scmi_telemetry_update_report *er = data; +struct telemetry_info *ti =telemetry_nb_to_info (nb); + +/* +* PARTIAL_ERRORs are reported as -EBADMSG and related lines, +* marked as DATA_INVALID, will be properly skipped by +* scmi_telemetry_msg_payld_process.
Doesn't matter much given it will still be 3 lines, but inconsistent short wrap.
Quoted 9 lines
drivers/firmware/arm_scmi/telemetry.c +4
-0+*/ +if (er->status && er->status != -EBADMSG ) { +trace_scmi_tlm_access (0 ,"BAD_NOTIF_MSG" ,0 ,0 ); +return NOTIFY_DONE ;/** * scmi_telemetry_resources_alloc - Resources allocation * @ti: A reference to the telemetry info descriptor for this instance
drivers/firmware/arm_scmi/telemetry.c static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph) · line 4650
Quoted 7 lines
drivers/firmware/arm_scmi/telemetry.c +3
-1@@ -4488,7 +4645,27 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph) ti->info.base.version = ph->version; -return ph->set_priv (ph, ti); + ret = ph->set_priv (ph, ti); +if (ret) +return ret;
Can drive this change back to original introduction to avoid a bit of churn.
Quoted 22 lines
drivers/firmware/arm_scmi/telemetry.c +18
-0+ +/* +* Register a notifier anyway straight upon protocol initialization +* since there could be some DEs that are ONLY reported by notifications +* even though the chosen collection method was SHMTI/FCs. +*/ +if (ti->info.continuous_update_support) { + ti->telemetry_nb.notifier_call = &scmi_telemetry_notifier; + ret = ph->instance_notifier_register (ph,SCMI_EVENT_TELEMETRY_UPDATE , +NULL , &ti->telemetry_nb); +if (ret) { +dev_err (ph->dev, +"Could NOT register Telemetry notifier\n" ); +return ret; + } + } + +return 0 ; }struct scmi_power_state_changed_report {
drivers/firmware/arm_scmi/telemetry.c struct scmi_powercap_meas_changed_report { · line 1352
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +7
-0@@ -1342,4 +1343,12 @@ struct scmi_powercap_meas_changed_report {unsigned int domain_id;unsigned int power; }; + +struct scmi_telemetry_update_report { +ktime_t timestamp; +unsigned int agent_id; +int status; +unsigned int num_dwords; +unsigned int dwords[];
__counted_by?
Quoted 2 lines
drivers/firmware/arm_scmi/telemetry.c +1
-0+};#endif /* _LINUX_SCMI_PROTOCOL_H */
Cristian Marussi
reviewerPATCH v15 21/35 firmware: arm_scmi: Add Telemetry boot-on support
In reply to Cristian MarussiAdd the initialization and discovery logic needed to detect when the platform SCMI server is configured with telemetry enabled at boot and perform all the needed resource enumerations to keep the kernel telemetry subsystem state aligned with the platform boot-on configurations.
---
v14 --> v15
- use IS_TDE_TS_ macro
- retrieve collection_mode at boot-on and force ignore groups
- count only successfully initialized groups at boot-on
v13 --> v14
- moved some definitions from earlier split patch
- renamed some fields to more strictly adhere to spec nomenclature
v12 --> v13
- greb tde->mtx on DE_ENABLED_LIST
v11 --> v12
- fixed TS_RATE determination at boot-on
v10 --> v11
- fix non-conformancy on CONFIG_GET when groups NON configurable
- use SCAN_UPDATE also on BOOT-ON initial scan (NO discovery)
- avoid 'naked' ti->rinfo accesses
v8 -- > v9
- remove useless initial state lookup after RESET
- properly set de->tstamp_support on DE_ENABLED_LIST
v5 --> v6
- reporte properly an error on scmi_telemetry_config_lookup() failure (Sashiko)
v4 --> v5
- use renamed grp_id instead of old id in group descriptors
- fixes sparse warnings
v3 --> v4
- bail-out FW_BUG errors
- fix typos in comments
- track boot-on Telemetry DE states
v2 --> v3
- split from monolithic telemetry protocol patch
- swap logic in scmi_telemetry_initial_state_lookup
--- drivers/firmware/arm_scmi/telemetry.c | 287 +++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 7 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +280 -7
@@ -141,6 +141,21 @@ struct scmi_msg_resp_telemetry_update_intervals {__le32 intervals[]; }; +struct scmi_msg_telemetry_de_enabled_list { +__le32 index; +__le32 flags; +}; + +struct scmi_enabled_de_desc { +__le32 id; +__le32 mode; +}; + +struct scmi_msg_resp_telemetry_de_enabled_list { +__le32 flags; +struct scmi_enabled_de_desc array[]; +}; +struct scmi_msg_telemetry_de_configure {__le32 identifier;__le32 flags;
@@ -189,6 +204,20 @@ struct scmi_msg_resp_telemetry_reading_complete { __le32 array[] __counted_by_le(num_dwords); }; +struct scmi_msg_telemetry_config_get { +__le32 group_identifier; +__le32 flags; +#define TELEMETRY_GET_SELECTOR (x) (FIELD_PREP(GENMASK(3, 0), (x))) +#define TELEMETRY_GET_SELECTOR_ORPHANS TELEMETRY_GET_SELECTOR(TLM_ORPHANS) +#define TELEMETRY_GET_SELECTOR_GROUP TELEMETRY_GET_SELECTOR(TLM_GROUP) +#define TELEMETRY_GET_SELECTOR_ALL TELEMETRY_GET_SELECTOR(TLM_ALL) +}; + +struct scmi_msg_resp_telemetry_config_get { +__le32 control; +__le32 sampling_rate; +}; +/* TDCF */ #define _I (__a) (ioread32((void __iomem *)(__a)))
@@ -528,6 +557,8 @@ static inline bool scmi_tde_has_tstamp(struct telemetry_de *t)return t->de.tstamp_support && t->de.tstamp_enabled; } +#define DE_ENABLED_WITH_TSTAMP 2 +enum de_state {ENA_STATE ,ENA_TSTAMP ,
@@ -943,9 +974,14 @@ static int scmi_telemetry_de_descriptor_parse(struct telemetry_info *ti,/* Variably sized depending on TS support */ *next +=sizeof (*line_ts_rate); - }else if (tde->ts_type ==TSTAMP_BLK ) { -/* Setup default BLK TS value at first */ - tde->de.info->ts_rate = ti->default_blk_ts_rate; + }else if (IS_TDE_TS_BLK (tde)) { +/* +* Setup default BLK TS rate value at first, if it has NOT +* already been picked from the BLK_TS line by the boot-on +* SCAN_UPDATE. +*/ +if (!tde->de.info->ts_rate) + tde->de.info->ts_rate = ti->default_blk_ts_rate; }if (tde->de.fc_support) {
@@ -1077,6 +1113,224 @@ static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph,return 0 ; } +static int scmi_telemetry_config_lookup (struct telemetry_info *ti, +unsigned int grp_id,bool *enabled, +unsigned int *active_update_interval, +enum scmi_telemetry_collection *mode, +bool ignore_groups) +{ +const struct scmi_protocol_handle *ph = ti->ph; +struct scmi_msg_telemetry_config_get *msg; +struct scmi_msg_resp_telemetry_config_get *resp; +bool is_group = grp_id !=SCMI_TLM_GRP_INVALID ; +struct scmi_xfer *t; +u32 flags; +int ret; + + ret = ph->xops->xfer_get_init (ph,TELEMETRY_CONFIG_GET , +sizeof (*msg),sizeof (*resp), &t); +if (ret) +return ret; + + msg = t->tx.buf; + msg->group_identifier =cpu_to_le32 (grp_id); + +/* Spec mandates the ALL selector when per-group config not available */ +if (!ti->info.per_group_config_support || ignore_groups) + flags =TELEMETRY_GET_SELECTOR_ALL ; +else + flags = !is_group ?TELEMETRY_GET_SELECTOR_ORPHANS : +TELEMETRY_GET_SELECTOR_GROUP ; + + msg->flags =cpu_to_le32 (flags); + + resp = t->rx.buf; + ret = ph->xops->do_xfer (ph, t); +if (!ret) { + *enabled =le32_get_bits (resp->control,TELEMETRY_ENABLE ); + *mode =le32_get_bits (resp->control,TELEMETRY_MODE ); +if (!is_group) + ti->streaming_mode = *mode ==SCMI_TLM_NOTIFICATION ; + *active_update_interval = +SCMI_TLM_GET_UPDATE_INTERVAL (le32_to_cpu (resp->sampling_rate)); + } + + ph->xops->xfer_put (ph, t); + +return ret; +} + +static int scmi_telemetry_group_config_lookup (struct telemetry_info *ti, +struct scmi_telemetry_group *grp) +{ +return scmi_telemetry_config_lookup (ti, grp->info->grp_id, &grp->enabled, + &grp->active_update_interval, + &grp->current_mode, false); +} + +static void iter_enabled_list_prepare_message (void *message, +unsigned int desc_index, +const void *priv) +{ +struct scmi_msg_telemetry_de_enabled_list *msg = message; + + msg->index =cpu_to_le32 (desc_index); + msg->flags =0 ; +} + +static int iter_enabled_list_update_state (struct scmi_iterator_state *st, +const void *response,void *priv) +{ +const struct scmi_msg_resp_telemetry_de_enabled_list *r = response; + + st->num_returned =le32_get_bits (r->flags,GENMASK (15 ,0 )); + st->num_remaining =le32_get_bits (r->flags,GENMASK (31 ,16 )); + +if (st->rx_len < (sizeof (*r) +sizeof (r->array[0 ]) * st->num_returned)) +return -EINVAL ; + +/* +* total enabled is not declared previously anywhere so we +* assume it's returned+remaining on first call. +*/ +if (!st->max_resources) + st->max_resources = st->num_returned + st->num_remaining; + +return 0 ; +} + +static int +iter_enabled_list_process_response (const struct scmi_protocol_handle *ph, +const void *response, +struct scmi_iterator_state *st,void *priv) +{ +const struct scmi_msg_resp_telemetry_de_enabled_list *r = response; +const struct scmi_enabled_de_desc *desc; +struct telemetry_info *ti = priv; +struct telemetry_de *tde; +u32 de_id; +int ret; + + desc = &r->array[st->loop_idx]; + de_id =le32_to_cpu (desc->id); +if (scmi_telemetry_tde_lookup (ti, de_id)) { +dev_err (ph->dev, +"Found INVALID DE with DUPLICATED ID:0x%08X\n" , de_id); +return -EINVAL ; + } + + tde =scmi_telemetry_tde_get (ti, de_id); +if (IS_ERR (tde)) +return PTR_ERR (tde); + +guard (mutex)(&tde->mtx); + tde->de.info->id = de_id; + tde->de.enabled = true; + tde->de.tstamp_enabled =le32_to_cpu (desc->mode) ==DE_ENABLED_WITH_TSTAMP ; +/* +* Guessing that, if a DE is reported as enabled with timestamp, it has +* to support timestamp: NOTE THAT the timestamp type (tde->ts_type) can +* ONLY be discovered later by DE_DESCRIPTION. +*/ + tde->de.tstamp_support = tde->de.tstamp_enabled; + + ret =scmi_telemetry_tde_register (ti, tde); +if (ret) { +scmi_telemetry_free_tde_put (ti, tde); +return ret; + } + +scmi_telemetry_de_state_update (ti,ENA_STATE ,NULL , true); +if (tde->de.tstamp_enabled) +scmi_telemetry_de_state_update (ti,ENA_TSTAMP ,NULL , true); + +dev_dbg (ph->dev,"Registered new ENABLED DE with ID:0x%08X\n" , + tde->de.info->id); + +return 0 ; +} + +static int scmi_telemetry_enumerate_des_enabled_list (struct telemetry_info *ti) +{ +const struct scmi_protocol_handle *ph = ti->ph; +struct scmi_telemetry_res_info *rinfo; +struct scmi_iterator_ops ops = { + .prepare_message = iter_enabled_list_prepare_message, + .update_state = iter_enabled_list_update_state, + .process_response = iter_enabled_list_process_response, + }; +void *iter; +int ret; + + iter = ph->hops->iter_response_init (ph, &ops,0 , +TELEMETRY_DE_ENABLED_LIST , +sizeof (u32) *2 , ti); +if (IS_ERR (iter)) +return PTR_ERR (iter); + + ret = ph->hops->iter_response_run (iter); +if (ret) +return ret; + +/* Get rinfo as it is...without triggering an enumeration */ + rinfo =__scmi_telemetry_resources_get (ti); + +dev_info (ti->ph->dev,"Found %u enabled DEs.\n" , rinfo->num_des); + +return 0 ; +} + +static int scmi_telemetry_initial_state_lookup (struct telemetry_info *ti) +{ +struct device *dev = ti->ph->dev; +int ret; + +/* Discover the initial state ONLY for DEs, ignoring groups */ + ret =scmi_telemetry_config_lookup (ti,SCMI_TLM_GRP_INVALID , + &ti->info.enabled, + &ti->info.active_update_interval, + &ti->info.current_mode, true); +if (ret) +return ret; + +if (!ti->info.enabled) +return 0 ; + +/* +* When Telemetry is found already enabled on the platform, proceed with +* passive discovery using DE_ENABLED_LIST and TDCF scanning: note that +* this CAN only discover DEs exposed via SHMTIs. +* FastChannel DEs need a proper DE_DESCRIPTION enumeration, while, even +* though incoming Notifications could be used for passive discovery too, +* it would carry a considerable risk of assimilating trash as DEs. +*/ +dev_info (dev, +"Telemetry found enabled with update interval %lux10^%d mode:%u\n" , +SCMI_TLM_GET_UPDATE_INTERVAL_SECS (ti->info.active_update_interval), +SCMI_TLM_GET_UPDATE_INTERVAL_EXP (ti->info.active_update_interval), + ti->info.current_mode); +/* +* Query enabled DEs list: collect states. It will include DEs from any +* interface. Enabled groups still NOT enumerated. +*/ + ret =scmi_telemetry_enumerate_des_enabled_list (ti); +if (ret) { +dev_err (dev,FW_BUG "Cannot query enabled DE list. Abort.\n" ); +return ret; + } + +/* Discover DEs on SHMTis: collect states/offsets/values */ +for (int id =0 ; id < ti->num_shmti; id++) { + ret =scmi_telemetry_shmti_scan (ti, id,SCAN_UPDATE ); +if (ret) +dev_warn (dev, +"Failed discovery-scan of SHMTI ID:%d - ret:%d\n" , + id, ret); + } + +return 0 ; +} +static int scmi_telemetry_de_groups_init (struct telemetry_info *ti) {
@@ -1163,8 +1417,13 @@ scmi_telemetry_de_groups_init(struct telemetry_info *ti) } } -/* Expose all groups once all fully initialized */ - rinfo->num_groups = num_groups; +for (int i =0 ; i < ti->info.base.num_groups; i++) { +/* Expose groups once successfully initialized */ +if (!scmi_telemetry_group_config_lookup (ti, &rinfo->grps[i])) + rinfo->num_groups++; +else +trace_scmi_tlm_access (0 ,"GROUP_LOOKUP_FAILED" ,0 ,0 ); + }return 0 ; }
@@ -2335,9 +2594,17 @@ static void scmi_telemetry_tdcf_data_parse(struct telemetry_info *ti,trace_scmi_tlm_access (de_id,"DE_LOCATION_UPDATED" ,0 ,0 ); +/* Just in case it was passively discovered boot-on */ +if (IS_TDE_TS_NONE (tde)) { +if (use_blk_ts) + tde->ts_type =TSTAMP_BLK ; +else if (LINE_TS_VALID_RAW (meta)) + tde->ts_type =TSTAMP_LINE ; + } +dev_dbg (ti->ph->dev, -"TDCF-updated DE_ID:0x%08X - shmti:%pK offset:%u\n" , - tde->de.info->id, tde->base, tde->offset); +"TDCF-updated DE_ID:0x%08X - shmti:%pK offset:%u ts_type:%d\n" , + tde->de.info->id, tde->base, tde->offset, tde->ts_type); }/*
@@ -4643,6 +4910,12 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph)return ret; } + ret =scmi_telemetry_initial_state_lookup (ti); +if (ret) { +dev_err (dev,FW_BUG "Cannot retrieve initial state. Abort.\n" ); +return ret; + } + ti->info.base.version = ph->version; ret = ph->set_priv (ph, ti);
Signature · 1 line
2.54.0
Re: [PATCH v15 21/35] firmware: arm_scmi: Add Telemetry boot-on support
Quoted 6 lines · On Mon, 9 Nov 2026 09:12:07 +0000 Cristian Marussi <[email protected]>…
Add the initialization and discovery logic needed to detect when the platform SCMI server is configured with telemetry enabled at boot and perform all the needed resource enumerations to keep the kernel telemetry subsystem state aligned with the platform boot-on configurations.
Signed-off-by Cristian Marussi <[email protected]>
Minor things inline.
Whilst reading this I'm kind of on the lookout for things that maybe could be pushed out to a future patch set just to cut down on the rather large reviews needed on current set.
I'll address that separately when I get to end of the series, but maybe this is one such thing that could wait.
Jonathan
Quoted 32 lines
--- drivers/firmware/arm_scmi/telemetry.c | 287 +++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 7 deletions(-)drivers/firmware/arm_scmi/telemetry.c +23
-0+ +static int +iter_enabled_list_process_response (const struct scmi_protocol_handle *ph, +const void *response, +struct scmi_iterator_state *st,void *priv) +{ +const struct scmi_msg_resp_telemetry_de_enabled_list *r = response; +const struct scmi_enabled_de_desc *desc; +struct telemetry_info *ti = priv; +struct telemetry_de *tde; +u32 de_id; +int ret; + + desc = &r->array[st->loop_idx]; + de_id =le32_to_cpu (desc->id); +if (scmi_telemetry_tde_lookup (ti, de_id)) { +dev_err (ph->dev, +"Found INVALID DE with DUPLICATED ID:0x%08X\n" , de_id); +return -EINVAL ; + } + + tde =scmi_telemetry_tde_get (ti, de_id); +if (IS_ERR (tde))
drivers/firmware/arm_scmi/telemetry.c static int iter_de_descr_process_response(const struct scmi_protocol_handle *ph, · line 1233
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+return PTR_ERR (tde); + +guard (mutex)(&tde->mtx); + tde->de.info->id = de_id; + tde->de.enabled = true; + tde->de.tstamp_enabled =le32_to_cpu (desc->mode) ==DE_ENABLED_WITH_TSTAMP ; +/* +* Guessing that, if a DE is reported as enabled with timestamp, it has +* to support timestamp: NOTE THAT the timestamp type (tde->ts_type) can +* ONLY be discovered later by DE_DESCRIPTION.
Extra pace before DE_DESCRIPTION that probably wants to go.
Quoted 24 lines
drivers/firmware/arm_scmi/telemetry.c +18
-0+*/ + tde->de.tstamp_support = tde->de.tstamp_enabled; + + ret =scmi_telemetry_tde_register (ti, tde); +if (ret) { +scmi_telemetry_free_tde_put (ti, tde); +return ret; + } + +scmi_telemetry_de_state_update (ti,ENA_STATE ,NULL , true); +if (tde->de.tstamp_enabled) +scmi_telemetry_de_state_update (ti,ENA_TSTAMP ,NULL , true); + +dev_dbg (ph->dev,"Registered new ENABLED DE with ID:0x%08X\n" , + tde->de.info->id); + +return 0 ; +}static int scmi_telemetry_de_groups_init (struct telemetry_info *ti) {
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_de_groups_init(struct telemetry_info *ti) · line 1167
Quoted 5 lines
drivers/firmware/arm_scmi/telemetry.c +0
-2@@ -1163,8 +1417,13 @@ scmi_telemetry_de_groups_init(struct telemetry_info *ti) } } -/* Expose all groups once all fully initialized */ - rinfo->num_groups = num_groups;
This reflects on patch 11, but using num_groups here was rather inconsistent with all the use of ti->info.base.num_groups.
They are equal as it was a santity check earlier but I'd make a change back then to use the ti->info.base.num_groups to assign this. Then more obvious here that you have the write change to this loop.
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +7
-0+for (int i =0 ; i < ti->info.base.num_groups; i++) { +/* Expose groups once successfully initialized */ +if (!scmi_telemetry_group_config_lookup (ti, &rinfo->grps[i])) + rinfo->num_groups++; +else +trace_scmi_tlm_access (0 ,"GROUP_LOOKUP_FAILED" ,0 ,0 ); + }return 0 ; }
Cristian Marussi
reviewerPATCH v15 22/35 firmware: arm-scmi: Add Telemetry generic event support
In reply to Cristian MarussiAdd support to allow SCMI driver users to subscribe/unsubscribe to specific Telemetry abstract (non protocol) events.
This enables SCMI drivers to subscribe to available events by registering an eventfd context that the core SCMI stack will use as a signalling mechanisms. Still no real subscribable events introduced at this point.
---
v12 --> v13
- refacto resources cleanup
v11 --> v12
- dropped eventfd_ctx_put on external ref
v10 --> v11
- move events cleanup into resource_free
v8 --> v9
- add a TLM_EVT_DUMMY placeholder (waiting for more events later on..)
--- drivers/firmware/arm_scmi/telemetry.c | 96 +++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 16 +++++ 2 files changed, 112 insertions(+)
drivers/firmware/arm_scmi/telemetry.c +96 -0
@@ -12,6 +12,7 @@#include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/eventfd.h> #include <linux/hash.h> #include <linux/hashtable.h> #include <linux/jhash.h>
@@ -565,6 +566,11 @@ enum de_state {ENA_MAX }; +struct telemetry_event { +struct eventfd_ctx *ctx; +struct list_head item; +}; +struct telemetry_info {bool streaming_mode;unsigned int num_shmti;
@@ -591,6 +597,9 @@ struct telemetry_info {/* Protect global and per-group configuration data */ struct mutex config_mtx;struct scmi_telemetry_info info; +/* Mutex to protect access to @events */ +struct mutex events_mtx; +struct list_head events[SCMI_TLM_EVT_MAX ];struct notifier_block telemetry_nb;atomic_t rinfo_initializing;struct completion rinfo_initdone;
@@ -618,6 +627,21 @@ struct mutex *lock_for_payld(struct mutex *locks, unsigned long offset)return &locks[hash_long (offset,PAYLD_HASH_BITS )]; } +static int scmi_telemetry_event_signal (struct telemetry_info *ti, +enum scmi_telemetry_event type) +{ +struct telemetry_event *evt; + +if (type >=SCMI_TLM_EVT_MAX ) +return -EINVAL ; + +guard (mutex)(&ti->events_mtx); +list_for_each_entry (evt, &ti->events[type], item) +eventfd_signal (evt->ctx); + +return 0 ; +} +static inline void scmi_telemetry_de_state_update (struct telemetry_info *ti,enum de_state state,bool *current_state,const bool next_state)
@@ -4331,6 +4355,54 @@ static int scmi_telemetry_reset(const struct scmi_protocol_handle *ph)return ret; } +static int scmi_telemetry_event_subscribe (const struct scmi_protocol_handle *ph, +enum scmi_telemetry_event type, +struct eventfd_ctx *ctx) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct telemetry_event *evt; + +if (type >=SCMI_TLM_EVT_MAX ) +return -EINVAL ; + + evt =kzalloc_obj (*evt); +if (!evt) +return -ENOMEM ; + + evt->ctx = ctx; +guard (mutex)(&ti->events_mtx); +list_add (&evt->item, &ti->events[type]); + +trace_scmi_tlm_access (0 ,"TLM_EVT_SUBS" ,0 ,0 ); + +return 0 ; +} + +static int scmi_telemetry_event_unsubscribe (const struct scmi_protocol_handle *ph, +enum scmi_telemetry_event type, +struct eventfd_ctx *ctx) +{ +struct telemetry_info *ti = ph->get_priv (ph); +struct telemetry_event *evt, *n; + +if (type >=SCMI_TLM_EVT_MAX ) +return -EINVAL ; + +guard (mutex)(&ti->events_mtx); +list_for_each_entry_safe (evt, n, &ti->events[type], item) { +if (evt->ctx == ctx) { +list_del (&evt->item); +kfree (evt); + +trace_scmi_tlm_access (0 ,"TLM_EVT_UNSUBS" ,0 ,0 ); + +break ; + } + } + +return 0 ; +} +static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .info_get = scmi_telemetry_info_get, .de_lookup = scmi_telemetry_de_lookup,
@@ -4342,6 +4414,8 @@ static const struct scmi_telemetry_proto_ops tlm_proto_ops = { .des_bulk_read = scmi_telemetry_des_bulk_read, .des_sample_get = scmi_telemetry_des_sample_get, .reset = scmi_telemetry_reset, + .event_subscribe = scmi_telemetry_event_subscribe, + .event_unsubscribe = scmi_telemetry_event_unsubscribe, }; static bool
@@ -4702,10 +4776,28 @@ static void scmi_telemetry_groups_free(struct scmi_telemetry_res_info *rinfo) } } +static void scmi_telemetry_event_cleanup (struct telemetry_info *ti) +{ +guard (mutex)(&ti->events_mtx); +/* Clear any residual events */ +for (int type =0 ; type <SCMI_TLM_EVT_MAX ; type++) { +struct telemetry_event *evt, *n; + +list_for_each_entry_safe (evt, n, &ti->events[type], item) { +dev_warn (ti->ph->dev, +"Found UN-SUBSCRIBED event type %d!\n" , type); +list_del (&evt->item); +kfree (evt); + } + } +} +static void scmi_telemetry_instance_cleanup (void *arg) {struct telemetry_info *ti = arg; +/* Cleanup any still registered event */ +scmi_telemetry_event_cleanup (ti);scmi_telemetry_groups_free (ti->rinfo);scmi_telemetry_tdes_cleanup (ti);scmi_telemetry_uuids_cleanup (ti);
@@ -4858,6 +4950,10 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)return ret; } +for (int i =0 ; i <SCMI_TLM_EVT_MAX ; i++) +INIT_LIST_HEAD (&ti->events[i]); +mutex_init (&ti->events_mtx); +atomic_set (&ti->des_enabled[ENA_STATE ],0 );atomic_set (&ti->des_enabled[ENA_TSTAMP ],0 );mutex_init (&ti->des_enabled_mtx);
include/linux/scmi_protocol.h +16 -0
@@ -12,6 +12,7 @@#include <linux/bitops.h> #include <linux/device-id/scmi.h> #include <linux/device.h> +#include <linux/eventfd.h> #include <linux/notifier.h> #include <linux/types.h> #include <linux/uuid.h>
@@ -1001,6 +1002,11 @@ struct scmi_telemetry_de_sample {unsigned long long val; }; +enum scmi_telemetry_event { +SCMI_TLM_EVT_DUMMY , +SCMI_TLM_EVT_MAX , +}; +/** * struct scmi_telemetry_proto_ops - represents the various operations provided * by SCMI Telemetry Protocol
@@ -1025,6 +1031,10 @@ struct scmi_telemetry_de_sample {* This causes an immediate update platform-side of all the * enabled DEs. * @reset: reset configuration and telemetry data. +* @event_subscribe: subscribe to the specified event @type using the provided +* @eventfd_ctx. +* @event_unsubscribe: unsubscribe to the specified event @type the previously +* registered @eventfd_ctx. */ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get)
@@ -1052,6 +1062,12 @@ struct scmi_telemetry_proto_ops {int __must_check (*des_sample_get)(const struct scmi_protocol_handle *ph,int grp_id,unsigned int *num_samples,struct scmi_telemetry_de_sample *samples); +int (*event_subscribe)(const struct scmi_protocol_handle *ph, +enum scmi_telemetry_event type, +struct eventfd_ctx *ctx); +int (*event_unsubscribe)(const struct scmi_protocol_handle *ph, +enum scmi_telemetry_event type, +struct eventfd_ctx *ctx);int (*reset)(const struct scmi_protocol_handle *ph); };
Signature · 1 line
2.54.0
Re: [PATCH v15 22/35] firmware: arm-scmi: Add Telemetry generic event support
Quoted 2 lines · On Mon, 9 Nov 2026 09:12:08 +0000 Cristian Marussi <[email protected]>…
Add support to allow SCMI driver users to subscribe/unsubscribe to specific Telemetry abstract (non protocol) events.
Bonus space before abstract.
Quoted 4 lines
This enables SCMI drivers to subscribe to available events by registering an eventfd context that the core SCMI stack will use as a signalling mechanisms. Still no real subscribable events introduced at this point.
Adding a fake event isn't that helpful. Just combine with the next patch.
Cristian Marussi
reviewerPATCH v15 23/35 firmware: arm_scmi: Add Telemetry generation counter event
In reply to Cristian MarussiAdd a per-instance generation counter to track configuration changes which uses the common telemetry events framework.
---
v14 --> v15
- do NOT reset generation on RESET
v12 --> v13
- update generation counter on do_xfer succcess even if failing to process response since the change hass been applied: this avoids state misalignment
v11 -->v12
- reviewed generation counter update logic
v6 --> v7
- use new Generic event mechanism based on EventFD instead of WaitQ
v5 --> v6
- add relese semantic to the atomic set of the generation counter
- use new eventfd_ctx common event logic
--- drivers/firmware/arm_scmi/telemetry.c | 46 ++++++++++++++++++++++++++- include/linux/scmi_protocol.h | 6 ++-- 2 files changed, 49 insertions(+), 3 deletions(-)
drivers/firmware/arm_scmi/telemetry.c +45 -1
@@ -18,6 +18,7 @@#include <linux/jhash.h> #include <linux/io.h> #include <linux/limits.h> +#include <linux/lockdep.h> #include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h>
@@ -30,6 +31,7 @@#include <linux/time.h> #include <linux/uuid.h> #include <linux/xarray.h> +#include <linux/wait.h> #include "errors.h" #include "protocols.h"
@@ -40,6 +42,7 @@/* Updated only after ALL the mandatory features for that version are merged */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 +#define SCMI_TLM_GENERATION_ONE (SCMI_TLM_GENERATION_INVALID + 1U)#define SCMI_TLM_TDCF_MAX_RETRIES 15#define SCMI_TLM_TDCF_RETRY_GAP_US 1234
@@ -821,6 +824,32 @@ scmi_telemetry_tde_cache_lookup(struct telemetry_de *tde,return 0 ; } +static inline void __scmi_telemetry_generation_set (struct telemetry_info *ti, +unsigned int new) +{ +int ret; + +atomic_set_release (&ti->info.generation, new); + + ret =scmi_telemetry_event_signal (ti,SCMI_TLM_EVT_GENERATION ); +if (ret) +dev_warn_once (ti->ph->dev, +"Could NOT signal telemetry event %d\n" , +SCMI_TLM_EVT_GENERATION ); +} + +static inline void scmi_telemetry_generation_update (struct telemetry_info *ti) +{ +unsigned int next; + +lockdep_assert_held (&ti->config_mtx); + +/* Wrap around skipping invalid generation 0 */ + next = (atomic_read (&ti->info.generation) +1 ) ?:SCMI_TLM_GENERATION_ONE ; + +__scmi_telemetry_generation_set (ti, next); +} +struct scmi_tlm_de_priv {struct telemetry_info *ti;void *next;
@@ -3386,6 +3415,9 @@ scmi_telemetry_remote_state_update_needed(struct telemetry_info *ti, is_group ? obj :NULL , !is_group ? obj : NULL); +/* A local change can have an impact anyway */ +scmi_telemetry_generation_update (ti); +return false; }
@@ -3495,6 +3527,8 @@ static int scmi_telemetry_tde_state_set(const struct scmi_protocol_handle *ph,dev_err (ph->dev,"Could NOT process DE ID 0x%08X state response - ret:%d\n" , tde->de.info->id, ret); + +scmi_telemetry_generation_update (ti); } ph->xops->xfer_put (ph, t);
@@ -3545,6 +3579,8 @@ static int scmi_telemetry_group_state_set(const struct scmi_protocol_handle *ph,dev_err (ph->dev,"Could NOT process GROUP %u state response - ret:%d\n" , grp->info->grp_id, ret); + +scmi_telemetry_generation_update (ti); } ph->xops->xfer_put (ph, t);
@@ -3705,6 +3741,9 @@ scmi_telemetry_collection_configure(const struct scmi_protocol_handle *ph, ph->xops->xfer_put (ph, t); +if (!ret) +scmi_telemetry_generation_update (ti); +return ret; }
@@ -4347,8 +4386,11 @@ static int scmi_telemetry_reset(const struct scmi_protocol_handle *ph)put_unaligned_le32 (0 , t->tx.buf); ret = ph->xops->do_xfer (ph, t); -if (!ret) +if (!ret) {scmi_telemetry_local_resources_reset (ti); +/* Cumulative generation tick */ +scmi_telemetry_generation_update (ti); + } ph->xops->xfer_put (ph, t);
@@ -4954,6 +4996,8 @@ static int scmi_telemetry_instance_init(struct telemetry_info *ti)INIT_LIST_HEAD (&ti->events[i]);mutex_init (&ti->events_mtx); +/* Generation counter init */ +atomic_set (&ti->info.generation,SCMI_TLM_GENERATION_ONE );atomic_set (&ti->des_enabled[ENA_STATE ],0 );atomic_set (&ti->des_enabled[ENA_TSTAMP ],0 );mutex_init (&ti->des_enabled_mtx);
include/linux/scmi_protocol.h +4 -2
@@ -895,6 +895,7 @@ enum scmi_telemetry_collection { SCMI_TLM_SINGLE_READ, }; +#define SCMI_TLM_GENERATION_INVALID 0U#define SCMI_TLM_GRP_INVALID 0xFFFFFFFFstruct scmi_telemetry_intervals {
@@ -993,6 +994,7 @@ struct scmi_telemetry_info { uuid_t * __rcu *uuids __counted_by_ptr(num_uuids);bool enabled;enum scmi_telemetry_collection current_mode; +atomic_t generation; };struct scmi_telemetry_de_sample {
@@ -1003,7 +1005,7 @@ struct scmi_telemetry_de_sample { };enum scmi_telemetry_event { -SCMI_TLM_EVT_DUMMY , +SCMI_TLM_EVT_GENERATION ,SCMI_TLM_EVT_MAX , };
@@ -1030,11 +1032,11 @@ enum scmi_telemetry_event {* the ones belonging to a specific group when provided. * This causes an immediate update platform-side of all the * enabled DEs. -* @reset: reset configuration and telemetry data. * @event_subscribe: subscribe to the specified event @type using the provided * @eventfd_ctx. * @event_unsubscribe: unsubscribe to the specified event @type the previously * registered @eventfd_ctx. +* @reset: reset configuration and telemetry data. */ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get)
Signature · 1 line
2.54.0
Re: [PATCH v15 23/35] firmware: arm_scmi: Add Telemetry generation counter event
Quoted 2 lines · On Mon, 9 Nov 2026 09:12:09 +0000 Cristian Marussi <[email protected]>…
Add a per-instance generation counter to track configuration changes which uses the common telemetry events framework.
Why? What's the usecase for this?
Needs some more information or I'd leave it for now.
Quoted 41 lines
Signed-off-by Cristian Marussi <[email protected]>---
v14 --> v15
- do NOT reset generation on RESET
v12 --> v13
- update generation counter ondo_xfersucccess even if failing to process response since the change hass been applied: this avoids state misalignment
v11 -->v12
- reviewed generation counter update logic
v6 --> v7
- use new Generic event mechanism based on EventFD instead of WaitQ
v5 --> v6
- add relese semantic to the atomic set of the generation counter
- use neweventfd_ctxcommon event logic--- drivers/firmware/arm_scmi/telemetry.c | 46 ++++++++++++++++++++++++++- include/linux/scmi_protocol.h | 6 ++-- 2 files changed, 49 insertions(+), 3 deletions(-)drivers/firmware/arm_scmi/telemetry.c +0
-0include/linux/scmi_protocol.h +1
-1struct scmi_telemetry_de_sample {@@ -1003,7 +1005,7 @@ struct scmi_telemetry_de_sample { };enum scmi_telemetry_event { -SCMI_TLM_EVT_DUMMY , +SCMI_TLM_EVT_GENERATION ,SCMI_TLM_EVT_MAX , };
include/linux/scmi_protocol.h enum scmi_telemetry_event { · line 1039
Quoted 9 lines
include/linux/scmi_protocol.h +1
-1@@ -1030,11 +1032,11 @@ enum scmi_telemetry_event {* the ones belonging to a specific group when provided. * This causes an immediate update platform-side of all the * enabled DEs. -* @reset: reset configuration and telemetry data. * @event_subscribe: subscribe to the specified event @type using the provided * @eventfd_ctx. * @event_unsubscribe: unsubscribe to the specified event @type the previously * registered @eventfd_ctx. +* @reset: reset configuration and telemetry data.
Unrelated change. Belongs in previous patch.
Quoted 3 lines
include/linux/scmi_protocol.h +0
-0*/ struct scmi_telemetry_proto_ops {const struct scmi_telemetry_info *(*info_get)
Cristian Marussi
reviewerPATCH v15 24/35 firmware: arm_scmi: Add common per-protocol debugfs support
In reply to Cristian MarussiAllow interested SCMI protocols to register their own specific debugfs entries under a common per-instance and per-protocol subtree rooted at /sys/kernel/debug/scmi/<N>/protocols/<PROTO_ID>/
Expose a helper to enable protocol initialization code to get access to such per-protocol/per-instance dentries in order to be able to install their own dedicated debugfs entries.
Per-protocol debugfs support is configurable and default off.
---
v14 --> v15
- fix debugfs_remove_recursive call-site...was too early
v8 --> v9
- avoid NULL deref on DBG cleanup path
v5 --> v6
- bail out of dentry_get helper when SCMI debug susbsytem failed to init
- remove stale SCMI protocol operations
- add a preoper clean up of debugfs when protocol init fails
--- drivers/firmware/arm_scmi/Kconfig | 14 +++++++++ drivers/firmware/arm_scmi/common.h | 2 ++ drivers/firmware/arm_scmi/driver.c | 42 +++++++++++++++++++++++++-- drivers/firmware/arm_scmi/protocols.h | 6 ++++ include/linux/scmi_protocol.h | 1 - 5 files changed, 62 insertions(+), 3 deletions(-)
drivers/firmware/arm_scmi/Kconfig +14 -0
@@ -69,6 +69,20 @@ config ARM_SCMI_DEBUG_COUNTERS such useful debug counters. This can be helpful for debugging and SCMI monitoring. +config ARM_SCMI_DEBUG_PROTOCOLS + bool "Enable SCMI protocols debug" + select ARM_SCMI_NEED_DEBUGFS + depends on DEBUG_FS + default n + help + Enables per-protocol specific debug features, where available. + When provided, such per-protocol debugfs entries are grouped + inside a common a subtree named by the protocol number and rooted + under a per-instance 'protocols' directory. + + Such per-protocol entries subtree structure is freely defined + within the related protocol code. + config ARM_SCMI_QUIRKS bool "Enable SCMI Quirks framework" depends on JUMP_LABEL || COMPILE_TEST
drivers/firmware/arm_scmi/common.h +2 -0
@@ -310,6 +310,7 @@ enum debug_counters {/** * struct scmi_debug_info - Debug common info * @top_dentry: A reference to the top debugfs dentry +* @protos: A reference to the top debugfs protocols subdirectory * @name: Name of this SCMI instance * @type: Type of this SCMI instance * @is_atomic: Flag to state if the transport of this instance is atomic
@@ -317,6 +318,7 @@ enum debug_counters {*/ struct scmi_debug_info {struct dentry *top_dentry; +struct dentry *protos;const char *name;const char *type;bool is_atomic;
drivers/firmware/arm_scmi/driver.c +40 -2
106 lines not on this page · raw message
drivers/firmware/arm_scmi/protocols.h +6 -0
@@ -11,6 +11,7 @@#include <linux/bitfield.h> #include <linux/completion.h> +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/kernel.h>
@@ -293,6 +294,9 @@ struct scmi_fc_info {* gathering FC descriptions from the SCMI platform server. * @fastchannel_db_ring: A common helper to ring a FC doorbell. * @get_max_msg_size: A common helper to get the maximum message size. +* @debugfs_proto_dentry_get: A common helper to get a per-protocol debugfs top +* directory to use as a root. It will be +* recursively removed on protocol de-initialization. */ struct scmi_proto_helpers_ops { int (*extended_name_get)(const struct scmi_protocol_handle *ph,
@@ -316,6 +320,8 @@ struct scmi_proto_helpers_ops { u32 *rate_limit);void (*fastchannel_db_ring)(struct scmi_fc_db_info *db);int (*get_max_msg_size)(const struct scmi_protocol_handle *ph); +struct dentry *(*debugfs_proto_dentry_get) + (const struct scmi_protocol_handle *ph); };/**
include/linux/scmi_protocol.h +0 -1
@@ -1164,7 +1164,6 @@ struct scmi_handle {void (*devm_protocol_put)(struct scmi_device *sdev,u8 proto);bool (*is_transport_atomic)(const struct scmi_handle *handle,unsigned int *atomic_threshold); -const struct scmi_notify_ops *notify_ops; };
Signature · 1 line
2.54.0
Re: [PATCH v15 24/35] firmware: arm_scmi: Add common per-protocol debugfs support
Quoted 33 lines · On Mon, 9 Nov 2026 09:12:10 +0000 Cristian Marussi <[email protected]>…
Allow interested SCMI protocols to register their own specific debugfs entries under a common per-instance and per-protocol subtree rooted at /sys/kernel/debug/scmi/<N>/protocols/<PROTO_ID>/
Expose a helper to enable protocol initialization code to get access to such per-protocol/per-instance dentries in order to be able to install their own dedicated debugfs entries.
Per-protocol debugfs support is configurable and default off.
Signed-off-by Cristian Marussi <[email protected]>---
v14 --> v15
- fixdebugfs_remove_recursivecall-site...was too early
v8 --> v9
- avoid NULL deref on DBG cleanup path
v5 --> v6
- bail out ofdentry_gethelper when SCMI debug susbsytem failed to init
- remove stale SCMI protocol operations
- add a preoper clean up of debugfs when protocol init fails--- drivers/firmware/arm_scmi/Kconfig | 14 +++++++++ drivers/firmware/arm_scmi/common.h | 2 ++ drivers/firmware/arm_scmi/driver.c | 42 +++++++++++++++++++++++++-- drivers/firmware/arm_scmi/protocols.h | 6 ++++ include/linux/scmi_protocol.h | 1 - 5 files changed, 62 insertions(+), 3 deletions(-)drivers/firmware/arm_scmi/Kconfig +0
-0
drivers/firmware/arm_scmi/Kconfig config ARM_SCMI_DEBUG_COUNTERS · line 76
Quoted 8 lines
drivers/firmware/arm_scmi/Kconfig +5
-0@@ -69,6 +69,20 @@ config ARM_SCMI_DEBUG_COUNTERS such useful debug counters. This can be helpful for debugging and SCMI monitoring. +config ARM_SCMI_DEBUG_PROTOCOLS + bool "Enable SCMI protocols debug" + select ARM_SCMI_NEED_DEBUGFS + depends on DEBUG_FS + default n
default of default is n, so no need. By all means add some text to discourage distros from enabling it.
Quoted 21 lines
drivers/firmware/arm_scmi/Kconfig +9
-0+ help + Enables per-protocol specific debug features, where available. + When provided, such per-protocol debugfs entries are grouped + inside a common a subtree named by the protocol number and rooted + under a per-instance 'protocols' directory. + + Such per-protocol entries subtree structure is freely defined + within the related protocol code. +drivers/firmware/arm_scmi/driver.c +0
-0include/linux/scmi_protocol.h +0
-0
include/linux/scmi_protocol.h struct scmi_handle { · line 1167
Quoted 4 lines
include/linux/scmi_protocol.h +0
-1@@ -1164,7 +1164,6 @@ struct scmi_handle {void (*devm_protocol_put)(struct scmi_device *sdev,u8 proto);bool (*is_transport_atomic)(const struct scmi_handle *handle,unsigned int *atomic_threshold); -
Unrelated. Scrub for this stuff before sending new versions. Saves time all round for a minute or two sanity checking.
Quoted 3 lines
include/linux/scmi_protocol.h +0
-0const struct scmi_notify_ops *notify_ops; };
Cristian Marussi
reviewerPATCH v15 25/35 firmware: arm_scmi: Add Telemetry debugfs SHMTI dump support
In reply to Cristian MarussiExpose one debugfs entry for each discovered SHMTI that can be used to dump the related SHMTI in binary form from TBGN up to TEND markers.
No processing is done kernel side, beside using proper accessors for device memory.
---
v14 --> v15
- cleanup local debugfs subtree with debugfs_remove_recursive on remove
v12 --> v13
- get inode lock on time update
v11 --> v12
- fix debugfs shmti dump file to use the SID as name
v8 --> v9
- use kvzalloc/kfree
- use file_update_time to update debugfs file ts
v5 --> v6
- added mutex to protect binary dump buffer during reads (Sashiko)
- using debugfs_create_file_size() (Sashiko)
--- drivers/firmware/arm_scmi/telemetry.c | 103 ++++++++++++++++++++++++++ 1 file changed, 103 insertions(+)
drivers/firmware/arm_scmi/telemetry.c +103 -0
@@ -9,10 +9,12 @@#include <linux/bitfield.h> #include <linux/compiler_types.h> #include <linux/completion.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/eventfd.h> +#include <linux/fs.h> #include <linux/hash.h> #include <linux/hashtable.h> #include <linux/jhash.h>
@@ -463,6 +465,14 @@ struct telemetry_shmti {#define TDCF_OFFS (_b, _p) \ ((unsigned long)((__force void *)(_p) - (__force void *)(_b))) +struct scmi_telemetry_dbg_shmti { +struct telemetry_shmti *shmti; +size_t buf_sz; +void *buf; +/* Protect this descriptor from concurrent readers using it */ +struct mutex mtx; +}; +#define SHMTI_EPLG (s) \ ({ \ struct telemetry_shmti *_s = (s); \
@@ -590,6 +600,7 @@ struct telemetry_info {unsigned int default_blk_ts_rate;const struct scmi_protocol_handle *ph;struct telemetry_shmti *shmti; +struct dentry *shmti_top;struct telemetry_de *tdes;struct xarray xa_des;struct telemetry_uuid *primary_uuid;
@@ -1683,6 +1694,91 @@ scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) ti->info.intervals); } +static int scmi_telemetry_dbg_shmti_open (struct inode *inode,struct file *filp) +{ +struct scmi_telemetry_dbg_shmti *sblob; +struct telemetry_shmti *shmti; + +if (!inode->i_private) +return -ENODEV ; + + shmti = inode->i_private; + sblob =kzalloc_obj (*sblob); +if (!sblob) +return -ENOMEM ; + + sblob->shmti = shmti; + sblob->buf =kvzalloc (shmti->info.len,GFP_KERNEL ); +if (!sblob->buf) { +kfree (sblob); +return -ENOMEM ; + } + +mutex_init (&sblob->mtx); + + filp->private_data = sblob; + +return 0 ; +} + +static ssize_t scmi_telemetry_dbg_shmti_read (struct file *filp,char __user *buf, +size_t count,loff_t *ppos) +{ +struct scmi_telemetry_dbg_shmti *sblob = filp->private_data; + +/* Dump on first read and again after each rewind to start pos */ +guard (mutex)(&sblob->mtx); +if (!sblob->buf_sz || *ppos ==0 ) { +struct inode *ino =file_inode (filp); + +memcpy_fromio (sblob->buf, sblob->shmti->base, + sblob->shmti->info.len); + sblob->buf_sz = sblob->shmti->info.len; +/* update inode timestamps */ +inode_lock (ino); +file_update_time (filp); +inode_unlock (ino); + } + +return simple_read_from_buffer (buf, count, ppos, sblob->buf, sblob->buf_sz); +} + +static int scmi_telemetry_dbg_shmti_release (struct inode *inode,struct file *filp) +{ +struct scmi_telemetry_dbg_shmti *sblob = filp->private_data; + +kvfree (sblob->buf); +kfree (sblob); + +return 0 ; +} + +static const struct file_operations scmi_telemetry_dbg_shmti_fops = { + .open = scmi_telemetry_dbg_shmti_open, + .release = scmi_telemetry_dbg_shmti_release, + .read = scmi_telemetry_dbg_shmti_read, + .llseek = generic_file_llseek, + .owner =THIS_MODULE , +}; + +static void scmi_telemetry_debugfs_initialize (struct telemetry_info *ti) +{ +const struct scmi_protocol_handle *ph = ti->ph; +struct dentry *top; + + top = ph->hops->debugfs_proto_dentry_get (ph); + ti->shmti_top =debugfs_create_dir ("shmtis" , top); + +for (unsigned int i =0 ; i < ti->num_shmti; i++) { +char id[16 ]; + +snprintf (id,16 ,"%u" , ti->shmti[i].info.sid); +debugfs_create_file_size (id,0444 , ti->shmti_top, &ti->shmti[i], + &scmi_telemetry_dbg_shmti_fops, + ti->shmti[i].info.len); + } +} +static int iter_shmti_update_state (struct scmi_iterator_state *st,const void *response,void *priv) {
@@ -4717,6 +4813,10 @@ static void scmi_telemetry_resources_free(struct telemetry_info *ti)/* Ensure rinfo is no more accessible upfront */ smp_store_release (&ti->rinfo,NULL ); +/* Safe removal of SHMTI debugfs tree before freeing underlying data */ +if (IS_ENABLED (CONFIG_ARM_SCMI_DEBUG_PROTOCOLS )) +debugfs_remove_recursive (ti->shmti_top); +kfree (ti->info.shmtis);kfree (ti->shmti);
@@ -5078,6 +5178,9 @@ static int scmi_telemetry_protocol_init(const struct scmi_protocol_handle *ph) } } +if (IS_ENABLED (CONFIG_ARM_SCMI_DEBUG_PROTOCOLS )) +scmi_telemetry_debugfs_initialize (ti); +return 0 ; }
Signature · 1 line
2.54.0
Re: [PATCH v15 25/35] firmware: arm_scmi: Add Telemetry debugfs SHMTI dump support
Quoted 7 lines · On Mon, 9 Nov 2026 09:12:11 +0000 Cristian Marussi <[email protected]>…
Expose one debugfs entry for each discovered SHMTI that can be used to dump the related SHMTI in binary form from TBGN up to TEND markers.
No processing is done kernel side, beside using proper accessors for device memory.
Signed-off-by Cristian Marussi <[email protected]>
Seems fine, one trivial inline. I find it hard to care about this particular option though.
Quoted 13 lines
--- drivers/firmware/arm_scmi/telemetry.c | 103 ++++++++++++++++++++++++++ 1 file changed, 103 insertions(+)drivers/firmware/arm_scmi/telemetry.c +0
-0@@ -1683,6 +1694,91 @@ scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) ti->info.intervals); }
drivers/firmware/arm_scmi/telemetry.c scmi_telemetry_enumerate_common_intervals(struct telemetry_info *ti) · line 1706
Quoted 10 lines
drivers/firmware/arm_scmi/telemetry.c +10
-0+static int scmi_telemetry_dbg_shmti_open (struct inode *inode,struct file *filp) +{ +struct scmi_telemetry_dbg_shmti *sblob; +struct telemetry_shmti *shmti; + +if (!inode->i_private) +return -ENODEV ; + + shmti = inode->i_private; + sblob =kzalloc_obj (*sblob);
Use some __free magic then appropriate no_free_ptr() after it can't fail any more.
Trivial but might as well.
Quoted 17 lines
drivers/firmware/arm_scmi/telemetry.c +16
-0+if (!sblob) +return -ENOMEM ; + + sblob->shmti = shmti; + sblob->buf =kvzalloc (shmti->info.len,GFP_KERNEL ); +if (!sblob->buf) { +kfree (sblob); +return -ENOMEM ; + } + +mutex_init (&sblob->mtx); + + filp->private_data = sblob; + +return 0 ; +}
Cristian Marussi
reviewerPATCH v15 26/35 firmware: arm_scmi: Add Telemetry debugfs ABI documentation
In reply to Cristian MarussiAdd description of the debugfs SCMI Telemetry protocol ABI.
---
v5 --> v6
- fixed typos
--- Documentation/ABI/testing/debugfs-scmi | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
Documentation/ABI/testing/debugfs-scmi +22 -0
@@ -68,3 +68,25 @@ Description: Max number of concurrently allowed in-flight SCMI messages for the currently configured SCMI transport for instance <n> on the RX channels. Users: Debugging, any userspace test suite + +What: /sys/kernel/debug/scmi/<n>/protocols/0x<m>/ +Date: Nov 2026 +KernelVersion: 7.3 +Contact: [email protected] +Description: A subdirectory grouping debug entries related to protocol <m> + for instance <n>. Each protocol owns and defines the subtree + of entries rooted under this directory. +Users: Debugging, any userspace test suite + +What: /sys/kernel/debug/scmi/<n>/protocols/0x1B/shmtis/<m> +Date: Nov 2026 +KernelVersion: 7.3 +Contact: [email protected] +Description: A set of RO files exposed by the Telemetry protocol (0x1B) in + order to dump the latest snapshot of each SHMTI memory area in + binary format: each file is named by its SHMTI id. + File is seekable and a seek to position zero on an open file + causes the SHMTI snapshot to be refreshed; file timestamps are + updated after each snapshot. + This directory reports SHMTIs for instance <n>. +Users: Debugging, any userspace test suite
Signature · 1 line
2.54.0
Re: [PATCH v15 26/35] firmware: arm_scmi: Add Telemetry debugfs ABI documentation
Quoted 15 lines · On Mon, 9 Nov 2026 09:12:12 +0000 Cristian Marussi <[email protected]>…
Add description of the debugfs SCMI Telemetry protocol ABI.
Signed-off-by Cristian Marussi <[email protected]>---
v5 --> v6
- fixed typos--- Documentation/ABI/testing/debugfs-scmi | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)Documentation/ABI/testing/debugfs-scmi +0
-0
Documentation/ABI/testing/debugfs-scmi Description: Max number of concurrently allowed in-flight SCMI messages for · line 74
Quoted 7 lines
Documentation/ABI/testing/debugfs-scmi +4
-0@@ -68,3 +68,25 @@ Description: Max number of concurrently allowed in-flight SCMI messages for the currently configured SCMI transport for instance <n> on the RX channels. Users: Debugging, any userspace test suite + +What: /sys/kernel/debug/scmi/<n>/protocols/0x<m>/ +Date: Nov 2026 +KernelVersion: 7.3
Overly optimistic use of time travel.
Quoted 18 lines
Documentation/ABI/testing/debugfs-scmi +18
-0+Contact: [email protected] +Description: A subdirectory grouping debug entries related to protocol <m> + for instance <n>. Each protocol owns and defines the subtree + of entries rooted under this directory. +Users: Debugging, any userspace test suite + +What: /sys/kernel/debug/scmi/<n>/protocols/0x1B/shmtis/<m> +Date: Nov 2026 +KernelVersion: 7.3 +Contact: [email protected] +Description: A set of RO files exposed by the Telemetry protocol (0x1B) in + order to dump the latest snapshot of each SHMTI memory area in + binary format: each file is named by its SHMTI id. + File is seekable and a seek to position zero on an open file + causes the SHMTI snapshot to be refreshed; file timestamps are + updated after each snapshot. + This directory reports SHMTIs for instance <n>. +Users: Debugging, any userspace test suite
Cristian Marussi
reviewerPATCH v15 27/35 firmware: arm_scmi: Expose per-instance identifier
In reply to Cristian MarussiWhen multiple SCMI instances are configured, the SCMI stack probes and it is initialized multiple times: one core stack for each defined instance.
Each istance is simply identified internally with a number and such number is already exposed to the user to name the root of the per-instance debugfs subtree, if enabled.
Expose such unique identifier to the SCMI drivers so that they can use that same identifier when in need to name their per-instance resources.
---
v7 --> v8
- fixed Doxygen 'non-negative' integer
--- drivers/firmware/arm_scmi/driver.c | 1 + include/linux/scmi_protocol.h | 5 +++++ 2 files changed, 6 insertions(+)
drivers/firmware/arm_scmi/driver.c +1 -0
@@ -3483,6 +3483,7 @@ static int scmi_probe(struct platform_device *pdev)idr_init (&info->rx_idr); handle = &info->handle; + handle->id = info->id; handle->dev = info->dev; handle->version = &info->version; handle->devm_protocol_acquire = scmi_devm_protocol_acquire;
include/linux/scmi_protocol.h +5 -0
@@ -1133,6 +1133,10 @@ struct scmi_notify_ops {/** * struct scmi_handle - Handle returned to ARM SCMI clients for usage. * +* @id: A unique non-negative natural integer identifying the SCMI Instance +* associated with this handle to be used across all drivers for +* naming purposes: same identifier used internally as the root for +* the debugfs per-instance tree. * @dev: pointer to the SCMI device * @version: pointer to the structure containing SCMI version information * @devm_protocol_acquire: devres managed method to get hold of a protocol,
@@ -1153,6 +1157,7 @@ struct scmi_notify_ops {* @notify_ops: pointer to set of notifications related operations */ struct scmi_handle { +unsigned int id;struct device *dev;struct scmi_base_info *version;
Signature · 1 line
2.54.0
Re: [PATCH v15 27/35] firmware: arm_scmi: Expose per-instance identifier
Quoted 11 lines · On Mon, 9 Nov 2026 09:12:13 +0000 Cristian Marussi <[email protected]>…
When multiple SCMI instances are configured, the SCMI stack probes and it is initialized multiple times: one core stack for each defined instance.
Each istance is simply identified internally with a number and such number is already exposed to the user to name the root of the per-instance debugfs subtree, if enabled.
Expose such unique identifier to the SCMI drivers so that they can use that same identifier when in need to name their per-instance resources.
Signed-off-by Cristian Marussi <[email protected]>
Cristian Marussi
reviewerPATCH v15 28/35 firmware: arm_scmi: Add un-managed methods to get/put protocols operations
In reply to Cristian MarussiAdd a couple of simple common methods to be able to manually get and put protocol operations to address situation in which devres managed methods cannot be used.
---
v10 --> v11
- fixed a few comments typos
--- drivers/firmware/arm_scmi/driver.c | 39 ++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 7 ++++++ 2 files changed, 46 insertions(+)
drivers/firmware/arm_scmi/driver.c +39 -0
@@ -2641,6 +2641,31 @@ scmi_devm_protocol_get(struct scmi_device *sdev, u8 protocol_id,return pi->proto->ops; } +/** +* scmi_protocol_operations_get - UnManaged get protocol operations +* +* @handle: A reference to the SCMI platform instance. +* @protocol_id: The protocol being acquired. +* @ph: A pointer reference used to pass back the associated protocol handle. +*/ +static const void __must_check * +scmi_protocol_operations_get (const struct scmi_handle *handle,u8 protocol_id, +struct scmi_protocol_handle **ph) +{ +struct scmi_protocol_instance *pi; + +if (!ph) +return ERR_PTR (-EINVAL ); + + pi =scmi_get_protocol_instance (handle, protocol_id); +if (IS_ERR (pi)) +return pi; + + *ph = &pi->ph; + +return pi->proto->ops; +} +/** * scmi_devm_protocol_acquire - Devres managed helper to get hold of a protocol * @sdev: A reference to an scmi_device whose embedded struct device is to
@@ -2697,6 +2722,18 @@ static void scmi_devm_protocol_put(struct scmi_device *sdev, u8 protocol_id)WARN_ON (ret); } +/** +* scmi_protocol_operations_put - UnManaged put protocol operations +* +* @handle: A reference to the SCMI platform instance. +* @protocol_id: The protocol being released. +*/ +static void scmi_protocol_operations_put (const struct scmi_handle *handle, +u8 protocol_id) +{ +scmi_protocol_release (handle, protocol_id); +} +/** * scmi_is_transport_atomic - Method to check if underlying transport for an * SCMI instance is configured as atomic.
@@ -3489,6 +3526,8 @@ static int scmi_probe(struct platform_device *pdev) handle->devm_protocol_acquire = scmi_devm_protocol_acquire; handle->devm_protocol_get = scmi_devm_protocol_get; handle->devm_protocol_put = scmi_devm_protocol_put; + handle->protocol_get = scmi_protocol_operations_get; + handle->protocol_put = scmi_protocol_operations_put; handle->is_transport_atomic = scmi_is_transport_atomic;/* Setup all channels described in the fwnode at first */
include/linux/scmi_protocol.h +7 -0
@@ -1145,6 +1145,9 @@ struct scmi_notify_ops {* @devm_protocol_get: devres managed method to acquire a protocol and get specific * operations and a dedicated protocol handler * @devm_protocol_put: devres managed method to release a protocol +* @protocol_get: unmanaged method to acquire a protocol and get specific +* operations and a dedicated protocol handler +* @protocol_put: unmanaged method to release a protocol * @is_transport_atomic: method to check if the underlying transport for this * instance handle is configured to support atomic * transactions for commands.
@@ -1167,6 +1170,10 @@ struct scmi_handle { (*devm_protocol_get)(struct scmi_device *sdev, u8 proto, struct scmi_protocol_handle **ph);void (*devm_protocol_put)(struct scmi_device *sdev,u8 proto); +const void __must_check * + (*protocol_get)(const struct scmi_handle *handle,u8 proto, +struct scmi_protocol_handle **ph); +void (*protocol_put)(const struct scmi_handle *handle,u8 proto);bool (*is_transport_atomic)(const struct scmi_handle *handle,unsigned int *atomic_threshold);const struct scmi_notify_ops *notify_ops;
Signature · 1 line
2.54.0
Re: [PATCH v15 28/35] firmware: arm_scmi: Add un-managed methods to get/put protocols operations
Quoted 11 lines · On Mon, 9 Nov 2026 09:12:14 +0000 Cristian Marussi <[email protected]>…
Add a couple of simple common methods to be able to manually get and put protocol operations to address situation in which devres managed methods cannot be used.
Signed-off-by Cristian Marussi <[email protected]>/**
*scmi_devm_protocol_acquire- Devres managed helper to get hold of a protocol
* @sdev: A reference to anscmi_devicewhose embedded struct device is to
drivers/firmware/arm_scmi/driver.c static void scmi_devm_protocol_put(struct scmi_device *sdev, u8 protocol_id) · line 2726
Quoted 5 lines
drivers/firmware/arm_scmi/driver.c +2
-0@@ -2697,6 +2722,18 @@ static void scmi_devm_protocol_put(struct scmi_device *sdev, u8 protocol_id)WARN_ON (ret); } +/** +* scmi_protocol_operations_put - UnManaged put protocol operations
Bonus space after -
Quoted 10 lines
drivers/firmware/arm_scmi/driver.c +10
-0+* +* @handle: A reference to the SCMI platform instance. +* @protocol_id: The protocol being released. +*/ +static void scmi_protocol_operations_put (const struct scmi_handle *handle, +u8 protocol_id) +{ +scmi_protocol_release (handle, protocol_id); +} +
Re: [PATCH v15 28/35] firmware: arm_scmi: Add un-managed methods to get/put protocols operations
In reply to Cristian MarussiQuoted 5 lines · On Mon, 9 Nov 2026 09:12:14 +0000 Cristian Marussi <[email protected]>…
Add a couple of simple common methods to be able to manually get and put protocol operations to address situation in which devres managed methods cannot be used.
Signed-off-by Cristian Marussi <[email protected]>
I was kind of assuming you actually use this. Where does that happen as it's not in the obvious place (patch 30).
Jonathan
Cristian Marussi
reviewerPATCH v15 29/35 uapi: Add ARM SCMI Telemetry definitions
In reply to Cristian MarussiAdd a number of structures and ioctls definitions used by the ARM SCMI Telemetry protocol.
---
v14 --> v15
- a couple of clarification in doxygen
v11 --> v12
- fixed documentation around event lifetime cycle to align with the new implemented logic
v10 --> v11
- typos fixing and comments fixup
- more docs on BATCH IOCTLs and structures
- more docs on SHMTI_LIST
- more docs on EVENT_REGISTER
- more docs on GET_INTRVS upper bounds and zero-query
- more docs on BULK/SINGLE upper bounds and zero-query
v8 --> v9
- Simplified ABI related to _LIST IOCTL, substituting all custom container structures in favour of the usage of scmi_tlm_batch, i.e. dropping:
- scmi_tlm_des_list
- scmi_tlm_uuid_list
- scmi_tlm_grps_list
- scmi_tlm_shmtis_list
- Updated related _LIST documentation
v7 --> v8
- Fixes for some Sashiko issues on v7
- fix typos and wrong struct reference in SCMI_TLM_BATCH_READ
- move a couple of IOCTL to be _IOW
- add a few more comments on usage and behaviour of BATCH ops
v6 --> v7
- added per-DE UUID/SID/OFFS
- IOCTL EVENTS: generation counter
- generic BATCH support (scmi_tlm_batch)
- BATCH READ with perf-DE status
- BATCH DE GET/SET CONFIG
- added reserved fields for grow
- added more ABI features flags
- better Doxygen desciption for IN/OUT params
v5 --> v6
- Fixed interval exp to be __s32 as documented
- Using a more unique name for IOCTL MAGIC define
- Renamed a few IOCTLs to be consistent
- redefine primary UUID as an array of u8
- add GET_ABI_INFO to gather ABI version and instance info
- add RESET support
- fixed typos in kdoc
v4 --> v5
- Added more structure padding
- Removed frowned upon flexible array in favour of u64 pointers to external userspace provided buffer
- Added UAPI support to access SHMTIs from userspace
- Cleaned up Doxygen
v2 --> v3
- Change tstamp_exp tp ts_rate
- renamed num -> num_intervals in scmi_tlm_interval
- added padding in scmi_tlm_de_sample to avoid packing issues on 32bit
v1 --> v2
- Added proper __counted_by marks
- Fixed a few dox comments
- Renamed reserved[] fields to pad[]
--- MAINTAINERS | 1 + include/uapi/linux/scmi.h | 531 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 532 insertions(+) create mode 100644 include/uapi/linux/scmi.h
MAINTAINERS +1 -0
@@ -27341,6 +27341,7 @@ F: drivers/reset/reset-scmi.c F: include/linux/device-id/scmi.h F: include/linux/sc[mp]i_protocol.h F: include/trace/events/scmi.h +F: include/uapi/linux/scmi.h F: include/uapi/linux/virtio_scmi.h SYSTEM CONTROL MANAGEMENT INTERFACE (SCMI) i.MX Extension Message Protocol drivers
include/uapi/linux/scmi.h new file +531 -0
531 lines not on this page · raw message
Signature · 1 line
2.54.0
Re: [PATCH v15 29/35] uapi: Add ARM SCMI Telemetry definitions
Quoted 36 lines · On Mon, 9 Nov 2026 09:12:15 +0000 Cristian Marussi <[email protected]>…
Add a number of structures and ioctls definitions used by the ARM SCMI Telemetry protocol.
Signed-off-by Cristian Marussi <[email protected]>---
---
include/uapi/linux/scmi.h new file +21
-0@@ -0,0 +1,531 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* +* Copyright (C) 2026 ARM Ltd. +*/ +#ifndef _UAPI_LINUX_SCMI_H +#define _UAPI_LINUX_SCMI_H + +/* +* Userspace interface SCMI Telemetry +*/ + +#include <linux/ioctl.h> +#include <linux/types.h> + +#define SCMI_TLM_ABI_VERSION_V1 1 +#define SCMI_TLM_CURRENT_ABI_VERSION SCMI_TLM_ABI_VERSION_V1 +#define SCMI_TLM_DE_IMPL_UUID_SZ 16 + +#define SCMI_TLM_SHMTI_ID_INVALID 0xFFFFFFFF + +/**
include/uapi/linux/scmi.h line 31
Quoted 10 lines
include/uapi/linux/scmi.h new file +10
-0+* struct scmi_tlm_abi_info - Basic information on the ABI/instance +* +* SCMI Telemetry characteristics like number of DEs, Groups, Intervals and +* SHMTIs are statically determined by the platform and do not change within +* one boot/session; they are reported here so that they can be used to properly +* size the requests on other IOCTLs, without having to discover dynamically +* those sizes by issuing a zero-sized IOCTL request at first. +* +* @size: sizeof this structure - IN +* @abi_version: ABI Version - OUT
I'm curious what the plan is for ABI versioning. Normally to have any hope of backwards compatibility it needs to be a negotiation between userspace and kernel (and then hardware if appropriate). Is there any route to do that?
include/uapi/linux/scmi.h line 40
Quoted 9 lines
include/uapi/linux/scmi.h new file +9
-0+* @abi_features: ABI capabilities bitmap - OUT +* @primary_de_impl_version: SCMI Telemetry DE implementation revision - OUT +* @num_des: Number of defined DEs - OUT +* @num_groups: Number of defined DEs groups - OUT +* @num_intervals: Number of available update intervals (instance-level) - OUT +* @num_shmtis: Number of discovered SHMTI areas - OUT +* @features: Instance specific SCMI feature-support bitmap - OUT +* @reserved: Some room for future expansion without the need to resize. +* Must be zero.
Why? I'd expect the input size to indicate what userspace supports. As such what use is this reserved padding?
Quoted 32 lines
include/uapi/linux/scmi.h new file +30
-0+* +* Used by: +* RW - SCMI_TLM_GET_ABI_INFO +*/ +struct scmi_tlm_abi_info { +__u32 size; +__u32 abi_version; +__u32 abi_features; +#define SCMI_TLM_ABI_FEAT_RESET (1 << 0) +#define SCMI_TLM_ABI_FEAT_EVENT (1 << 1) +#define SCMI_TLM_ABI_FEAT_UUID_LIST (1 << 2) +#define SCMI_TLM_ABI_FEAT_BATCH_STATE (1 << 3) +#define SCMI_TLM_ABI_FEAT_BATCHED_CFG (1 << 4) +#define SCMI_TLM_ABI_FEAT_DE_TRACKING (1 << 5) +__u8 primary_de_impl_version[SCMI_TLM_DE_IMPL_UUID_SZ ]; +__u32 num_des; +__u32 num_groups; +__u32 num_intervals; +__u32 num_shmtis; +__u32 features; +#define SCMI_TLM_SCMI_SUPPORT_RESET (1 << 0) +#define SCMI_TLM_SCMI_SUPPORT_SINGLE_SAMPLE (1 << 1) +#define SCMI_TLM_SCMI_SUPPORT_GROUP_CONFIG (1 << 2) +#define SCMI_TLM_SCMI_SUPPORT_UPDATE_NOTIFICATION (1 << 3) +__u64 reserved; +}; + +/** +* struct scmi_tlm_config - Whole instance or group configuration +*
include/uapi/linux/scmi.h line 94
Quoted 10 lines
include/uapi/linux/scmi.h new file +10
-0+* @enable: Get/Set Telemetry enabled state for the whole instance or the +* group specified in @grp_id - IN/OUT +* @t_enable: Get/Set timestamp state for the group specified in @grp_id - IN/OUT +* @flags: Bitmask to represent special characteristics - IN +* @pad: Padding fields to enforce alignment +* @grp_id: Identifier of the target group upon which this configuration will +* be applied: ignored if not marked as a group in @flags - IN/OUT +* @active: Get/Set currently active update interval for the whole instance +* or the group specified in @grp_id - IN/OUT +* @reserved: Some room for future expansion. Must be zero.
I'd add a size field for this one too to make it easier to expand. Probably the same applies to all others.
Quoted 16 lines
+ *
+ * Used by:
+ * RW -SCMI_TLM_GET_CFG
+ * WO -SCMI_TLM_SET_CFG
+ */include/uapi/linux/scmi.h new file +11
-0+struct scmi_tlm_config { +__u8 enable; +__u8 t_enable; +__u8 flags; +#define SCMI_TLM_CONFIG_GROUP (1 << 0) +#define SCMI_TLM_CONFIG_FLAGS (SCMI_TLM_CONFIG_GROUP) +#define SCMI_TLM_CONFIG_IS_GROUP (f) ((f) & SCMI_TLM_CONFIG_GROUP) +__u8 pad; +__u32 grp_id; +struct scmi_tlm_update_interval active; +__u64 reserved;
include/uapi/linux/scmi.h line 120
Quoted 10 lines
include/uapi/linux/scmi.h new file +10
-0+}; + +/** +* struct scmi_tlm_intervals - Update intervals descriptor +* +* @grp_id: Identifier of the target group upon which this configuration +* will be applied: ignored if not marked as a group in @flags - IN +* @flags: Bitmask to represent special characteristics. When the interval +* is NOT marked as SCMI_TLM_INTERV_DISCRETE, @intervals will +* contain a triplet: min/max/step - OUT
I'd have a reference to this in the num_intervals text. What happens if the input only allows for two intervals?
Quoted 5 lines
+ * @pad: Padding fields to enforce alignment
+ * @num_intervals: Number of entries of @intervals. On input this represents the+ * size of @intervals, while on output carries the effective + * number of entries filled-in. + * On input this has an upper-bound determined as:
Cristian Marussi
reviewerPATCH v15 30/35 firmware: arm_scmi: Add System Telemetry driver
In reply to Cristian MarussiAdd an SCMI driver to allow enumeration, configuration and data collection of SCMI Telemetry data.
The driver exposes one character device for each found SCMI instance and a set of related IOCTLs can be used to enumerate, configure and collect Telemetry resources.
---
v14 --> v15
- reject config attempts and intervals query for groups that do not support per-group config
- harden/simplify batch helpers
- fix shmti mmap size check to include offset
- drop usage of sdev->dev in device_release
- simplify session_unsubscribe cleanup on exit/unbind
- added missing kref.h include
- expose TLM EVENTS capabilities ONLY IF CONFIG_EVENTFD
- mmap: check/clear also VM_EXEC VM_MAYWRITE VM_MAYEXEC
- reject non zero vm_pgoff
- drop usage of guard(srcu) to avoid races with put_device
v13 --> v14
- avoid double free on probe err path
- grab ctx and check if dead when mmapping an anon_fd
- move away from dead_mtx in favour of dead_srcu and extend srcu'ed section in ioctl handler
v12 --> v13
- pass back E2BIG from copy_struct_from_user to userspace
- remove a few redundant copy_from_user
- fix filedescriptor lifecycle: drop kref in favour of embedded cdev and use cdev_device_add. Rely on devm_protocol_get for proto_ops
- refactor EVENT session unsubscribe and check for aliveness
- use *alloc_objs
v11 --> v12
- added RCU deref access on UUIDs
- dropped needless copy_from_user on states_batch
- fixed double free on anon ids error path
- reworked eventfd handling using per-session tracking and cleanup on chardev last release
- properly free also ti->tsp on error path
- properly acquire/release sdev->dev via get/put_device
v10 --> v11
- make SCMI tlm_<N> devices 664 by default
- add 0-sized query support for all the NON _LIST remaining BATCH ops
- add 0-sized query support on GET_INTRVS
- add 0-sized query support on BULOK/SINGLE reads
v8 --> v9
- Fix some Sashiko reports on v8
- use kvmalloc/kvcalloc for all buffers with user/FW generated size
- add a proper upper bounds checks on IOCTL requests
- make SET_ALL_CFG carry on after a failure (reporting last error anyway)
- properly initialize some possibly unused stack vars
- refactor SHMTIs list processing and ANON fds allocation to avoid leaking fds by premature installing them into process.
- completely rework cdev and scmi_tlm_instance interactions and lifecycle and drop all devres allocation: reject fops on cdev after driver unbind and delay data structures freeing till the last reference has been dropped.
- reworked BATCH helpers to handle ZERO items requests (for IOCTL query)
- reworked all the _LIST IOCTLs to use scmi_tlm_batch as a container structure instead of custom structs
- add support for 0-items query pattern to all _LIST IOCTLs
v7 --> v8
- Fixes for some Sashiko issues on v7
- refactor batch handling into common batch init/fini routines
- use array_size to check for overflows
- explain cleanup logic of eventfd_ctx (xa_for_each)
- add a mutex to protect events_xa
- introduce per-IOCTL check on batch upper bounds
v6 --> v7
- fixed module_init errors paths
- introduced a check on FMODE to filter-out unprivleged writers
- added UUID list IOCTL
- added UUID/SID/OFFS tp GET/SET config IOCTLs
- added EVENT subscribe IOCTL
- modified BATCH READ with per-DE status
- added BATCHed mode for DE_CFG_SET/GET
- fix setting properly discrete interval flag
- new ABI feats flags\
- new SCMI feats flags
- new reserved checks
v5 --> v6
- drop hunam readable .read capabilities on chardev
- fill-up the new UAPI de_implementation version u8 array
- added compat_ioctl support
- fixed initialization of flags in scmi_tlm_to_uapi_base_info
- fix grp_id bounds check in scmi_tlm_grp_info_get_ioctl
- fix booleans
- fix SHMTI get list to loop on input params
- reworked bounds checks into:
- scmi_tlm_intervals_get_ioctl
- scmi_tlm_des_list_get_ioctl
- scmi_tlm_grp_list_get_ioctl
- scmi_tlm_grp_desc_get_ioctl
- renamed some READ ioctl to be consistent
- reject oversized mmap requests
- support new GET_ABI_INFO IOCTL
- support RESET IOCTL
- add a missing READ_ONCE() to rinfo get
- avoid gaps in the anon fds returned array needed by shmti mmap
- adding missing cdev_del on remove
--- drivers/firmware/arm_scmi/Kconfig | 10 + drivers/firmware/arm_scmi/Makefile | 1 + .../firmware/arm_scmi/scmi_system_telemetry.c | 1788 +++++++++++++++++ 3 files changed, 1799 insertions(+) create mode 100644 drivers/firmware/arm_scmi/scmi_system_telemetry.c
drivers/firmware/arm_scmi/Kconfig +10 -0
@@ -113,4 +113,14 @@ config ARM_SCMI_POWER_CONTROL called scmi_power_control. Note this may be needed early in boot to catch early shutdown/reboot SCMI requests. +config ARM_SCMI_SYSTEM_TELEMETRY + tristate "SCMI System Telemetry driver" + depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) + help + This enables SCMI Systemn Telemetry support that allows userspace to + retrieve ARM Telemetry data made available via SCMI. + + This driver can also be built as a module. If so, the module will be + called scmi_system_telemetry. + endmenu
drivers/firmware/arm_scmi/Makefile +1 -0
@@ -18,3 +18,4 @@ obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o +obj-$(CONFIG_ARM_SCMI_SYSTEM_TELEMETRY) += scmi_system_telemetry.o
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +1788 -0
1,788 lines not on this page · raw message
Signature · 1 line
2.54.0
Re: [PATCH v15 30/35] firmware: arm_scmi: Add System Telemetry driver
Quoted 8 lines · On Mon, 9 Nov 2026 09:12:16 +0000 Cristian Marussi <[email protected]>…
Add an SCMI driver to allow enumeration, configuration and data collection of SCMI Telemetry data.
The driver exposes one character device for each found SCMI instance and a set of related IOCTLs can be used to enumerate, configure and collect Telemetry resources.
Signed-off-by Cristian Marussi <[email protected]>
Some comments inline. I'm not even going to pretend to be alert enough to claim this is a thorough review though.
Jonathan
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 945
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +4
-0+static long scmi_tlm_des_batch_read_ioctl (const struct scmi_tlm_instance *ti, +unsigned long arg) +{ + struct scmi_telemetry_de_sample *samples __free(kvfree) = NULL;
As near as possible to the constructor for an __free declarations.
drivers/firmware/arm_scmi/scmi_system_telemetry.c
Quoted 1 line
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +1
-0+ int ret, *states __free(kvfree) = NULL;
Definitely don't mix __free and non __free declarations. Separate lines!
Quoted 37 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +37
-0+const struct scmi_telemetry_res_info *rinfo; +void __user *uptr = (void __user *)arg; +struct scmi_tlm_batch batch = {}; +size_t batch_sz, states_sz; + + rinfo =scmi_telemetry_res_info_get (ti->tsp); + ret =scmi_tlm_batch_initialize (uptr, &batch,sizeof (*samples), + rinfo->num_des, (void **)&samples, + &batch_sz, &states, &states_sz); +if (ret) +return ret; + +for (int i =0 ; i < batch.num_items; i++) { +int ret; + + ret = ti->tsp->ops->de_data_read (ti->tsp->ph, &samples[i]); +if (ret) { +if (!states) +return ret; + + states[i] = ret; +continue ; + } + } + +/* Return max batch size on zero query */ +if (!batch.num_items) + batch.num_items = rinfo->num_des; + +return scmi_tlm_batch_finalize (uptr, &batch, samples, states, batch_sz, + states_sz); +} + +static struct scmi_tlm_shmti_ctx * +scmi_tlm_shmti_ctx_alloc (struct scmi_tlm_instance *ti, +struct scmi_telemetry_shmti_info *shmti) +{
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 993
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+struct scmi_tlm_shmti_ctx *ctx; + + ctx =kzalloc_obj (*ctx); +if (!ctx) +return ERR_PTR (-ENOMEM ); + + ctx->shmti = shmti; +kref_init (&ctx->ref); + +/* Get hold of the Telemetry instance when returning anon_fds */
This bit of the code isn't returning anon_fds???
Quoted 18 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +17
-0+get_device (&ti->dev); + ctx->ti = ti; + +return ctx; +} + +static int scmi_tlm_shmti_mmap (struct file *filp,struct vm_area_struct *vma) +{ +unsigned long npages, expect, needed, req; +struct scmi_tlm_shmti_ctx *ctx; +phys_addr_t base; +size_t map_sz; +int ret, idx; + +if (vma->vm_pgoff) +return -EINVAL ; +
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1057
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+if (vma->vm_flags & (VM_WRITE |VM_EXEC )) +return -EPERM ; + +vm_flags_clear (vma,VM_MAYWRITE |VM_MAYEXEC ); + +/* Grab a ctx reference early on */ + ctx =scmi_tlm_shmti_ctx_get (filp->private_data); +/* +* Note that any ctx_put potentially release the device and +* cleans the SRCU structure...no guards.
No guards is fine but maybe goto and a common cleanup block?
Quoted 51 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +50
-0+*/ + idx =srcu_read_lock (&ctx->ti->dead_srcu); +if (READ_ONCE (ctx->ti->dead)) { +srcu_read_unlock (&ctx->ti->dead_srcu, idx); +scmi_tlm_shmti_ctx_put (ctx); +return -ENODEV ; + } + + needed = ctx->shmti->len + ctx->shmti->offset; + req = vma->vm_end - vma->vm_start; +if (req < needed) { +srcu_read_unlock (&ctx->ti->dead_srcu, idx); +scmi_tlm_shmti_ctx_put (ctx); +return -EINVAL ; + } + +/* Align & reject oversized requests */ + base = ctx->shmti->phys &PAGE_MASK ; + expect =DIV_ROUND_UP (needed,PAGE_SIZE ); + npages = req >>PAGE_SHIFT ; +if (npages > expect) { +srcu_read_unlock (&ctx->ti->dead_srcu, idx); +scmi_tlm_shmti_ctx_put (ctx); +return -EINVAL ; + } + + map_sz =PAGE_ALIGN (needed); + vma->vm_private_data = ctx; + vma->vm_ops = &scmi_tlm_shmti_vm_ops; +/* Set appropriate caching attributes */ + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot); + +/* Map physical address range into user space */ + ret =vm_iomap_memory (vma, base, map_sz); +if (ret) { +srcu_read_unlock (&ctx->ti->dead_srcu, idx); +scmi_tlm_shmti_ctx_put (ctx); +return ret; + } + +srcu_read_unlock (&ctx->ti->dead_srcu, idx); + +return 0 ; +} +static void +scmi_tlm_allocate_anon_fds (struct scmi_tlm_instance *ti,unsigned int *num_shmtis, +struct scmi_tlm_shmti_info *shinfo, +struct file **filps) +{ +const struct scmi_telemetry_info *info = ti->info;
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1131
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+struct device *dev = &ti->tsp->sdev->dev; +int idx =0 ; + +for (int i =0 ; i < *num_shmtis; i++) { +struct scmi_telemetry_shmti_info *shmti = info->shmtis[i]; +struct scmi_tlm_shmti_ctx *ctx; +struct file *filp; +int fd; + + fd =get_unused_fd_flags (O_CLOEXEC );
Bit marginal, but perhaps some __free magic for this.
Quoted 7 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +7
-0+if (fd <0 ) { +dev_err (dev,"Failed GET_UNUSED_FD for SID: %u\n" , + shmti->sid); +continue ; + } + + ctx =scmi_tlm_shmti_ctx_alloc (ti, shmti);
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1148
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+if (IS_ERR (ctx)) { +dev_err (dev,"Failed CTX_ALLOC for SID: %u\n" , shmti->sid); +put_unused_fd (fd); +continue ; + } + + filp =anon_inode_getfile (SCMI_TLM_DRIVER , &scmi_tlm_shmti_fops, + ctx,O_RDONLY |O_CLOEXEC ); +if (IS_ERR (filp)) { +scmi_tlm_shmti_ctx_put (ctx);
Bit fiddlier but maybe some __free magic for ctx as well.
Quoted 13 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +13
-0+put_unused_fd (fd); +dev_err (dev,"Failed ANON GET_FILE for SID: %u\n" , + shmti->sid); +continue ; + } + + shinfo[idx].sid = shmti->sid; + shinfo[idx].fd = fd; + shinfo[idx].len = shmti->len; + shinfo[idx].offset = shmti->offset; + + filps[idx++] = filp; + }
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1189
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +9
-0+ +/* Return effective initialized items */ + *num_shmtis = idx; +} + +static long +scmi_tlm_shmtis_list_get_ioctl (struct scmi_tlm_instance *ti,unsigned long arg) +{ + struct scmi_tlm_shmti_info *shinfo __free(kvfree) = NULL;
Move down to where it's constructed. cleanup.h talks about this.
Quoted 31 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +31
-0+unsigned int max_shmtis = ti->info->base.num_shmtis; +void __user *uptr = (void __user *)arg; +struct scmi_tlm_batch batch = {}; +size_t batch_sz; +int ret; + + ret =scmi_tlm_batch_initialize (uptr, &batch,sizeof (*shinfo), max_shmtis, + (void **)&shinfo, &batch_sz,NULL ,NULL ); +if (ret) +return ret; + +if (batch.num_items ==0 ) { + batch.num_items = max_shmtis; + +return scmi_tlm_batch_finalize (uptr, &batch, shinfo,NULL , + batch_sz,0 ); + } + + struct file **filps __free(kfree) = kzalloc_objs(*filps, batch.num_items); +if (!filps) +return -ENOMEM ; + +/* Passing the array as a param avoids dancing with uptr->items */ +scmi_tlm_allocate_anon_fds (ti, &batch.num_items, shinfo, filps); + ret =scmi_tlm_batch_finalize (uptr, &batch, shinfo,NULL , batch_sz,0 ); +if (ret) { +scmi_tlm_anon_fds_free (batch.num_items, shinfo, filps); +return ret; + } + +scmi_tlm_anon_fds_install (batch.num_items, shinfo, filps);
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1235
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +9
-0+ +return 0 ; +} + +static long +scmi_tlm_uuid_list_get_ioctl (const struct scmi_tlm_instance *ti, +unsigned long arg) +{ + struct scmi_tlm_uuid *uuids __free(kvfree) = NULL;
Move this down next to where the constructor is. See cleanup.h guidance on why they should be together.
Quoted 85 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +85
-0+void __user *uptr = (void __user *)arg; +struct scmi_tlm_batch batch = {}; +unsigned int num_uuids; +uuid_t **cur_uuids; +size_t batch_sz; +int ret; + +/* +* Note that UUID array can ONLY grow and it has NOT a known upper +* bound, so it does not matter if by the time we grab the cur_uuids +* via rcu_dereference() the array has grown in size and we end up +* here reading a stale/incomplete set of UUIDs, we will simply +* read the new UUIDs next-time: this simplifies allocation. +*/ + num_uuids =smp_load_acquire (&ti->info->num_uuids); + ret =scmi_tlm_batch_initialize (uptr, &batch,sizeof (*uuids), + num_uuids, (void **)&uuids, &batch_sz, +NULL ,NULL ); +if (ret) +return ret; + +rcu_read_lock (); + cur_uuids =rcu_dereference (ti->info->uuids); +for (int j =0 ; j < batch.num_items; j++) +export_uuid (uuids[j].bytes, cur_uuids[j]); +rcu_read_unlock (); + +/* Return max batch size on zero query */ +if (!batch.num_items) + batch.num_items = num_uuids; + +return scmi_tlm_batch_finalize (uptr, &batch, uuids,NULL , batch_sz,0 ); +} + +static int scmi_tlm_event_subscribe (struct scmi_tlm_instance *ti, +struct scmi_tlm_event *evt,void *key) +{ +struct scmi_tlm_events_session *session; +struct scmi_tlm_registered_event *revt; +struct eventfd_ctx *ctx; +u32 cookie; +int ret; + + scoped_guard(mutex, &ti->sessions_mtx) { + session =xa_load (&ti->sessions_xa, (unsigned long )key); +if (!session) { +/* +* Initialize a new session on first call. +* Once created successfully initialized sessions are +* freed only on final release. +*/ + session =kzalloc_obj (*session); +if (!session) +return -ENOMEM ; + +/* Initialize inner with proper flags to support xa_alloc */ +xa_init_flags (&session->events_xa,XA_FLAGS_ALLOC ); +mutex_init (&session->events_mtx); + ret =xa_insert (&ti->sessions_xa, (unsigned long )key, + session,GFP_KERNEL ); +if (ret) { +xa_destroy (&session->events_xa); +kfree (session); +return ret; + } + } + } + + ctx =eventfd_ctx_fdget (evt->efd); +if (IS_ERR (ctx)) +return PTR_ERR (ctx); + + revt =kzalloc_obj (*revt); +if (!revt) { +eventfd_ctx_put (ctx); +return -ENOMEM ; + } + +guard (mutex)(&session->events_mtx); + revt->ctx = ctx; + revt->type = evt->type; + ret =xa_alloc (&session->events_xa, &cookie, revt, + scmi_evt_xa_limit,GFP_KERNEL ); +if (ret) { +eventfd_ctx_put (revt->ctx);
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1330
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+kfree (revt); +return ret; + } + + ret = ti->tsp->ops->event_subscribe (ti->tsp->ph, revt->type, revt->ctx); +if (ret) { +xa_erase (&session->events_xa, cookie); +eventfd_ctx_put (revt->ctx); +kfree (revt); +return ret;
Quite a bit of repetition in the error paths. Use a common goto block to clean up. For the guard stuff above, a helper is probably the easiest way to avoid mixing that with cleanup via gotos down here.
Quoted 68 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +67
-0+ } + +/* Return the allocated cookie */ + evt->cookie = cookie; + +/* Get hold of the instance */ +get_device (&ti->dev); + +return 0 ; +} + +static long scmi_tlm_unlocked_ioctl (struct file *filp,unsigned int cmd, +unsigned long arg) +{ +struct scmi_tlm_instance *ti = filp->private_data; +bool writable = filp->f_mode &FMODE_WRITE ; + +guard (srcu)(&ti->dead_srcu); +if (READ_ONCE (ti->dead)) +return -ENODEV ; + +switch (cmd) { +case SCMI_TLM_GET_ABI_INFO : +return scmi_tlm_abi_info_get_ioctl (ti, arg); +case SCMI_TLM_GET_CFG : +return scmi_tlm_config_get_ioctl (ti, arg); +case SCMI_TLM_SET_CFG : +if (writable) +return scmi_tlm_config_set_ioctl (ti, arg); +break ; +case SCMI_TLM_GET_INTRVS : +return scmi_tlm_intervals_get_ioctl (ti, arg); +case SCMI_TLM_GET_DE_CFG : +return scmi_tlm_de_config_get_ioctl (ti, arg); +case SCMI_TLM_SET_DE_CFG : +if (writable) +return scmi_tlm_de_config_set_ioctl (ti, arg); +break ; +case SCMI_TLM_GET_DE_INFO : +return scmi_tlm_de_info_get_ioctl (ti, arg); +case SCMI_TLM_GET_DE_LIST : +return scmi_tlm_des_list_get_ioctl (ti, arg); +case SCMI_TLM_DE_READ : +return scmi_tlm_de_value_get_ioctl (ti, arg); +case SCMI_TLM_GET_ALL_CFG : +return scmi_tlm_all_de_config_get_ioctl (ti, arg); +case SCMI_TLM_SET_ALL_CFG : +if (writable) +return scmi_tlm_all_de_config_set_ioctl (ti, arg); +break ; +case SCMI_TLM_GET_GRP_LIST : +return scmi_tlm_grps_list_get_ioctl (ti, arg); +case SCMI_TLM_GET_GRP_INFO : +return scmi_tlm_grp_info_get_ioctl (ti, arg); +case SCMI_TLM_GET_GRP_DESC : +return scmi_tlm_grp_desc_get_ioctl (ti, arg); +case SCMI_TLM_SINGLE_READ : +return scmi_tlm_des_read_ioctl (ti, arg, true); +case SCMI_TLM_BULK_READ : +return scmi_tlm_des_read_ioctl (ti, arg, false); +case SCMI_TLM_BATCH_READ : +return scmi_tlm_des_batch_read_ioctl (ti, arg); +case SCMI_TLM_GET_SHMTI_LIST : +return scmi_tlm_shmtis_list_get_ioctl (ti, arg); +case SCMI_TLM_RESET : +if (writable) +return scmi_tlm_reset_ioctl (ti, arg);
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1482
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+break ; +case SCMI_TLM_GET_UUID_LIST : +return scmi_tlm_uuid_list_get_ioctl (ti, arg); +case SCMI_TLM_EVENT_SUBSCRIBE : +return scmi_tlm_event_subscribe_ioctl (ti, arg, filp); +default : +return -ENOTTY ; + } + +return -EPERM ;
I'd just return this where you break above.
Seems little benefit in coming down here to do so given they are all error paths.
Quoted 68 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +67
-0+} + +static int scmi_telemetry_probe (struct scmi_device *sdev) +{ +const struct scmi_handle *handle = sdev->handle; +struct scmi_protocol_handle *ph; +struct scmi_tlm_instance *ti; +unsigned int instance; +const void *ops; +int ret; + +if (!handle) +return -ENODEV ; + + instance = handle->id; +if (instance >=SCMI_TLM_NUM_DEVS ) +return -ENOSPC ; + + ops = handle->devm_protocol_get (sdev, sdev->protocol_id, &ph); +if (IS_ERR (ops)) +return dev_err_probe (&sdev->dev,PTR_ERR (ops), +"Cannot access protocol:0x%X\n" , + sdev->protocol_id); + + ti =scmi_tlm_instance_init (sdev, ops, ph, instance); +if (IS_ERR (ti)) +return PTR_ERR (ti); + +/* +* Setting the platform device as parent to enforce proper ordering +* at shutdown and to avoid the need for explicit get/put_device +*/ + ti->dev.parent = &sdev->dev; + ti->dev.devt =MKDEV (MAJOR (stlm_devt),MINOR (stlm_devt) + instance); + ti->dev.class = &stlm_class; + ti->dev.release = scmi_tlm_device_release; +device_initialize (&ti->dev); + + ret =dev_set_name (&ti->dev,"tlm_%d" , ti->id); +if (ret) + goto err_dev; + +cdev_init (&ti->cdev, &stlm_fops); + ti->cdev.owner =THIS_MODULE ; + + ret =cdev_device_add (&ti->cdev, &ti->dev); +if (ret) + goto err_dev; + +dev_set_drvdata (&sdev->dev, ti); + +return 0 ; + +err_dev: +put_device (&ti->dev); + +return ret; +} + +static void scmi_telemetry_remove (struct scmi_device *sdev) +{ +struct scmi_tlm_events_session *session; +struct scmi_tlm_instance *ti; +unsigned long index; + + ti =dev_get_drvdata (&sdev->dev); +if (!ti)
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1722
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+return ; + +/* Stop ongoing opens and wait on in-flight operation to drain */ +WRITE_ONCE (ti->dead, true); +synchronize_srcu (&ti->dead_srcu); + +/* Inhibit future opens */ +cdev_device_del (&ti->cdev, &ti->dev); +/* +* Drop any residual session and related events if unbind happened whiel
while
Quoted 37 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +36
-0+* some event fds were still open. +*/ +mutex_lock (&ti->sessions_mtx); +xa_for_each (&ti->sessions_xa, index, session) { +xa_erase (&ti->sessions_xa, index); +/* Force any event unsubscribe...protocol is still alive */ +scmi_tlm_events_session_unsubscribe (ti, session); + } +mutex_unlock (&ti->sessions_mtx); + +put_device (&ti->dev); +} + +static int __init scmi_telemetry_init (void ) +{ +int ret; + + ret =alloc_chrdev_region (&stlm_devt,0 ,SCMI_TLM_NUM_DEVS , +SCMI_TLM_DRIVER ); +if (ret) +return ret; + + ret =class_register (&stlm_class); +if (ret) + goto err_class; + + ret =scmi_register (&scmi_telemetry_driver); +if (ret) + goto err_scmi; + +return 0 ; + +err_scmi: +class_unregister (&stlm_class); + +err_class:
drivers/firmware/arm_scmi/scmi_system_telemetry.c line 1781
Quoted 10 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +10
-0+unregister_chrdev_region (stlm_devt,SCMI_TLM_NUM_DEVS ); + +return ret; +} +module_init (scmi_telemetry_init); + +static void __exit scmi_telemetry_exit(void) +{ +scmi_unregister (&scmi_telemetry_driver); +unregister_chrdev_region (stlm_devt,SCMI_TLM_NUM_DEVS );
This should be reverse order of init == same as error paths at the end of init. Anything else needs some comments on why.
Quoted 3 lines
drivers/firmware/arm_scmi/scmi_system_telemetry.c new file +3
-0+class_unregister (&stlm_class); +} +module_exit (scmi_telemetry_exit);
Cristian Marussi
reviewerPATCH v15 31/35 firmware: arm_scmi: Enable autoloading for System Telemetry driver
In reply to Cristian MarussiAdd the SCMI System Telemetry driver and related protocols to the list of standard protocols whosw devices are created automatically, so enabling module autoloading.
--- drivers/firmware/arm_scmi/bus.c | 1 + 1 file changed, 1 insertion(+)
drivers/firmware/arm_scmi/bus.c +1 -0
@@ -546,6 +546,7 @@ static const struct scmi_device_id scmi_std_id_table[] = { { SCMI_PROTOCOL_POWERCAP, "powercap" }, { SCMI_PROTOCOL_PINCTRL, "pinctrl" }, { SCMI_PROTOCOL_PINCTRL, "pinctrl-imx" }, + { SCMI_PROTOCOL_TELEMETRY, "telemetry" }, { }, };
Signature · 1 line
2.54.0
Cristian Marussi
reviewerPATCH v15 32/35 docs: ioctl-number: Add SCMI Ioctls
In reply to Cristian MarussiAdd SCMI Subsystem IOCTLs numbers
--- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 + 1 file changed, 1 insertion(+)
Documentation/userspace-api/ioctl/ioctl-number.rst +1 -0
@@ -403,6 +403,7 @@ Code Seq# Include File Comments 0xEC 00-01 linux/platform_data/cros_ec_chardev.h ChromeOS EC driver 0xEE 00-09 uapi/linux/pfrut.h Platform Firmware Runtime Update and Telemetry 0xEF 00-0F uapi/linux/ntfs.h NTFS +0xF1 00-FF uapi/linux/scmi.h ARM SCMI Subsystem 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) <mailto:[email protected]> 0xF6 all LTTng Linux Trace Toolkit Next Generation
Signature · 1 line
2.54.0
Cristian Marussi
reviewerPATCH v15 33/35 Documentation: Add SCMI System Telemetry documentation
In reply to Cristian MarussiDocument ARM SCMI Telemetry IOCTLs interface.
--- Documentation/userspace-api/index.rst | 1 + Documentation/userspace-api/stlm.rst | 590 ++++++++++++++++++++++++++ 2 files changed, 591 insertions(+) create mode 100644 Documentation/userspace-api/stlm.rst
Documentation/userspace-api/index.rst +1 -0
@@ -54,6 +54,7 @@ Devices and I/O dcdbas vduse isapnp + stlm Everything else ===============
Documentation/userspace-api/stlm.rst new file +590 -0
590 lines not on this page · raw message
Signature · 1 line
2.54.0
Re: [PATCH v15 33/35] Documentation: Add SCMI System Telemetry documentation
Quoted 4 lines · On Mon, 9 Nov 2026 09:12:19 +0000 Cristian Marussi <[email protected]>…
Document ARM SCMI Telemetry IOCTLs interface.
Co-developed-by Philip Radford <[email protected]>Signed-off-by Philip Radford <[email protected]>
Various trivial things inline. For example code it is always a balance between writing production quality stuff that has lots of info that doesn't matter for docs, and over simplifying to the point of ending with random broken code.
Here there is enough error handling to expect it to work but there are cases where it doesn't. So tidy those up for consistency.
Jonathan
Quoted 26 lines
--- Documentation/userspace-api/index.rst | 1 + Documentation/userspace-api/stlm.rst | 590 ++++++++++++++++++++++++++ 2 files changed, 591 insertions(+) create mode 100644 Documentation/userspace-api/stlm.rstDocumentation/userspace-api/index.rst +1
-0@@ -54,6 +54,7 @@ Devices and I/O dcdbas vduse isapnp + stlm Everything else ===============Documentation/userspace-api/stlm.rst new file +2
-0@@ -0,0 +1,590 @@ +.. SPDX-License-Identifier: GPL-2.0 +
Documentation/userspace-api/stlm.rst line 12
Quoted 10 lines
Documentation/userspace-api/stlm.rst new file +10
-0+======================================= +STLM - ARM SCMI Telemetry Userspace API +======================================= + +.. contents:: + +Overview +======== + +ARM SCMI is a System and Configuration Management protocol, based on a
Wrap docs to 80 chars.
Quoted 28 lines
Documentation/userspace-api/stlm.rst new file +27
-0+client-server model, that defines a number of messages that allows a +client/agent like Linux to discover, configure and make use of services +provided by the server/platform firmware. + +Enumerating DataEvents +====================== + +SCMI_TLM_GET_DE_LIST returns the DataEvent descriptors exposed by the +Telemetery instance. Users can size the request from +SCMI_TLM_GET_ABI_INFO.num_des or by issuing a zero-sized batch query. + +Example +------- + +.. code-block:: c + + int main(int argc, char **argv) + { + struct scmi_tlm_abi_info abi = {}; + struct scmi_tlm_batch batch = {}; + struct scmi_tlm_de_info *des; + int fd, ret, i; + + fd = open("/dev/scmi/tlm_0", O_RDONLY | O_CLOEXEC); + if (fd < 0) + return fd; +
Documentation/userspace-api/stlm.rst line 85
Quoted 10 lines
Documentation/userspace-api/stlm.rst new file +10
-0+ abi.size = sizeof(abi); + ret = ioctl(fd, SCMI_TLM_GET_ABI_INFO, &abi); + if (ret) { + close(fd); + return ret; + } + + if (!abi.num_des) { + close(fd); + return ret;
returns success. Make that explicit with return 0;
Quoted 62 lines
Documentation/userspace-api/stlm.rst new file +62
-0+ } + + des = calloc(abi.num_des, sizeof(*des)); + if (!des) { + close(fd); + return -ENOMEM; + } + + batch.num_items = abi.num_des; + batch.item_sz = sizeof(*des); + batch.items = (unsigned long)des; + + ret = ioctl(fd, SCMI_TLM_GET_DE_LIST, &batch); + if (ret) { + free(des); + close(fd); + return ret; + } + + for (i = 0; i < batch.num_items; i++) { + printf("0x%08x: %.*s size:%u type:%u unit:%u", des[i].id, 16, + des[i].name, des[i].data_sz, des[i].type, des[i].unit); + + if (des[i].flags & SCMI_TLM_DEINFO_GROUP) + printf("group:%u", des[i].grp_id); + + printf("\n"); + } + + return 0; + } + +Enumerating Groups +================== + +SCMI_TLM_GET_GRP_LIST returns the groups exposed by the Telemetry Instance. +Group descriptors report; the group ID, the number of DEs in the group, and +the number of update intervals supported by the group. + +SCMI_TLM_GET_GRP_DESC can then be used to retrieve the DE IDs of a specific +group. + +Example +------- + +.. code-block:: c + + int main(int argc, char **argv) + { + struct scmi_tlm_abi_info abi = {}; + struct scmi_tlm_batch batch = {}; + struct scmi_tlm_grp_info *groups; + int fd, ret, i; + + fd = open("/dev/scmi/tlm_0", O_RDONLY | O_CLOEXEC); + if (fd < 0) + return fd; + + abi.size = sizeof(abi); + ret = ioctl(fd, SCMI_TLM_GET_ABI_INFO, &abi); + + if (ret){
Documentation/userspace-api/stlm.rst line 157
Quoted 10 lines
Documentation/userspace-api/stlm.rst new file +10
-0+ free(groups); + close(fd); + return ret; + } + + if (!abi.num_groups) { + ret = 0; + free(groups); + close(fd); + return ret;
return 0;
rather than setting ret.
Documentation/userspace-api/stlm.rst line 164
Quoted 7 lines
Documentation/userspace-api/stlm.rst new file +7
-0+ } + + groups = calloc(abi.num_groups, sizeof(*groups)); + if (!groups) { + ret = -ENOMEM; + free(groups); + close(fd);
return an error rather than carrying on.
Quoted 20 lines
Documentation/userspace-api/stlm.rst new file +20
-0+ } + + batch.num_items = abi.num_groups; + batch.item_sz = sizeof(*groups); + batch.items = (unsigned long)groups; + + ret = ioctl(fd, SCMI_TLM_GET_GRP_LIST, &batch); + if (ret) { + free(groups); + close(fd); + return ret; + } + + for (i = 0; i < batch.num_items; i++) { + struct scmi_tlm_grp_desc desc = {}; + __u32 *de_ids; + int j; + + printf("group:%u des:%u intervals:%u\n", groups[i].grp_id, + groups[i].num_des, groups[i].num_intervals);
Documentation/userspace-api/stlm.rst line 194
Quoted 10 lines
Documentation/userspace-api/stlm.rst new file +10
-0+ + if (!groups[i].num_des) + continue; + + de_ids = calloc(groups[i].num_des, sizeof(*de_ids)); + if (!de_ids) { + ret = -ENOMEM; + free(groups); + close(fd); + return de_ids;
return -ENOMEM;
I'm curious if sashiko noticed your docs and spotted this stuff? I guess it might be conservative as often such examples in docs skip error handling.
Quoted 26 lines
Documentation/userspace-api/stlm.rst new file +26
-0+ } + + desc.grp_id = groups[i].grp_id; + desc.num_des = groups[i].num_des; + desc.composing_des = (unsigned long)de_ids; + + ret = ioctl(fd, SCMI_TLM_GET_GRP_DESC, &desc); + if (ret) { + free(de_ids); + free(groups); + close(fd); + return ret; + } + + printf("DataEvents:"); + for (j = 0; j < desc.num_des; j++) + printf(" 0x%08x", de_ids[j]); + printf("\n"); + + free(de_ids); + } + + free(groups); + close(fd); + return 0; + }
...
Cristian Marussi
reviewerRFC PATCH v15 34/35 kselftest/arm64: Add SCMI Telemetry UAPI compliance testcases
In reply to Cristian MarussiFrom: Philip Radford <[email protected]>
Add support to test SCMI Telemetry UAPI compliance testcases.
--- tools/testing/selftests/arm64/Makefile | 2 +- tools/testing/selftests/arm64/scmi/Makefile | 12 + tools/testing/selftests/arm64/scmi/config | 2 + .../arm64/scmi/test_scmi_telemetry_uapi.c | 1810 +++++++++++++++++ 4 files changed, 1825 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/scmi/Makefile create mode 100644 tools/testing/selftests/arm64/scmi/config create mode 100644 tools/testing/selftests/arm64/scmi/test_scmi_telemetry_uapi.c
tools/testing/selftests/arm64/Makefile +1 -1
@@ -4,7 +4,7 @@ ARCH ?= $(shell uname -m 2>/dev/null || echo not) ifneq (,$(filter $(ARCH),aarch64 arm64)) -ARM64_SUBTARGETS ?= tags signal pauth fp mte bti abi gcs +ARM64_SUBTARGETS ?= tags signal pauth fp mte bti abi gcs scmi else ARM64_SUBTARGETS := endif
tools/testing/selftests/arm64/scmi/Makefile new file +12 -0
@@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2026 ARM Limited + +# Additional include paths needed by kselftest.h and local headers +CFLAGS += -std=gnu99 -I. +LDFLAGS += -static + +# Generated binaries to be installed by top KSFT script +TEST_GEN_PROGS := test_scmi_telemetry_uapi + +# Include KSFT lib.mk. +include ../../lib.mk
tools/testing/selftests/arm64/scmi/config new file +2 -0
@@ -0,0 +1,2 @@ +CONFIG_ARM_SCMI_SYSTEM_TELEMETRY=y +
tools/testing/selftests/arm64/scmi/test_scmi_telemetry_uapi.c new file +1810 -0
1,810 lines not on this page · raw message
Signature · 1 line
2.54.0