Skip to content

Commit 556ef09

Browse files
Yicong YangSuzuki K Poulose
authored andcommitted
hwtracing: hisi_ptt: Add support for dynamically updating the filter list
The PCIe devices supported by the PTT trace can be removed/rescanned by hotplug or through sysfs. Add support for dynamically updating the available filter list by registering a PCI bus notifier block. Then user can always get latest information about available tracing filters and driver can block the invalid filters of which related devices no longer exist in the system. Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Yicong Yang <yangyicong@hisilicon.com> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com> Link: https://lore.kernel.org/r/20230621092804.15120-3-yangyicong@huawei.com
1 parent a3ecaba commit 556ef09

3 files changed

Lines changed: 205 additions & 11 deletions

File tree

Documentation/trace/hisi-ptt.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ Endpoint function can be specified in one trace. Specifying both Root Port
153153
and function at the same time is not supported. Driver maintains a list of
154154
available filters and will check the invalid inputs.
155155

156-
Currently the available filters are detected in driver's probe. If the supported
157-
devices are removed/added after probe, you may need to reload the driver to update
158-
the filters.
156+
The available filters will be dynamically updated, which means you will always
157+
get correct filter information when hotplug events happen, or when you manually
158+
remove/rescan the devices.
159159

160160
2. Type
161161
-------

drivers/hwtracing/ptt/hisi_ptt.c

Lines changed: 162 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,24 +357,42 @@ static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt)
357357
static void hisi_ptt_del_free_filter(struct hisi_ptt *hisi_ptt,
358358
struct hisi_ptt_filter_desc *filter)
359359
{
360+
if (filter->is_port)
361+
hisi_ptt->port_mask &= ~hisi_ptt_get_filter_val(filter->devid, true);
362+
360363
list_del(&filter->list);
364+
kfree(filter->name);
361365
kfree(filter);
362366
}
363367

364368
static struct hisi_ptt_filter_desc *
365-
hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, struct pci_dev *pdev)
369+
hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port)
366370
{
367371
struct hisi_ptt_filter_desc *filter;
372+
u8 devfn = devid & 0xff;
373+
char *filter_name;
374+
375+
filter_name = kasprintf(GFP_KERNEL, "%04x:%02x:%02x.%d", pci_domain_nr(hisi_ptt->pdev->bus),
376+
PCI_BUS_NUM(devid), PCI_SLOT(devfn), PCI_FUNC(devfn));
377+
if (!filter_name) {
378+
pci_err(hisi_ptt->pdev, "failed to allocate name for filter %04x:%02x:%02x.%d\n",
379+
pci_domain_nr(hisi_ptt->pdev->bus), PCI_BUS_NUM(devid),
380+
PCI_SLOT(devfn), PCI_FUNC(devfn));
381+
return NULL;
382+
}
368383

369384
filter = kzalloc(sizeof(*filter), GFP_KERNEL);
370385
if (!filter) {
371386
pci_err(hisi_ptt->pdev, "failed to add filter for %s\n",
372-
pci_name(pdev));
387+
filter_name);
388+
kfree(filter_name);
373389
return NULL;
374390
}
375391

376-
filter->devid = PCI_DEVID(pdev->bus->number, pdev->devfn);
377-
filter->is_port = pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT;
392+
filter->name = filter_name;
393+
filter->is_port = is_port;
394+
filter->devid = devid;
395+
378396
if (filter->is_port) {
379397
list_add_tail(&filter->list, &hisi_ptt->port_filters);
380398

@@ -387,6 +405,102 @@ hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, struct pci_dev *pdev)
387405
return filter;
388406
}
389407

408+
static void hisi_ptt_update_filters(struct work_struct *work)
409+
{
410+
struct delayed_work *delayed_work = to_delayed_work(work);
411+
struct hisi_ptt_filter_update_info info;
412+
struct hisi_ptt_filter_desc *filter;
413+
struct hisi_ptt *hisi_ptt;
414+
415+
hisi_ptt = container_of(delayed_work, struct hisi_ptt, work);
416+
417+
if (!mutex_trylock(&hisi_ptt->filter_lock)) {
418+
schedule_delayed_work(&hisi_ptt->work, HISI_PTT_WORK_DELAY_MS);
419+
return;
420+
}
421+
422+
while (kfifo_get(&hisi_ptt->filter_update_kfifo, &info)) {
423+
if (info.is_add) {
424+
/*
425+
* Notify the users if failed to add this filter, others
426+
* still work and available. See the comments in
427+
* hisi_ptt_init_filters().
428+
*/
429+
filter = hisi_ptt_alloc_add_filter(hisi_ptt, info.devid, info.is_port);
430+
if (!filter)
431+
continue;
432+
} else {
433+
struct hisi_ptt_filter_desc *tmp;
434+
struct list_head *target_list;
435+
436+
target_list = info.is_port ? &hisi_ptt->port_filters :
437+
&hisi_ptt->req_filters;
438+
439+
list_for_each_entry_safe(filter, tmp, target_list, list)
440+
if (filter->devid == info.devid) {
441+
hisi_ptt_del_free_filter(hisi_ptt, filter);
442+
break;
443+
}
444+
}
445+
}
446+
447+
mutex_unlock(&hisi_ptt->filter_lock);
448+
}
449+
450+
/*
451+
* A PCI bus notifier is used here for dynamically updating the filter
452+
* list.
453+
*/
454+
static int hisi_ptt_notifier_call(struct notifier_block *nb, unsigned long action,
455+
void *data)
456+
{
457+
struct hisi_ptt *hisi_ptt = container_of(nb, struct hisi_ptt, hisi_ptt_nb);
458+
struct hisi_ptt_filter_update_info info;
459+
struct pci_dev *pdev, *root_port;
460+
struct device *dev = data;
461+
u32 port_devid;
462+
463+
pdev = to_pci_dev(dev);
464+
root_port = pcie_find_root_port(pdev);
465+
if (!root_port)
466+
return 0;
467+
468+
port_devid = PCI_DEVID(root_port->bus->number, root_port->devfn);
469+
if (port_devid < hisi_ptt->lower_bdf ||
470+
port_devid > hisi_ptt->upper_bdf)
471+
return 0;
472+
473+
info.is_port = pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT;
474+
info.devid = PCI_DEVID(pdev->bus->number, pdev->devfn);
475+
476+
switch (action) {
477+
case BUS_NOTIFY_ADD_DEVICE:
478+
info.is_add = true;
479+
break;
480+
case BUS_NOTIFY_DEL_DEVICE:
481+
info.is_add = false;
482+
break;
483+
default:
484+
return 0;
485+
}
486+
487+
/*
488+
* The FIFO size is 16 which is sufficient for almost all the cases,
489+
* since each PCIe core will have most 8 Root Ports (typically only
490+
* 1~4 Root Ports). On failure log the failed filter and let user
491+
* handle it.
492+
*/
493+
if (kfifo_in_spinlocked(&hisi_ptt->filter_update_kfifo, &info, 1,
494+
&hisi_ptt->filter_update_lock))
495+
schedule_delayed_work(&hisi_ptt->work, 0);
496+
else
497+
pci_warn(hisi_ptt->pdev,
498+
"filter update fifo overflow for target %s\n",
499+
pci_name(pdev));
500+
501+
return 0;
502+
}
503+
390504
static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data)
391505
{
392506
struct pci_dev *root_port = pcie_find_root_port(pdev);
@@ -407,7 +521,8 @@ static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data)
407521
* should be partial initialized and users would know which filter fails
408522
* through the log. Other functions of PTT device are still available.
409523
*/
410-
filter = hisi_ptt_alloc_add_filter(hisi_ptt, pdev);
524+
filter = hisi_ptt_alloc_add_filter(hisi_ptt, PCI_DEVID(pdev->bus->number, pdev->devfn),
525+
pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT);
411526
if (!filter)
412527
return -ENOMEM;
413528

@@ -466,8 +581,13 @@ static int hisi_ptt_init_ctrls(struct hisi_ptt *hisi_ptt)
466581
int ret;
467582
u32 reg;
468583

584+
INIT_DELAYED_WORK(&hisi_ptt->work, hisi_ptt_update_filters);
585+
INIT_KFIFO(hisi_ptt->filter_update_kfifo);
586+
spin_lock_init(&hisi_ptt->filter_update_lock);
587+
469588
INIT_LIST_HEAD(&hisi_ptt->port_filters);
470589
INIT_LIST_HEAD(&hisi_ptt->req_filters);
590+
mutex_init(&hisi_ptt->filter_lock);
471591

472592
ret = hisi_ptt_config_trace_buf(hisi_ptt);
473593
if (ret)
@@ -620,6 +740,7 @@ static int hisi_ptt_trace_valid_filter(struct hisi_ptt *hisi_ptt, u64 config)
620740
{
621741
unsigned long val, port_mask = hisi_ptt->port_mask;
622742
struct hisi_ptt_filter_desc *filter;
743+
int ret = 0;
623744

624745
hisi_ptt->trace_ctrl.is_port = FIELD_GET(HISI_PTT_PMU_FILTER_IS_PORT, config);
625746
val = FIELD_GET(HISI_PTT_PMU_FILTER_VAL_MASK, config);
@@ -633,16 +754,20 @@ static int hisi_ptt_trace_valid_filter(struct hisi_ptt *hisi_ptt, u64 config)
633754
* For Requester ID filters, walk the available filter list to see
634755
* whether we have one matched.
635756
*/
757+
mutex_lock(&hisi_ptt->filter_lock);
636758
if (!hisi_ptt->trace_ctrl.is_port) {
637759
list_for_each_entry(filter, &hisi_ptt->req_filters, list) {
638760
if (val == hisi_ptt_get_filter_val(filter->devid, filter->is_port))
639-
return 0;
761+
goto out;
640762
}
641763
} else if (bitmap_subset(&val, &port_mask, BITS_PER_LONG)) {
642-
return 0;
764+
goto out;
643765
}
644766

645-
return -EINVAL;
767+
ret = -EINVAL;
768+
out:
769+
mutex_unlock(&hisi_ptt->filter_lock);
770+
return ret;
646771
}
647772

648773
static void hisi_ptt_pmu_init_configs(struct hisi_ptt *hisi_ptt, struct perf_event *event)
@@ -916,6 +1041,31 @@ static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt)
9161041
&hisi_ptt->hisi_ptt_pmu);
9171042
}
9181043

1044+
static void hisi_ptt_unregister_filter_update_notifier(void *data)
1045+
{
1046+
struct hisi_ptt *hisi_ptt = data;
1047+
1048+
bus_unregister_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb);
1049+
1050+
/* Cancel any work that has been queued */
1051+
cancel_delayed_work_sync(&hisi_ptt->work);
1052+
}
1053+
1054+
/* Register the bus notifier for dynamically updating the filter list */
1055+
static int hisi_ptt_register_filter_update_notifier(struct hisi_ptt *hisi_ptt)
1056+
{
1057+
int ret;
1058+
1059+
hisi_ptt->hisi_ptt_nb.notifier_call = hisi_ptt_notifier_call;
1060+
ret = bus_register_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb);
1061+
if (ret)
1062+
return ret;
1063+
1064+
return devm_add_action_or_reset(&hisi_ptt->pdev->dev,
1065+
hisi_ptt_unregister_filter_update_notifier,
1066+
hisi_ptt);
1067+
}
1068+
9191069
/*
9201070
* The DMA of PTT trace can only use direct mappings due to some
9211071
* hardware restriction. Check whether there is no IOMMU or the
@@ -987,6 +1137,10 @@ static int hisi_ptt_probe(struct pci_dev *pdev,
9871137
return ret;
9881138
}
9891139

1140+
ret = hisi_ptt_register_filter_update_notifier(hisi_ptt);
1141+
if (ret)
1142+
pci_warn(pdev, "failed to register filter update notifier, ret = %d", ret);
1143+
9901144
ret = hisi_ptt_register_pmu(hisi_ptt);
9911145
if (ret) {
9921146
pci_err(pdev, "failed to register PMU device, ret = %d", ret);

drivers/hwtracing/ptt/hisi_ptt.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111

1212
#include <linux/bits.h>
1313
#include <linux/cpumask.h>
14+
#include <linux/kfifo.h>
1415
#include <linux/list.h>
1516
#include <linux/mutex.h>
17+
#include <linux/notifier.h>
1618
#include <linux/pci.h>
1719
#include <linux/perf_event.h>
1820
#include <linux/spinlock.h>
1921
#include <linux/types.h>
22+
#include <linux/workqueue.h>
2023

2124
#define DRV_NAME "hisi_ptt"
2225

@@ -71,6 +74,11 @@
7174
#define HISI_PTT_WAIT_TRACE_TIMEOUT_US 100UL
7275
#define HISI_PTT_WAIT_POLL_INTERVAL_US 10UL
7376

77+
/* FIFO size for dynamically updating the PTT trace filter list. */
78+
#define HISI_PTT_FILTER_UPDATE_FIFO_SIZE 16
79+
/* Delay time for filter updating work */
80+
#define HISI_PTT_WORK_DELAY_MS 100UL
81+
7482
#define HISI_PCIE_CORE_PORT_ID(devfn) ((PCI_SLOT(devfn) & 0x7) << 1)
7583

7684
/* Definition of the PMU configs */
@@ -135,11 +143,25 @@ struct hisi_ptt_trace_ctrl {
135143
* struct hisi_ptt_filter_desc - Descriptor of the PTT trace filter
136144
* @list: entry of this descriptor in the filter list
137145
* @is_port: the PCI device of the filter is a Root Port or not
146+
* @name: name of this filter, same as the name of the related PCI device
138147
* @devid: the PCI device's devid of the filter
139148
*/
140149
struct hisi_ptt_filter_desc {
141150
struct list_head list;
142151
bool is_port;
152+
char *name;
153+
u16 devid;
154+
};
155+
156+
/**
157+
* struct hisi_ptt_filter_update_info - Information for PTT filter updating
158+
* @is_port: the PCI device to update is a Root Port or not
159+
* @is_add: adding to the filter or not
160+
* @devid: the PCI device's devid of the filter
161+
*/
162+
struct hisi_ptt_filter_update_info {
163+
bool is_port;
164+
bool is_add;
143165
u16 devid;
144166
};
145167

@@ -160,6 +182,7 @@ struct hisi_ptt_pmu_buf {
160182
/**
161183
* struct hisi_ptt - Per PTT device data
162184
* @trace_ctrl: the control information of PTT trace
185+
* @hisi_ptt_nb: dynamic filter update notifier
163186
* @hotplug_node: node for register cpu hotplug event
164187
* @hisi_ptt_pmu: the pum device of trace
165188
* @iobase: base IO address of the device
@@ -170,10 +193,15 @@ struct hisi_ptt_pmu_buf {
170193
* @lower_bdf: the lower BDF range of the PCI devices managed by this PTT device
171194
* @port_filters: the filter list of root ports
172195
* @req_filters: the filter list of requester ID
196+
* @filter_lock: lock to protect the filters
173197
* @port_mask: port mask of the managed root ports
198+
* @work: delayed work for filter updating
199+
* @filter_update_lock: spinlock to protect the filter update fifo
200+
* @filter_update_fifo: fifo of the filters waiting to update the filter list
174201
*/
175202
struct hisi_ptt {
176203
struct hisi_ptt_trace_ctrl trace_ctrl;
204+
struct notifier_block hisi_ptt_nb;
177205
struct hlist_node hotplug_node;
178206
struct pmu hisi_ptt_pmu;
179207
void __iomem *iobase;
@@ -192,7 +220,19 @@ struct hisi_ptt {
192220
*/
193221
struct list_head port_filters;
194222
struct list_head req_filters;
223+
struct mutex filter_lock;
195224
u16 port_mask;
225+
226+
/*
227+
* We use a delayed work here to avoid indefinitely waiting for
228+
* the hisi_ptt->mutex which protecting the filter list. The
229+
* work will be delayed only if the mutex can not be held,
230+
* otherwise no delay will be applied.
231+
*/
232+
struct delayed_work work;
233+
spinlock_t filter_update_lock;
234+
DECLARE_KFIFO(filter_update_kfifo, struct hisi_ptt_filter_update_info,
235+
HISI_PTT_FILTER_UPDATE_FIFO_SIZE);
196236
};
197237

198238
#define to_hisi_ptt(pmu) container_of(pmu, struct hisi_ptt, hisi_ptt_pmu)

0 commit comments

Comments
 (0)