static int intel_pmu_hw_config()

in events/intel/core.c [3761:3910]


static int intel_pmu_hw_config(struct perf_event *event)
{
	int ret = x86_pmu_hw_config(event);

	if (ret)
		return ret;

	ret = intel_pmu_bts_config(event);
	if (ret)
		return ret;

	if (event->attr.precise_ip) {
		if ((event->attr.config & INTEL_ARCH_EVENT_MASK) == INTEL_FIXED_VLBR_EVENT)
			return -EINVAL;

		if (!(event->attr.freq || (event->attr.wakeup_events && !event->attr.watermark))) {
			event->hw.flags |= PERF_X86_EVENT_AUTO_RELOAD;
			if (!(event->attr.sample_type &
			      ~intel_pmu_large_pebs_flags(event))) {
				event->hw.flags |= PERF_X86_EVENT_LARGE_PEBS;
				event->attach_state |= PERF_ATTACH_SCHED_CB;
			}
		}
		if (x86_pmu.pebs_aliases)
			x86_pmu.pebs_aliases(event);

		if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN)
			event->attr.sample_type |= __PERF_SAMPLE_CALLCHAIN_EARLY;
	}

	if (needs_branch_stack(event)) {
		ret = intel_pmu_setup_lbr_filter(event);
		if (ret)
			return ret;
		event->attach_state |= PERF_ATTACH_SCHED_CB;

		/*
		 * BTS is set up earlier in this path, so don't account twice
		 */
		if (!unlikely(intel_pmu_has_bts(event))) {
			/* disallow lbr if conflicting events are present */
			if (x86_add_exclusive(x86_lbr_exclusive_lbr))
				return -EBUSY;

			event->destroy = hw_perf_lbr_event_destroy;
		}
	}

	if (event->attr.aux_output) {
		if (!event->attr.precise_ip)
			return -EINVAL;

		event->hw.flags |= PERF_X86_EVENT_PEBS_VIA_PT;
	}

	if ((event->attr.type == PERF_TYPE_HARDWARE) ||
	    (event->attr.type == PERF_TYPE_HW_CACHE))
		return 0;

	/*
	 * Config Topdown slots and metric events
	 *
	 * The slots event on Fixed Counter 3 can support sampling,
	 * which will be handled normally in x86_perf_event_update().
	 *
	 * Metric events don't support sampling and require being paired
	 * with a slots event as group leader. When the slots event
	 * is used in a metrics group, it too cannot support sampling.
	 */
	if (intel_pmu_has_cap(event, PERF_CAP_METRICS_IDX) && is_topdown_event(event)) {
		if (event->attr.config1 || event->attr.config2)
			return -EINVAL;

		/*
		 * The TopDown metrics events and slots event don't
		 * support any filters.
		 */
		if (event->attr.config & X86_ALL_EVENT_FLAGS)
			return -EINVAL;

		if (is_available_metric_event(event)) {
			struct perf_event *leader = event->group_leader;

			/* The metric events don't support sampling. */
			if (is_sampling_event(event))
				return -EINVAL;

			/* The metric events require a slots group leader. */
			if (!is_slots_event(leader))
				return -EINVAL;

			/*
			 * The leader/SLOTS must not be a sampling event for
			 * metric use; hardware requires it starts at 0 when used
			 * in conjunction with MSR_PERF_METRICS.
			 */
			if (is_sampling_event(leader))
				return -EINVAL;

			event->event_caps |= PERF_EV_CAP_SIBLING;
			/*
			 * Only once we have a METRICs sibling do we
			 * need TopDown magic.
			 */
			leader->hw.flags |= PERF_X86_EVENT_TOPDOWN;
			event->hw.flags  |= PERF_X86_EVENT_TOPDOWN;
		}
	}

	/*
	 * The load latency event X86_CONFIG(.event=0xcd, .umask=0x01) on SPR
	 * doesn't function quite right. As a work-around it needs to always be
	 * co-scheduled with a auxiliary event X86_CONFIG(.event=0x03, .umask=0x82).
	 * The actual count of this second event is irrelevant it just needs
	 * to be active to make the first event function correctly.
	 *
	 * In a group, the auxiliary event must be in front of the load latency
	 * event. The rule is to simplify the implementation of the check.
	 * That's because perf cannot have a complete group at the moment.
	 */
	if (require_mem_loads_aux_event(event) &&
	    (event->attr.sample_type & PERF_SAMPLE_DATA_SRC) &&
	    is_mem_loads_event(event)) {
		struct perf_event *leader = event->group_leader;
		struct perf_event *sibling = NULL;

		if (!is_mem_loads_aux_event(leader)) {
			for_each_sibling_event(sibling, leader) {
				if (is_mem_loads_aux_event(sibling))
					break;
			}
			if (list_entry_is_head(sibling, &leader->sibling_list, sibling_list))
				return -ENODATA;
		}
	}

	if (!(event->attr.config & ARCH_PERFMON_EVENTSEL_ANY))
		return 0;

	if (x86_pmu.version < 3)
		return -EINVAL;

	ret = perf_allow_cpu(&event->attr);
	if (ret)
		return ret;

	event->hw.config |= ARCH_PERFMON_EVENTSEL_ANY;

	return 0;
}