Skip to content

Commit 416eeb2

Browse files
debox1jwrdegoede
authored andcommitted
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>
1 parent 4d1b7ef commit 416eeb2

File tree

4 files changed

+344
-8
lines changed

4 files changed

+344
-8
lines changed

drivers/platform/x86/intel/pmt/class.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include "../vsec.h"
1818
#include "class.h"
1919

20-
#define PMT_XA_START 0
20+
#define PMT_XA_START 1
2121
#define PMT_XA_MAX INT_MAX
2222
#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX)
2323
#define GUID_SPR_PUNIT 0x9956f43f
@@ -247,6 +247,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
247247
struct intel_pmt_namespace *ns,
248248
struct device *parent)
249249
{
250+
struct intel_vsec_device *ivdev = dev_to_ivdev(parent);
250251
struct resource res = {0};
251252
struct device *dev;
252253
int ret;
@@ -270,7 +271,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
270271
if (ns->attr_grp) {
271272
ret = sysfs_create_group(entry->kobj, ns->attr_grp);
272273
if (ret)
273-
goto fail_sysfs;
274+
goto fail_sysfs_create_group;
274275
}
275276

276277
/* if size is 0 assume no data buffer, so no file needed */
@@ -295,13 +296,23 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
295296
entry->pmt_bin_attr.size = entry->size;
296297

297298
ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr);
298-
if (!ret)
299-
return 0;
299+
if (ret)
300+
goto fail_ioremap;
301+
302+
if (ns->pmt_add_endpoint) {
303+
ret = ns->pmt_add_endpoint(entry, ivdev->pcidev);
304+
if (ret)
305+
goto fail_add_endpoint;
306+
}
307+
308+
return 0;
300309

310+
fail_add_endpoint:
311+
sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr);
301312
fail_ioremap:
302313
if (ns->attr_grp)
303314
sysfs_remove_group(entry->kobj, ns->attr_grp);
304-
fail_sysfs:
315+
fail_sysfs_create_group:
305316
device_unregister(dev);
306317
fail_dev_create:
307318
xa_erase(ns->xa, entry->devid);

drivers/platform/x86/intel/pmt/class.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/io.h>
1010

1111
#include "../vsec.h"
12+
#include "telemetry.h"
1213

1314
/* PMT access types */
1415
#define ACCESS_BARID 2
@@ -18,6 +19,16 @@
1819
#define GET_BIR(v) ((v) & GENMASK(2, 0))
1920
#define GET_ADDRESS(v) ((v) & GENMASK(31, 3))
2021

22+
struct pci_dev;
23+
24+
struct telem_endpoint {
25+
struct pci_dev *pcidev;
26+
struct telem_header header;
27+
void __iomem *base;
28+
bool present;
29+
struct kref kref;
30+
};
31+
2132
struct intel_pmt_header {
2233
u32 base_offset;
2334
u32 size;
@@ -26,6 +37,7 @@ struct intel_pmt_header {
2637
};
2738

2839
struct intel_pmt_entry {
40+
struct telem_endpoint *ep;
2941
struct intel_pmt_header header;
3042
struct bin_attribute pmt_bin_attr;
3143
struct kobject *kobj;
@@ -43,6 +55,8 @@ struct intel_pmt_namespace {
4355
const struct attribute_group *attr_grp;
4456
int (*pmt_header_decode)(struct intel_pmt_entry *entry,
4557
struct device *dev);
58+
int (*pmt_add_endpoint)(struct intel_pmt_entry *entry,
59+
struct pci_dev *pdev);
4660
};
4761

4862
bool intel_pmt_is_early_client_hw(struct device *dev);

drivers/platform/x86/intel/pmt/telemetry.c

Lines changed: 188 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@
3030
/* Used by client hardware to identify a fixed telemetry entry*/
3131
#define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000
3232

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+
3342
enum telem_type {
3443
TELEM_TYPE_PUNIT = 0,
3544
TELEM_TYPE_CRASHLOG,
@@ -84,21 +93,195 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
8493
return 0;
8594
}
8695

96+
static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry,
97+
struct pci_dev *pdev)
98+
{
99+
struct telem_endpoint *ep;
100+
101+
/* Endpoint lifetimes are managed by kref, not devres */
102+
entry->ep = kzalloc(sizeof(*(entry->ep)), GFP_KERNEL);
103+
if (!entry->ep)
104+
return -ENOMEM;
105+
106+
ep = entry->ep;
107+
ep->pcidev = pdev;
108+
ep->header.access_type = entry->header.access_type;
109+
ep->header.guid = entry->header.guid;
110+
ep->header.base_offset = entry->header.base_offset;
111+
ep->header.size = entry->header.size;
112+
ep->base = entry->base;
113+
ep->present = true;
114+
115+
kref_init(&ep->kref);
116+
117+
return 0;
118+
}
119+
87120
static DEFINE_XARRAY_ALLOC(telem_array);
88121
static struct intel_pmt_namespace pmt_telem_ns = {
89122
.name = "telem",
90123
.xa = &telem_array,
91124
.pmt_header_decode = pmt_telem_header_decode,
125+
.pmt_add_endpoint = pmt_telem_add_endpoint,
92126
};
93127

128+
/* Called when all users unregister and the device is removed */
129+
static void pmt_telem_ep_release(struct kref *kref)
130+
{
131+
struct telem_endpoint *ep;
132+
133+
ep = container_of(kref, struct telem_endpoint, kref);
134+
kfree(ep);
135+
}
136+
137+
unsigned long pmt_telem_get_next_endpoint(unsigned long start)
138+
{
139+
struct intel_pmt_entry *entry;
140+
unsigned long found_idx;
141+
142+
mutex_lock(&ep_lock);
143+
xa_for_each_start(&telem_array, found_idx, entry, start) {
144+
/*
145+
* Return first found index after start.
146+
* 0 is not valid id.
147+
*/
148+
if (found_idx > start)
149+
break;
150+
}
151+
mutex_unlock(&ep_lock);
152+
153+
return found_idx == start ? 0 : found_idx;
154+
}
155+
EXPORT_SYMBOL_NS_GPL(pmt_telem_get_next_endpoint, INTEL_PMT_TELEMETRY);
156+
157+
struct telem_endpoint *pmt_telem_register_endpoint(int devid)
158+
{
159+
struct intel_pmt_entry *entry;
160+
unsigned long index = devid;
161+
162+
mutex_lock(&ep_lock);
163+
entry = xa_find(&telem_array, &index, index, XA_PRESENT);
164+
if (!entry) {
165+
mutex_unlock(&ep_lock);
166+
return ERR_PTR(-ENXIO);
167+
}
168+
169+
kref_get(&entry->ep->kref);
170+
mutex_unlock(&ep_lock);
171+
172+
return entry->ep;
173+
}
174+
EXPORT_SYMBOL_NS_GPL(pmt_telem_register_endpoint, INTEL_PMT_TELEMETRY);
175+
176+
void pmt_telem_unregister_endpoint(struct telem_endpoint *ep)
177+
{
178+
kref_put(&ep->kref, pmt_telem_ep_release);
179+
}
180+
EXPORT_SYMBOL_NS_GPL(pmt_telem_unregister_endpoint, INTEL_PMT_TELEMETRY);
181+
182+
int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info)
183+
{
184+
struct intel_pmt_entry *entry;
185+
unsigned long index = devid;
186+
int err = 0;
187+
188+
if (!info)
189+
return -EINVAL;
190+
191+
mutex_lock(&ep_lock);
192+
entry = xa_find(&telem_array, &index, index, XA_PRESENT);
193+
if (!entry) {
194+
err = -ENXIO;
195+
goto unlock;
196+
}
197+
198+
info->pdev = entry->ep->pcidev;
199+
info->header = entry->ep->header;
200+
201+
unlock:
202+
mutex_unlock(&ep_lock);
203+
return err;
204+
205+
}
206+
EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, INTEL_PMT_TELEMETRY);
207+
208+
int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count)
209+
{
210+
u32 offset, size;
211+
212+
if (!ep->present)
213+
return -ENODEV;
214+
215+
offset = SAMPLE_ID_OFFSET(id);
216+
size = ep->header.size;
217+
218+
if (offset + NUM_BYTES_QWORD(count) > size)
219+
return -EINVAL;
220+
221+
memcpy_fromio(data, ep->base + offset, NUM_BYTES_QWORD(count));
222+
223+
return ep->present ? 0 : -EPIPE;
224+
}
225+
EXPORT_SYMBOL_NS_GPL(pmt_telem_read, INTEL_PMT_TELEMETRY);
226+
227+
int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count)
228+
{
229+
u32 offset, size;
230+
231+
if (!ep->present)
232+
return -ENODEV;
233+
234+
offset = SAMPLE_ID_OFFSET32(id);
235+
size = ep->header.size;
236+
237+
if (offset + NUM_BYTES_DWORD(count) > size)
238+
return -EINVAL;
239+
240+
memcpy_fromio(data, ep->base + offset, NUM_BYTES_DWORD(count));
241+
242+
return ep->present ? 0 : -EPIPE;
243+
}
244+
EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, INTEL_PMT_TELEMETRY);
245+
246+
struct telem_endpoint *
247+
pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos)
248+
{
249+
int devid = 0;
250+
int inst = 0;
251+
int err = 0;
252+
253+
while ((devid = pmt_telem_get_next_endpoint(devid))) {
254+
struct telem_endpoint_info ep_info;
255+
256+
err = pmt_telem_get_endpoint_info(devid, &ep_info);
257+
if (err)
258+
return ERR_PTR(err);
259+
260+
if (ep_info.header.guid == guid && ep_info.pdev == pcidev) {
261+
if (inst == pos)
262+
return pmt_telem_register_endpoint(devid);
263+
++inst;
264+
}
265+
}
266+
267+
return ERR_PTR(-ENXIO);
268+
}
269+
EXPORT_SYMBOL_NS_GPL(pmt_telem_find_and_register_endpoint, INTEL_PMT_TELEMETRY);
270+
94271
static void pmt_telem_remove(struct auxiliary_device *auxdev)
95272
{
96273
struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev);
97274
int i;
98275

99-
for (i = 0; i < priv->num_entries; i++)
100-
intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns);
101-
}
276+
mutex_lock(&ep_lock);
277+
for (i = 0; i < priv->num_entries; i++) {
278+
struct intel_pmt_entry *entry = &priv->entry[i];
279+
280+
kref_put(&entry->ep->kref, pmt_telem_ep_release);
281+
intel_pmt_dev_destroy(entry, &pmt_telem_ns);
282+
}
283+
mutex_unlock(&ep_lock);
284+
};
102285

103286
static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
104287
{
@@ -117,7 +300,9 @@ static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxilia
117300
for (i = 0; i < intel_vsec_dev->num_resources; i++) {
118301
struct intel_pmt_entry *entry = &priv->entry[priv->num_entries];
119302

303+
mutex_lock(&ep_lock);
120304
ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i);
305+
mutex_unlock(&ep_lock);
121306
if (ret < 0)
122307
goto abort_probe;
123308
if (ret)

0 commit comments

Comments
 (0)