drivers/dma/idxd/perfmon.c

Source file repositories/reference/linux-study-clean/drivers/dma/idxd/perfmon.c

File Facts

System
Linux kernel
Corpus path
drivers/dma/idxd/perfmon.c
Extension
.c
Size
14368 bytes
Lines
563
Domain
Driver Families
Bucket
drivers/dma
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (idx < 0) {
			ret = idx;
			goto out;
		}
	}
out:
	kfree(fake_pmu);

	return ret;
}

static int perfmon_pmu_event_init(struct perf_event *event)
{
	struct idxd_device *idxd;
	int ret = 0;

	idxd = event_to_idxd(event);
	event->hw.idx = -1;

	if (event->attr.type != event->pmu->type)
		return -ENOENT;

	/* sampling not supported */
	if (event->attr.sample_period)
		return -EINVAL;

	if (event->cpu < 0)
		return -EINVAL;

	if (event->pmu != &idxd->idxd_pmu->pmu)
		return -EINVAL;

	event->hw.event_base = ioread64(PERFMON_TABLE_OFFSET(idxd));
	event->hw.config = event->attr.config;

	if (event->group_leader != event)
		 /* non-group events have themselves as leader */
		ret = perfmon_validate_group(idxd->idxd_pmu, event);

	return ret;
}

static inline u64 perfmon_pmu_read_counter(struct perf_event *event)
{
	struct hw_perf_event *hwc = &event->hw;
	struct idxd_device *idxd;
	int cntr = hwc->idx;

	idxd = event_to_idxd(event);

	return ioread64(CNTRDATA_REG(idxd, cntr));
}

static void perfmon_pmu_event_update(struct perf_event *event)
{
	struct idxd_device *idxd = event_to_idxd(event);
	u64 prev_raw_count, new_raw_count, delta, p, n;
	int shift = 64 - idxd->idxd_pmu->counter_width;
	struct hw_perf_event *hwc = &event->hw;

	prev_raw_count = local64_read(&hwc->prev_count);
	do {
		new_raw_count = perfmon_pmu_read_counter(event);
	} while (!local64_try_cmpxchg(&hwc->prev_count,
				      &prev_raw_count, new_raw_count));
	n = (new_raw_count << shift);
	p = (prev_raw_count << shift);

	delta = ((n - p) >> shift);

	local64_add(delta, &event->count);
}

void perfmon_counter_overflow(struct idxd_device *idxd)
{
	int i, n_counters, max_loop = OVERFLOW_SIZE;
	struct perf_event *event;
	unsigned long ovfstatus;

	n_counters = min(idxd->idxd_pmu->n_counters, OVERFLOW_SIZE);

	ovfstatus = ioread32(OVFSTATUS_REG(idxd));

	/*
	 * While updating overflowed counters, other counters behind
	 * them could overflow and be missed in a given pass.
	 * Normally this could happen at most n_counters times, but in
	 * theory a tiny counter width could result in continual
	 * overflows and endless looping.  max_loop provides a
	 * failsafe in that highly unlikely case.

Annotation

Implementation Notes