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

platform/x86/intel/pmt: telemetry: Export API to read telemetry

Export symbols to allow access to Intel PMT Telemetry data on available
devices. Provides APIs to search, register, and read telemetry using a
kref managed pointer that serves as a handle to a telemetry endpoint.
To simplify searching for present devices, have the IDA start at 1
instead of 0 so that 0 can be used to indicate end of search.

Signed-off-by: David E. Box <david.e.box@linux.intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20231129222132.2331261-11-david.e.box@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

David E. Box and committed by
Hans de Goede
416eeb2e 4d1b7efe

+344 -8
+16 -5
drivers/platform/x86/intel/pmt/class.c
··· 17 17 #include "../vsec.h" 18 18 #include "class.h" 19 19 20 - #define PMT_XA_START 0 20 + #define PMT_XA_START 1 21 21 #define PMT_XA_MAX INT_MAX 22 22 #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) 23 23 #define GUID_SPR_PUNIT 0x9956f43f ··· 247 247 struct intel_pmt_namespace *ns, 248 248 struct device *parent) 249 249 { 250 + struct intel_vsec_device *ivdev = dev_to_ivdev(parent); 250 251 struct resource res = {0}; 251 252 struct device *dev; 252 253 int ret; ··· 271 270 if (ns->attr_grp) { 272 271 ret = sysfs_create_group(entry->kobj, ns->attr_grp); 273 272 if (ret) 274 - goto fail_sysfs; 273 + goto fail_sysfs_create_group; 275 274 } 276 275 277 276 /* if size is 0 assume no data buffer, so no file needed */ ··· 296 295 entry->pmt_bin_attr.size = entry->size; 297 296 298 297 ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); 299 - if (!ret) 300 - return 0; 298 + if (ret) 299 + goto fail_ioremap; 301 300 301 + if (ns->pmt_add_endpoint) { 302 + ret = ns->pmt_add_endpoint(entry, ivdev->pcidev); 303 + if (ret) 304 + goto fail_add_endpoint; 305 + } 306 + 307 + return 0; 308 + 309 + fail_add_endpoint: 310 + sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); 302 311 fail_ioremap: 303 312 if (ns->attr_grp) 304 313 sysfs_remove_group(entry->kobj, ns->attr_grp); 305 - fail_sysfs: 314 + fail_sysfs_create_group: 306 315 device_unregister(dev); 307 316 fail_dev_create: 308 317 xa_erase(ns->xa, entry->devid);
+14
drivers/platform/x86/intel/pmt/class.h
··· 9 9 #include <linux/io.h> 10 10 11 11 #include "../vsec.h" 12 + #include "telemetry.h" 12 13 13 14 /* PMT access types */ 14 15 #define ACCESS_BARID 2 ··· 19 18 #define GET_BIR(v) ((v) & GENMASK(2, 0)) 20 19 #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) 21 20 21 + struct pci_dev; 22 + 23 + struct telem_endpoint { 24 + struct pci_dev *pcidev; 25 + struct telem_header header; 26 + void __iomem *base; 27 + bool present; 28 + struct kref kref; 29 + }; 30 + 22 31 struct intel_pmt_header { 23 32 u32 base_offset; 24 33 u32 size; ··· 37 26 }; 38 27 39 28 struct intel_pmt_entry { 29 + struct telem_endpoint *ep; 40 30 struct intel_pmt_header header; 41 31 struct bin_attribute pmt_bin_attr; 42 32 struct kobject *kobj; ··· 55 43 const struct attribute_group *attr_grp; 56 44 int (*pmt_header_decode)(struct intel_pmt_entry *entry, 57 45 struct device *dev); 46 + int (*pmt_add_endpoint)(struct intel_pmt_entry *entry, 47 + struct pci_dev *pdev); 58 48 }; 59 49 60 50 bool intel_pmt_is_early_client_hw(struct device *dev);
+188 -3
drivers/platform/x86/intel/pmt/telemetry.c
··· 30 30 /* Used by client hardware to identify a fixed telemetry entry*/ 31 31 #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 32 32 33 + #define NUM_BYTES_QWORD(v) ((v) << 3) 34 + #define SAMPLE_ID_OFFSET(v) ((v) << 3) 35 + 36 + #define NUM_BYTES_DWORD(v) ((v) << 2) 37 + #define SAMPLE_ID_OFFSET32(v) ((v) << 2) 38 + 39 + /* Protects access to the xarray of telemetry endpoint handles */ 40 + static DEFINE_MUTEX(ep_lock); 41 + 33 42 enum telem_type { 34 43 TELEM_TYPE_PUNIT = 0, 35 44 TELEM_TYPE_CRASHLOG, ··· 93 84 return 0; 94 85 } 95 86 87 + static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry, 88 + struct pci_dev *pdev) 89 + { 90 + struct telem_endpoint *ep; 91 + 92 + /* Endpoint lifetimes are managed by kref, not devres */ 93 + entry->ep = kzalloc(sizeof(*(entry->ep)), GFP_KERNEL); 94 + if (!entry->ep) 95 + return -ENOMEM; 96 + 97 + ep = entry->ep; 98 + ep->pcidev = pdev; 99 + ep->header.access_type = entry->header.access_type; 100 + ep->header.guid = entry->header.guid; 101 + ep->header.base_offset = entry->header.base_offset; 102 + ep->header.size = entry->header.size; 103 + ep->base = entry->base; 104 + ep->present = true; 105 + 106 + kref_init(&ep->kref); 107 + 108 + return 0; 109 + } 110 + 96 111 static DEFINE_XARRAY_ALLOC(telem_array); 97 112 static struct intel_pmt_namespace pmt_telem_ns = { 98 113 .name = "telem", 99 114 .xa = &telem_array, 100 115 .pmt_header_decode = pmt_telem_header_decode, 116 + .pmt_add_endpoint = pmt_telem_add_endpoint, 101 117 }; 118 + 119 + /* Called when all users unregister and the device is removed */ 120 + static void pmt_telem_ep_release(struct kref *kref) 121 + { 122 + struct telem_endpoint *ep; 123 + 124 + ep = container_of(kref, struct telem_endpoint, kref); 125 + kfree(ep); 126 + } 127 + 128 + unsigned long pmt_telem_get_next_endpoint(unsigned long start) 129 + { 130 + struct intel_pmt_entry *entry; 131 + unsigned long found_idx; 132 + 133 + mutex_lock(&ep_lock); 134 + xa_for_each_start(&telem_array, found_idx, entry, start) { 135 + /* 136 + * Return first found index after start. 137 + * 0 is not valid id. 138 + */ 139 + if (found_idx > start) 140 + break; 141 + } 142 + mutex_unlock(&ep_lock); 143 + 144 + return found_idx == start ? 0 : found_idx; 145 + } 146 + EXPORT_SYMBOL_NS_GPL(pmt_telem_get_next_endpoint, INTEL_PMT_TELEMETRY); 147 + 148 + struct telem_endpoint *pmt_telem_register_endpoint(int devid) 149 + { 150 + struct intel_pmt_entry *entry; 151 + unsigned long index = devid; 152 + 153 + mutex_lock(&ep_lock); 154 + entry = xa_find(&telem_array, &index, index, XA_PRESENT); 155 + if (!entry) { 156 + mutex_unlock(&ep_lock); 157 + return ERR_PTR(-ENXIO); 158 + } 159 + 160 + kref_get(&entry->ep->kref); 161 + mutex_unlock(&ep_lock); 162 + 163 + return entry->ep; 164 + } 165 + EXPORT_SYMBOL_NS_GPL(pmt_telem_register_endpoint, INTEL_PMT_TELEMETRY); 166 + 167 + void pmt_telem_unregister_endpoint(struct telem_endpoint *ep) 168 + { 169 + kref_put(&ep->kref, pmt_telem_ep_release); 170 + } 171 + EXPORT_SYMBOL_NS_GPL(pmt_telem_unregister_endpoint, INTEL_PMT_TELEMETRY); 172 + 173 + int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info) 174 + { 175 + struct intel_pmt_entry *entry; 176 + unsigned long index = devid; 177 + int err = 0; 178 + 179 + if (!info) 180 + return -EINVAL; 181 + 182 + mutex_lock(&ep_lock); 183 + entry = xa_find(&telem_array, &index, index, XA_PRESENT); 184 + if (!entry) { 185 + err = -ENXIO; 186 + goto unlock; 187 + } 188 + 189 + info->pdev = entry->ep->pcidev; 190 + info->header = entry->ep->header; 191 + 192 + unlock: 193 + mutex_unlock(&ep_lock); 194 + return err; 195 + 196 + } 197 + EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, INTEL_PMT_TELEMETRY); 198 + 199 + int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) 200 + { 201 + u32 offset, size; 202 + 203 + if (!ep->present) 204 + return -ENODEV; 205 + 206 + offset = SAMPLE_ID_OFFSET(id); 207 + size = ep->header.size; 208 + 209 + if (offset + NUM_BYTES_QWORD(count) > size) 210 + return -EINVAL; 211 + 212 + memcpy_fromio(data, ep->base + offset, NUM_BYTES_QWORD(count)); 213 + 214 + return ep->present ? 0 : -EPIPE; 215 + } 216 + EXPORT_SYMBOL_NS_GPL(pmt_telem_read, INTEL_PMT_TELEMETRY); 217 + 218 + int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count) 219 + { 220 + u32 offset, size; 221 + 222 + if (!ep->present) 223 + return -ENODEV; 224 + 225 + offset = SAMPLE_ID_OFFSET32(id); 226 + size = ep->header.size; 227 + 228 + if (offset + NUM_BYTES_DWORD(count) > size) 229 + return -EINVAL; 230 + 231 + memcpy_fromio(data, ep->base + offset, NUM_BYTES_DWORD(count)); 232 + 233 + return ep->present ? 0 : -EPIPE; 234 + } 235 + EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, INTEL_PMT_TELEMETRY); 236 + 237 + struct telem_endpoint * 238 + pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) 239 + { 240 + int devid = 0; 241 + int inst = 0; 242 + int err = 0; 243 + 244 + while ((devid = pmt_telem_get_next_endpoint(devid))) { 245 + struct telem_endpoint_info ep_info; 246 + 247 + err = pmt_telem_get_endpoint_info(devid, &ep_info); 248 + if (err) 249 + return ERR_PTR(err); 250 + 251 + if (ep_info.header.guid == guid && ep_info.pdev == pcidev) { 252 + if (inst == pos) 253 + return pmt_telem_register_endpoint(devid); 254 + ++inst; 255 + } 256 + } 257 + 258 + return ERR_PTR(-ENXIO); 259 + } 260 + EXPORT_SYMBOL_NS_GPL(pmt_telem_find_and_register_endpoint, INTEL_PMT_TELEMETRY); 102 261 103 262 static void pmt_telem_remove(struct auxiliary_device *auxdev) 104 263 { 105 264 struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev); 106 265 int i; 107 266 108 - for (i = 0; i < priv->num_entries; i++) 109 - intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); 110 - } 267 + mutex_lock(&ep_lock); 268 + for (i = 0; i < priv->num_entries; i++) { 269 + struct intel_pmt_entry *entry = &priv->entry[i]; 270 + 271 + kref_put(&entry->ep->kref, pmt_telem_ep_release); 272 + intel_pmt_dev_destroy(entry, &pmt_telem_ns); 273 + } 274 + mutex_unlock(&ep_lock); 275 + }; 111 276 112 277 static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 113 278 { ··· 300 117 for (i = 0; i < intel_vsec_dev->num_resources; i++) { 301 118 struct intel_pmt_entry *entry = &priv->entry[priv->num_entries]; 302 119 120 + mutex_lock(&ep_lock); 303 121 ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i); 122 + mutex_unlock(&ep_lock); 304 123 if (ret < 0) 305 124 goto abort_probe; 306 125 if (ret)
+126
drivers/platform/x86/intel/pmt/telemetry.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _TELEMETRY_H 3 + #define _TELEMETRY_H 4 + 5 + /* Telemetry types */ 6 + #define PMT_TELEM_TELEMETRY 0 7 + #define PMT_TELEM_CRASHLOG 1 8 + 9 + struct telem_endpoint; 10 + struct pci_dev; 11 + 12 + struct telem_header { 13 + u8 access_type; 14 + u16 size; 15 + u32 guid; 16 + u32 base_offset; 17 + }; 18 + 19 + struct telem_endpoint_info { 20 + struct pci_dev *pdev; 21 + struct telem_header header; 22 + }; 23 + 24 + /** 25 + * pmt_telem_get_next_endpoint() - Get next device id for a telemetry endpoint 26 + * @start: starting devid to look from 27 + * 28 + * This functions can be used in a while loop predicate to retrieve the devid 29 + * of all available telemetry endpoints. Functions pmt_telem_get_next_endpoint() 30 + * and pmt_telem_register_endpoint() can be used inside of the loop to examine 31 + * endpoint info and register to receive a pointer to the endpoint. The pointer 32 + * is then usable in the telemetry read calls to access the telemetry data. 33 + * 34 + * Return: 35 + * * devid - devid of the next present endpoint from start 36 + * * 0 - when no more endpoints are present after start 37 + */ 38 + unsigned long pmt_telem_get_next_endpoint(unsigned long start); 39 + 40 + /** 41 + * pmt_telem_register_endpoint() - Register a telemetry endpoint 42 + * @devid: device id/handle of the telemetry endpoint 43 + * 44 + * Increments the kref usage counter for the endpoint. 45 + * 46 + * Return: 47 + * * endpoint - On success returns pointer to the telemetry endpoint 48 + * * -ENXIO - telemetry endpoint not found 49 + */ 50 + struct telem_endpoint *pmt_telem_register_endpoint(int devid); 51 + 52 + /** 53 + * pmt_telem_unregister_endpoint() - Unregister a telemetry endpoint 54 + * @ep: ep structure to populate. 55 + * 56 + * Decrements the kref usage counter for the endpoint. 57 + */ 58 + void pmt_telem_unregister_endpoint(struct telem_endpoint *ep); 59 + 60 + /** 61 + * pmt_telem_get_endpoint_info() - Get info for an endpoint from its devid 62 + * @devid: device id/handle of the telemetry endpoint 63 + * @info: Endpoint info structure to be populated 64 + * 65 + * Return: 66 + * * 0 - Success 67 + * * -ENXIO - telemetry endpoint not found for the devid 68 + * * -EINVAL - @info is NULL 69 + */ 70 + int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); 71 + 72 + /** 73 + * pmt_telem_find_and_register_endpoint() - Get a telemetry endpoint from 74 + * pci_dev device, guid and pos 75 + * @pdev: PCI device inside the Intel vsec 76 + * @guid: GUID of the telemetry space 77 + * @pos: Instance of the guid 78 + * 79 + * Return: 80 + * * endpoint - On success returns pointer to the telemetry endpoint 81 + * * -ENXIO - telemetry endpoint not found 82 + */ 83 + struct telem_endpoint *pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, 84 + u32 guid, u16 pos); 85 + 86 + /** 87 + * pmt_telem_read() - Read qwords from counter sram using sample id 88 + * @ep: Telemetry endpoint to be read 89 + * @id: The beginning sample id of the metric(s) to be read 90 + * @data: Allocated qword buffer 91 + * @count: Number of qwords requested 92 + * 93 + * Callers must ensure reads are aligned. When the call returns -ENODEV, 94 + * the device has been removed and callers should unregister the telemetry 95 + * endpoint. 96 + * 97 + * Return: 98 + * * 0 - Success 99 + * * -ENODEV - The device is not present. 100 + * * -EINVAL - The offset is out bounds 101 + * * -EPIPE - The device was removed during the read. Data written 102 + * but should be considered invalid. 103 + */ 104 + int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count); 105 + 106 + /** 107 + * pmt_telem_read32() - Read qwords from counter sram using sample id 108 + * @ep: Telemetry endpoint to be read 109 + * @id: The beginning sample id of the metric(s) to be read 110 + * @data: Allocated dword buffer 111 + * @count: Number of dwords requested 112 + * 113 + * Callers must ensure reads are aligned. When the call returns -ENODEV, 114 + * the device has been removed and callers should unregister the telemetry 115 + * endpoint. 116 + * 117 + * Return: 118 + * * 0 - Success 119 + * * -ENODEV - The device is not present. 120 + * * -EINVAL - The offset is out bounds 121 + * * -EPIPE - The device was removed during the read. Data written 122 + * but should be considered invalid. 123 + */ 124 + int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count); 125 + 126 + #endif