Received: by 2002:a05:6a10:2726:0:0:0:0 with SMTP id ib38csp1043193pxb; Wed, 6 Apr 2022 07:23:00 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwrq8+2E4QfjOZgAaSmD+nlNbJpz23DzEBzloL2AwuyeKzp/FbZ4Rev7ajK/fd/dL8b8fgu X-Received: by 2002:a62:e404:0:b0:4fd:b97b:8bc9 with SMTP id r4-20020a62e404000000b004fdb97b8bc9mr9226115pfh.48.1649254980053; Wed, 06 Apr 2022 07:23:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1649254980; cv=none; d=google.com; s=arc-20160816; b=CZUI7pYXXtNoSBXEM3swjF5y8fYUfa5Whhs3b9O01nwah7GvFZ9d6Du7iG3gI6Acbh gbqVo7R52vMHZ6EJlaw+TuU3Ahk3QdfnQp0Qgk5T6t0UV1kxUB978ubD8oohNGYE8Tf+ LNThoz8h3kQZxiKYvf0ySNRqrxNKrVJFEiMhaHfmaXp2CGre0xZMuFerzFhk7xgjdKxE aPlbeRS35lnquQd1Oa9pJnM/DADU5HXXkeyd+4YU0CY3bVhb2sm2mFTSgtF24VSihAXH kuFE1csbA0+5tTqUDEq3N0c+urvzbs9SVCXT4YeHks4V18VEbVDNkLZ5FkKI2AzDhCvF k/dA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=PNFBIjcZnJ2jQVem+4nZBZ0zkHFQQ6HgONGFv+jM+eU=; b=p842NFCk9J8ZLhSlrJzFKh+CUxNaC7BB/uITE0X+MVDX5rOfGrvwtOm8lECic78HgH 60imR+B9qLJG9oFEFygY3+0Yr0v1v5zX5K9gEDR7R/1uucenfyjBB9CJTgVuDLDse+PO TpE+SYr+1Z/+fVmbsIi4LzOLDWVLHrrFmAajBwTzEpHC6H5g6lDxdcCZB2wOhEXWgi6e xVOKqTY8LJ+zDTHN7Dfj/2oQnpuX1OvS2PLsWQS1BcKoIaYfl5/5Yc1VIHoRSCuIEIl/ fpOM374itsxEctjMknyA8x8pOQ6TRhvIpsGTS02CZs7N0MN4yjSoNO7mEwwGuHo3wdz4 TvSQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=hisilicon.com Return-Path: Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net. [2620:137:e000::1:18]) by mx.google.com with ESMTPS id p14-20020a056a000a0e00b004fdf6336020si10204145pfh.61.2022.04.06.07.22.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 06 Apr 2022 07:23:00 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) client-ip=2620:137:e000::1:18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=hisilicon.com Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id DC5D4496598; Wed, 6 Apr 2022 05:07:31 -0700 (PDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230285AbiDFLzk (ORCPT + 99 others); Wed, 6 Apr 2022 07:55:40 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34754 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230516AbiDFLzL (ORCPT ); Wed, 6 Apr 2022 07:55:11 -0400 Received: from szxga02-in.huawei.com (szxga02-in.huawei.com [45.249.212.188]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2514E35C048; Wed, 6 Apr 2022 00:19:04 -0700 (PDT) Received: from canpemm500009.china.huawei.com (unknown [172.30.72.53]) by szxga02-in.huawei.com (SkyGuard) with ESMTP id 4KYG6k06D3zgYQj; Wed, 6 Apr 2022 15:17:18 +0800 (CST) Received: from localhost.localdomain (10.67.164.66) by canpemm500009.china.huawei.com (7.192.105.203) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Wed, 6 Apr 2022 15:19:01 +0800 From: Yicong Yang To: , , , , , , , , , , , , , , , , , , , , , , , , CC: , , , , Subject: [PATCH v6 3/7] hisi_ptt: Add support for dynamically updating the filter list Date: Wed, 6 Apr 2022 15:17:26 +0800 Message-ID: <20220406071730.41031-4-yangyicong@hisilicon.com> X-Mailer: git-send-email 2.31.0 In-Reply-To: <20220406071730.41031-1-yangyicong@hisilicon.com> References: <20220406071730.41031-1-yangyicong@hisilicon.com> MIME-Version: 1.0 Content-Transfer-Encoding: 7BIT Content-Type: text/plain; charset=US-ASCII X-Originating-IP: [10.67.164.66] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To canpemm500009.china.huawei.com (7.192.105.203) X-CFilter-Loop: Reflected X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RDNS_NONE, SPF_HELO_NONE,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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. Signed-off-by: Yicong Yang Reviewed-by: Jonathan Cameron --- drivers/hwtracing/ptt/hisi_ptt.c | 159 ++++++++++++++++++++++++++++--- drivers/hwtracing/ptt/hisi_ptt.h | 34 +++++++ 2 files changed, 180 insertions(+), 13 deletions(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 3342774b6b14..266b0195ce8c 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -270,27 +270,121 @@ static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt) return 0; } -static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data) +static void hisi_ptt_update_filters(struct work_struct *work) { + struct delayed_work *delayed_work = to_delayed_work(work); + struct hisi_ptt_filter_update_info info; struct hisi_ptt_filter_desc *filter; - struct hisi_ptt *hisi_ptt = data; struct list_head *target_list; + struct hisi_ptt *hisi_ptt; - target_list = pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ? - &hisi_ptt->port_filters : &hisi_ptt->req_filters; + hisi_ptt = container_of(delayed_work, struct hisi_ptt, work); - filter = kzalloc(sizeof(*filter), GFP_KERNEL); - if (!filter) { - pci_err(hisi_ptt->pdev, "failed to add filter %s\n", pci_name(pdev)); - return -ENOMEM; + if (!mutex_trylock(&hisi_ptt->mutex)) { + schedule_delayed_work(&hisi_ptt->work, HISI_PTT_WORK_DELAY_MS); + return; } - filter->pdev = pdev; - list_add_tail(&filter->list, target_list); + while (kfifo_get(&hisi_ptt->filter_update_kfifo, &info)) { + bool is_port = pci_pcie_type(info.pdev) == PCI_EXP_TYPE_ROOT_PORT; + u16 val = hisi_ptt_get_filter_val(info.pdev); - /* Update the available port mask */ - if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) - hisi_ptt->port_mask |= hisi_ptt_get_filter_val(pdev); + target_list = is_port ? &hisi_ptt->port_filters : &hisi_ptt->req_filters; + + if (info.is_add) { + filter = kzalloc(sizeof(*filter), GFP_KERNEL); + if (!filter) { + pci_err(hisi_ptt->pdev, "failed to add filter %s\n", + pci_name(info.pdev)); + continue; + } + + filter->pdev = info.pdev; + list_add_tail(&filter->list, target_list); + } else { + list_for_each_entry(filter, target_list, list) + if (hisi_ptt_get_filter_val(filter->pdev) == val) { + list_del(&filter->list); + kfree(filter); + break; + } + } + + /* Update the available port mask */ + if (!is_port) + continue; + + if (info.is_add) + hisi_ptt->port_mask |= val; + else + hisi_ptt->port_mask &= ~val; + } + + mutex_unlock(&hisi_ptt->mutex); +} + +static void hisi_ptt_update_fifo_in(struct hisi_ptt *hisi_ptt, + struct hisi_ptt_filter_update_info *info) +{ + struct pci_dev *root_port = pcie_find_root_port(info->pdev); + u32 port_devid; + + if (!root_port) + return; + + port_devid = PCI_DEVID(root_port->bus->number, root_port->devfn); + if (port_devid < hisi_ptt->lower || + port_devid > hisi_ptt->upper) + return; + + if (kfifo_in_spinlocked(&hisi_ptt->filter_update_kfifo, info, 1, + &hisi_ptt->filter_update_lock)) + schedule_delayed_work(&hisi_ptt->work, 0); + else + pci_warn(hisi_ptt->pdev, + "filter update fifo overflow for target %s\n", + pci_name(info->pdev)); +} + +/* + * A PCI bus notifier is used here for dynamically updating the filter + * list. + */ +static int hisi_ptt_notifier_call(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct hisi_ptt *hisi_ptt = container_of(nb, struct hisi_ptt, hisi_ptt_nb); + struct hisi_ptt_filter_update_info info; + struct device *dev = data; + struct pci_dev *pdev = to_pci_dev(dev); + + info.pdev = pdev; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + info.is_add = true; + break; + case BUS_NOTIFY_DEL_DEVICE: + info.is_add = false; + break; + default: + return 0; + } + + hisi_ptt_update_fifo_in(hisi_ptt, &info); + + return 0; +} + +static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data) +{ + struct hisi_ptt_filter_update_info info = { + .pdev = pdev, + .is_add = true, + }; + struct hisi_ptt *hisi_ptt = data; + + hisi_ptt_update_fifo_in(hisi_ptt, &info); return 0; } @@ -318,6 +412,9 @@ static int hisi_ptt_init_ctrls(struct hisi_ptt *hisi_ptt) int ret; u32 reg; + INIT_DELAYED_WORK(&hisi_ptt->work, hisi_ptt_update_filters); + spin_lock_init(&hisi_ptt->filter_update_lock); + INIT_KFIFO(hisi_ptt->filter_update_kfifo); INIT_LIST_HEAD(&hisi_ptt->port_filters); INIT_LIST_HEAD(&hisi_ptt->req_filters); @@ -334,6 +431,13 @@ static int hisi_ptt_init_ctrls(struct hisi_ptt *hisi_ptt) hisi_ptt->upper = FIELD_GET(HISI_PTT_DEVICE_RANGE_UPPER, reg); hisi_ptt->lower = FIELD_GET(HISI_PTT_DEVICE_RANGE_LOWER, reg); + /* + * No need to fail if the bus is NULL here as the device + * maybe hotplugged after the PTT driver probe, in which + * case we can detect the event and update the list as + * we register a bus notifier for dynamically updating + * the filter list. + */ bus = pci_find_bus(pci_domain_nr(pdev->bus), PCI_BUS_NUM(hisi_ptt->upper)); if (bus) pci_walk_bus(bus, hisi_ptt_init_filters, hisi_ptt); @@ -760,6 +864,31 @@ static int hisi_ptt_register_pmu(struct hisi_ptt *hisi_ptt) return perf_pmu_register(&hisi_ptt->hisi_ptt_pmu, pmu_name, -1); } +static void hisi_ptt_unregister_filter_update_notifier(void *data) +{ + struct hisi_ptt *hisi_ptt = data; + + bus_unregister_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb); + + /* Cancel any work that has been queued */ + cancel_delayed_work_sync(&hisi_ptt->work); +} + +/* Register the bus notifier for dynamically updating the filter list */ +static int hisi_ptt_register_filter_update_notifier(struct hisi_ptt *hisi_ptt) +{ + int ret; + + hisi_ptt->hisi_ptt_nb.notifier_call = hisi_ptt_notifier_call; + ret = bus_register_notifier(&pci_bus_type, &hisi_ptt->hisi_ptt_nb); + if (ret) + return ret; + + return devm_add_action_or_reset(&hisi_ptt->pdev->dev, + hisi_ptt_unregister_filter_update_notifier, + hisi_ptt); +} + /* * The DMA of PTT trace can only use direct mapping, due to some * hardware restriction. Check whether there is an IOMMU or the @@ -832,6 +961,10 @@ static int hisi_ptt_probe(struct pci_dev *pdev, return ret; } + ret = hisi_ptt_register_filter_update_notifier(hisi_ptt); + if (ret) + pci_warn(pdev, "failed to register filter update notifier, ret = %d", ret); + ret = hisi_ptt_register_pmu(hisi_ptt); if (ret) { pci_err(pdev, "failed to register PMU device, ret = %d", ret); diff --git a/drivers/hwtracing/ptt/hisi_ptt.h b/drivers/hwtracing/ptt/hisi_ptt.h index 29b6d6293d6c..53805f6c9bc2 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.h +++ b/drivers/hwtracing/ptt/hisi_ptt.h @@ -10,11 +10,15 @@ #define _HISI_PTT_H #include +#include #include #include +#include #include #include +#include #include +#include #define DRV_NAME "hisi_ptt" @@ -57,6 +61,12 @@ /* Wait time for hardware DMA to reset */ #define HISI_PTT_RESET_TIMEOUT_US 10UL #define HISI_PTT_RESET_POLL_INTERVAL_US 1UL +/* FIFO size for dynamically updating the PTT trace filter list. */ +#define HISI_PTT_FILTER_UPDATE_FIFO_SIZE 16 +/* Delay time for filter updating work */ +#define HISI_PTT_WORK_DELAY_MS 100UL +/* Wait time for DMA hardware to reset */ +#define HISI_PTT_RESET_WAIT_MS 1000UL /* Poll timeout and interval for waiting hardware work to finish */ #define HISI_PTT_WAIT_TRACE_TIMEOUT_US 100UL #define HISI_PTT_WAIT_POLL_INTERVAL_US 10UL @@ -112,6 +122,15 @@ struct hisi_ptt_filter_desc { struct pci_dev *pdev; }; +/** + * struct hisi_ptt_filter_update_info - information for PTT filter updating + * @pdev: the PCI device to update in the filter list + * @is_add: adding to the filter or not + */ +struct hisi_ptt_filter_update_info { + struct pci_dev *pdev; + bool is_add; +}; /** * struct hisi_ptt_pmu_buf - descriptor of the AUX buffer of PTT trace @@ -139,9 +158,13 @@ struct hisi_ptt_pmu_buf { * @port_filters: the filter list of root ports * @req_filters: the filter list of requester ID * @port_mask: port mask of the managed root ports + * @work: delayed work for filter updating + * @filter_update_lock: spinlock to protect the filter update fifo + * @filter_update_fifo: fifo of the filters waiting to update the filter list */ struct hisi_ptt { struct hisi_ptt_trace_ctrl trace_ctrl; + struct notifier_block hisi_ptt_nb; struct pmu hisi_ptt_pmu; void __iomem *iobase; struct pci_dev *pdev; @@ -159,6 +182,17 @@ struct hisi_ptt { struct list_head port_filters; struct list_head req_filters; u16 port_mask; + + /* + * We use a delayed work here to avoid indefinitely waiting for + * the hisi_ptt->mutex which protecting the filter list. The + * work will be delayed only if the mutex can not be held, + * otherwise no delay will be applied. + */ + struct delayed_work work; + spinlock_t filter_update_lock; + DECLARE_KFIFO(filter_update_kfifo, struct hisi_ptt_filter_update_info, + HISI_PTT_FILTER_UPDATE_FIFO_SIZE); }; #define to_hisi_ptt(pmu) container_of(pmu, struct hisi_ptt, hisi_ptt_pmu) -- 2.24.0