Unverified Commit f07013f9 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!5972 Perf-related bugfix

Merge Pull Request from: @xia-bing1 
 
This series mainly fix and optimize the several usage of the driver:
- Add more events to complement the TLP bandwidth counting
- Fix the wrong counting on using the event group
- Properly check the target filter to avoid invalid filter value
- Optimize the handling of related events which are not in an event group
- Update the docs
- Enable HiSilicon Erratum 162700402 quirk for HIP09

https://gitee.com/openeuler/kernel/issues/I96O7N 
 
Link:https://gitee.com/openeuler/kernel/pulls/5972

 

Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parents 9c56bd59 d9bbf3a2
Loading
Loading
Loading
Loading
+24 −8
Original line number Diff line number Diff line
@@ -37,9 +37,21 @@ Example usage of perf::
  hisi_pcie0_core0/rx_mwr_cnt/ [kernel PMU event]
  ------------------------------------------

  $# perf stat -e hisi_pcie0_core0/rx_mwr_latency/
  $# perf stat -e hisi_pcie0_core0/rx_mwr_cnt/
  $# perf stat -g -e hisi_pcie0_core0/rx_mwr_latency/ -e hisi_pcie0_core0/rx_mwr_cnt/
  $# perf stat -e hisi_pcie0_core0/rx_mwr_latency,port=0xffff/
  $# perf stat -e hisi_pcie0_core0/rx_mwr_cnt,port=0xffff/

The related events usually used to calculate the bandwidth, latency or others.
They need to start and end counting at the same time, therefore related events
are best used in the same event group to get the expected value. There are two
ways to know if they are related events:

a) By event name, such as the latency events "xxx_latency, xxx_cnt" or
   bandwidth events "xxx_flux, xxx_time".
b) By event type, such as "event=0xXXXX, event=0x1XXXX".

Example usage of perf group::

  $# perf stat -e "{hisi_pcie0_core0/rx_mwr_latency,port=0xffff/,hisi_pcie0_core0/rx_mwr_cnt,port=0xffff/}"

The current driver does not support sampling. So "perf record" is unsupported.
Also attach to a task is unsupported for PCIe PMU.
@@ -51,8 +63,12 @@ Filter options

   PMU could only monitor the performance of traffic downstream target Root
   Ports or downstream target Endpoint. PCIe PMU driver support "port" and
   "bdf" interfaces for users, and these two interfaces aren't supported at the
   same time.
   "bdf" interfaces for users.
   Please notice that, one of these two interfaces must be set, and these two
   interfaces aren't supported at the same time. If they are both set, only
   "port" filter is valid.
   If "port" filter not being set or is set explicitly to zero (default), the
   "bdf" filter will be in effect, because "bdf=0" meaning 0000:000:00.0.

   - port

@@ -95,7 +111,7 @@ Filter options

   Example usage of perf::

     $# perf stat -e hisi_pcie0_core0/rx_mrd_flux,trig_len=0x4,trig_mode=1/ sleep 5
     $# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port=0xffff,trig_len=0x4,trig_mode=1/ sleep 5

3. Threshold filter

@@ -109,7 +125,7 @@ Filter options

   Example usage of perf::

     $# perf stat -e hisi_pcie0_core0/rx_mrd_flux,thr_len=0x4,thr_mode=1/ sleep 5
     $# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port=0xffff,thr_len=0x4,thr_mode=1/ sleep 5

4. TLP Length filter

@@ -127,4 +143,4 @@ Filter options

   Example usage of perf::

     $# perf stat -e hisi_pcie0_core0/rx_mrd_flux,len_mode=0x1/ sleep 5
     $# perf stat -e hisi_pcie0_core0/rx_mrd_flux,port=0xffff,len_mode=0x1/ sleep 5
+53 −49
Original line number Diff line number Diff line
@@ -216,10 +216,8 @@ static void hisi_pcie_pmu_writeq(struct hisi_pcie_pmu *pcie_pmu, u32 reg_offset,
	writeq_relaxed(val, pcie_pmu->base + offset);
}

static void hisi_pcie_pmu_config_filter(struct perf_event *event)
static u64 hisi_pcie_pmu_get_event_ctrl_val(struct perf_event *event)
{
	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
	struct hw_perf_event *hwc = &event->hw;
	u64 port, trig_len, thr_len, len_mode;
	u64 reg = HISI_PCIE_INIT_SET;

@@ -256,10 +254,19 @@ static void hisi_pcie_pmu_config_filter(struct perf_event *event)
	else
		reg |= FIELD_PREP(HISI_PCIE_LEN_M, HISI_PCIE_LEN_M_DEFAULT);

	return reg;
}

static void hisi_pcie_pmu_config_event_ctrl(struct perf_event *event)
{
	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
	struct hw_perf_event *hwc = &event->hw;
	u64 reg = hisi_pcie_pmu_get_event_ctrl_val(event);

	hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EVENT_CTRL, hwc->idx, reg);
}

static void hisi_pcie_pmu_clear_filter(struct perf_event *event)
static void hisi_pcie_pmu_clear_event_ctrl(struct perf_event *event)
{
	struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
	struct hw_perf_event *hwc = &event->hw;
@@ -299,18 +306,24 @@ static bool hisi_pcie_pmu_valid_filter(struct perf_event *event,
	if (hisi_pcie_get_trig_len(event) > HISI_PCIE_TRIG_MAX_VAL)
		return false;

	if (requester_id) {
		if (!hisi_pcie_pmu_valid_requester_id(pcie_pmu, requester_id))
	/* Need to explicitly set filter of "port" or "bdf" */
	if (!hisi_pcie_get_port(event) &&
	    !hisi_pcie_pmu_valid_requester_id(pcie_pmu, requester_id))
		return false;
	}

	return true;
}

/*
 * Check Whether two events share the same config. The same config means not
 * only the event code, but also the filter settings of the two events are
 * the same.
 */
static bool hisi_pcie_pmu_cmp_event(struct perf_event *target,
					struct perf_event *event)
{
	return hisi_pcie_get_real_event(target) == hisi_pcie_get_real_event(event);
	return hisi_pcie_pmu_get_event_ctrl_val(target) ==
	       hisi_pcie_pmu_get_event_ctrl_val(event);
}

static bool hisi_pcie_pmu_validate_event_group(struct perf_event *event)
@@ -385,40 +398,32 @@ static u64 hisi_pcie_pmu_read_counter(struct perf_event *event)
	return hisi_pcie_pmu_readq(pcie_pmu, event->hw.event_base, idx);
}

static int hisi_pcie_pmu_find_related_event(struct hisi_pcie_pmu *pcie_pmu,
/*
 * Check all work events, if a relevant event is found then we return it
 * first, otherwise return the first idle counter (need to reset).
 */
static int hisi_pcie_pmu_get_event_idx(struct hisi_pcie_pmu *pcie_pmu,
					struct perf_event *event)
{
	int first_idle = -EAGAIN;
	struct perf_event *sibling;
	int idx;

	for (idx = 0; idx < HISI_PCIE_MAX_COUNTERS; idx++) {
		sibling = pcie_pmu->hw_events[idx];
		if (!sibling)
			continue;

		if (!hisi_pcie_pmu_cmp_event(sibling, event))
		if (!sibling) {
			if (first_idle == -EAGAIN)
				first_idle = idx;
			continue;

		/* Related events must be used in group */
		if (sibling->group_leader == event->group_leader)
			return idx;
		else
			return -EINVAL;
		}

		/* Related events must be used in group */
		if (hisi_pcie_pmu_cmp_event(sibling, event) &&
		    sibling->group_leader == event->group_leader)
			return idx;
	}

static int hisi_pcie_pmu_get_event_idx(struct hisi_pcie_pmu *pcie_pmu)
{
	int idx;

	for (idx = 0; idx < HISI_PCIE_MAX_COUNTERS; idx++) {
		if (!pcie_pmu->hw_events[idx])
			return idx;
	}

	return -EINVAL;
	return first_idle;
}

static void hisi_pcie_pmu_event_update(struct perf_event *event)
@@ -505,7 +510,7 @@ static void hisi_pcie_pmu_start(struct perf_event *event, int flags)
	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
	hwc->state = 0;

	hisi_pcie_pmu_config_filter(event);
	hisi_pcie_pmu_config_event_ctrl(event);
	hisi_pcie_pmu_enable_counter(pcie_pmu, hwc);
	hisi_pcie_pmu_enable_int(pcie_pmu, hwc);
	hisi_pcie_pmu_set_period(event);
@@ -526,7 +531,7 @@ static void hisi_pcie_pmu_stop(struct perf_event *event, int flags)
	hisi_pcie_pmu_event_update(event);
	hisi_pcie_pmu_disable_int(pcie_pmu, hwc);
	hisi_pcie_pmu_disable_counter(pcie_pmu, hwc);
	hisi_pcie_pmu_clear_filter(event);
	hisi_pcie_pmu_clear_event_ctrl(event);
	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
	hwc->state |= PERF_HES_STOPPED;

@@ -544,27 +549,18 @@ static int hisi_pcie_pmu_add(struct perf_event *event, int flags)

	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;

	/* Check all working events to find a related event. */
	idx = hisi_pcie_pmu_find_related_event(pcie_pmu, event);
	idx = hisi_pcie_pmu_get_event_idx(pcie_pmu, event);
	if (idx < 0)
		return idx;

	/* Current event shares an enabled counter with the related event */
	if (idx < HISI_PCIE_MAX_COUNTERS) {
	hwc->idx = idx;
		goto start_count;
	}

	idx = hisi_pcie_pmu_get_event_idx(pcie_pmu);
	if (idx < 0)
		return idx;

	hwc->idx = idx;
	pcie_pmu->hw_events[idx] = event;
	/* Reset Counter to avoid previous statistic interference. */
	/* No enabled counter found with related event, reset it */
	if (!pcie_pmu->hw_events[idx]) {
		hisi_pcie_pmu_reset_counter(pcie_pmu, idx);
		pcie_pmu->hw_events[idx] = event;
	}

start_count:
	if (flags & PERF_EF_START)
		hisi_pcie_pmu_start(event, PERF_EF_RELOAD);

@@ -714,10 +710,18 @@ static struct attribute *hisi_pcie_pmu_events_attr[] = {
	HISI_PCIE_PMU_EVENT_ATTR(rx_mrd_cnt, 0x10210),
	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_latency, 0x0011),
	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_cnt, 0x10011),
	HISI_PCIE_PMU_EVENT_ATTR(rx_mwr_flux, 0x0104),
	HISI_PCIE_PMU_EVENT_ATTR(rx_mwr_time, 0x10104),
	HISI_PCIE_PMU_EVENT_ATTR(rx_mrd_flux, 0x0804),
	HISI_PCIE_PMU_EVENT_ATTR(rx_mrd_time, 0x10804),
	HISI_PCIE_PMU_EVENT_ATTR(rx_cpl_flux, 0x2004),
	HISI_PCIE_PMU_EVENT_ATTR(rx_cpl_time, 0x12004),
	HISI_PCIE_PMU_EVENT_ATTR(tx_mwr_flux, 0x0105),
	HISI_PCIE_PMU_EVENT_ATTR(tx_mwr_time, 0x10105),
	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_flux, 0x0405),
	HISI_PCIE_PMU_EVENT_ATTR(tx_mrd_time, 0x10405),
	HISI_PCIE_PMU_EVENT_ATTR(tx_cpl_flux, 0x1005),
	HISI_PCIE_PMU_EVENT_ATTR(tx_cpl_time, 0x11005),
	NULL
};

+41 −1
Original line number Diff line number Diff line
@@ -287,12 +287,52 @@ static u64 hisi_uc_pmu_read_counter(struct hisi_pmu *uc_pmu,
	return readq(uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx));
}

static void hisi_uc_pmu_write_counter(struct hisi_pmu *uc_pmu,
static bool hisi_uc_pmu_get_glb_en_state(struct hisi_pmu *uc_pmu)
{
	u32 val;

	val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG);
	return !!FIELD_GET(HISI_UC_EVENT_GLB_EN, val);
}

static void hisi_uc_pmu_write_counter_normal(struct hisi_pmu *uc_pmu,
				      struct hw_perf_event *hwc, u64 val)
{
	writeq(val, uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx));
}

static void hisi_uc_pmu_write_counter_quirk_v2(struct hisi_pmu *uc_pmu,
				      struct hw_perf_event *hwc, u64 val)
{
	hisi_uc_pmu_start_counters(uc_pmu);
	hisi_uc_pmu_write_counter_normal(uc_pmu, hwc, val);
	hisi_uc_pmu_stop_counters(uc_pmu);
}

static void hisi_uc_pmu_write_counter(struct hisi_pmu *uc_pmu,
				      struct hw_perf_event *hwc, u64 val)
{
	bool enable = hisi_uc_pmu_get_glb_en_state(uc_pmu);
	bool erratum = uc_pmu->identifier == HISI_PMU_V2;

	/*
	 * HiSilicon UC PMU v2 suffers the erratum 162700402 that the
	 * PMU counter cannot be set due to the lack of clock under power
	 * saving mode. This will lead to error or inaccurate counts.
	 * The clock can be enabled by the PMU global enabling control.
	 * The irq handler and pmu_start() will call the function to set
	 * period. If the function under irq context, the PMU has been
	 * enabled therefore we set counter directly. Other situations
	 * the PMU is disabled, we need to enable it to turn on the
	 * counter clock to set period, and then restore PMU enable
	 * status, the counter can hold its value without a clock.
	 */
	if (enable || !erratum)
		hisi_uc_pmu_write_counter_normal(uc_pmu, hwc, val);
	else
		hisi_uc_pmu_write_counter_quirk_v2(uc_pmu, hwc, val);
}

static void hisi_uc_pmu_enable_counter_int(struct hisi_pmu *uc_pmu,
					   struct hw_perf_event *hwc)
{