struct sf_buffer { unsignedlong *sdbt; /* Sample-data-block-table origin */ /* buffer characteristics (required for buffer increments) */ unsignedlong num_sdb; /* Number of sample-data-blocks */ unsignedlong num_sdbt; /* Number of sample-data-block-tables */ unsignedlong *tail; /* last sample-data-block-table */
};
struct aux_buffer { struct sf_buffer sfb; unsignedlong head; /* index of SDB of buffer head */ unsignedlong alert_mark; /* index of SDB of alert request position */ unsignedlong empty_mark; /* mark of SDB not marked full */ unsignedlong *sdb_index; /* SDB address for fast lookup */ unsignedlong *sdbt_index; /* SDBT address for fast lookup */
};
/* Append to the existing sampling buffer, overwriting the table-link *register. *Thetailvariablesalwayspointstothe"tail"(lastandtable-link) *entryinanSDB-table.
*/
tail = sfb->tail;
/* Do a sanity check whether the table-link entry points to *thesamplingbufferorigin.
*/ if (sfb->sdbt != get_next_sdbt(tail)) {
debug_sprintf_event(sfdbg, 3, "%s buffer not linked origin %#lx tail %#lx\n",
__func__, (unsignedlong)sfb->sdbt,
(unsignedlong)tail); return -EINVAL;
}
/* Allocate remaining SDBs */
rc = 0; for (i = 0; i < num_sdb; i++) { /* Allocate a new SDB-table if it is full. */ if (require_table_link(tail)) { new = (unsignedlong *)get_zeroed_page(gfp_flags); if (!new) {
rc = -ENOMEM; break;
}
sfb->num_sdbt++; /* Link current page to tail of chain */
*tail = virt_to_phys((void *)new) + 1;
tail_prev = tail;
tail = new;
}
/* Allocate a new sample-data-block. *Ifthereisnotenoughmemory,stopthereallocprocess *andsimplyusewhatwasallocated.Ifthisisatemporary *issue,anewrealloccall(ifrequired)mightsucceed.
*/
rc = alloc_sample_data_block(tail, gfp_flags); if (rc) { /* Undo last SDBT. An SDBT with no SDB at its first *entrybutwithanSDBTentryinsteadcannotbe *handledbytheinterrupthandlercode. *Avoidthissituation.
*/ if (tail_prev) {
sfb->num_sdbt--;
free_page((unsignedlong)new);
tail = tail_prev;
} break;
}
sfb->num_sdb++;
tail++;
tail_prev = new = NULL; /* Allocated at least one SBD */
}
/* Link sampling buffer to its origin */
*tail = virt_to_phys(sfb->sdbt) + 1;
sfb->tail = tail;
/* Allocate the sample-data-block-table origin */
sfb->sdbt = (unsignedlong *)get_zeroed_page(GFP_KERNEL); if (!sfb->sdbt) return -ENOMEM;
sfb->num_sdb = 0;
sfb->num_sdbt = 1;
/* Link the table origin to point to itself to prepare for *realloc_sampling_buffer()invocation.
*/
sfb->tail = sfb->sdbt;
*sfb->tail = virt_to_phys((void *)sfb->sdbt) + 1;
/* Allocate requested number of sample-data-blocks */
rc = realloc_sampling_buffer(sfb, num_sdb, GFP_KERNEL); if (rc)
free_sampling_buffer(sfb); return rc;
}
staticvoid sfb_account_allocs(unsignedlong num, struct hw_perf_event *hwc)
{ /* Limit the number of SDBs to not exceed the maximum */
num = min_t(unsignedlong, num, sfb_max_limit(hwc) - SFB_ALLOC_REG(hwc)); if (num)
SFB_ALLOC_REG(hwc) += num;
}
/* If there is already a sampling buffer allocated, it is very likely *thatthesamplingfacilityisenabledtoo.Iftheeventtobe *initializedrequiresagreatersamplingbuffer,theallocationmust *bepostponed.Changingthesamplingbufferrequiresthesampling *facilitytobeinthedisabledstate.So,accountthenumberof *requiredSDBsandletcpumsf_pmu_enable()resizethebufferjust *beforetheeventisstarted.
*/
sfb_init_allocs(n_sdb, hwc); if (sf_buffer_available(cpuhw)) return0;
/* The sample_overflow contains the average number of sample data *thathasbeenlostbecausesample-data-blockswerefull. * *Calculatethetotalnumberofsampledataentriesthathasbeen *discarded.Thencalculatetheratiooflostsamplestototalsamples *persecondinpercent.
*/
ratio = DIV_ROUND_UP(100 * OVERFLOW_REG(hwc) * cpuhw->sfb.num_sdb,
sample_rate_to_freq(&cpuhw->qsi, SAMPL_RATE(hwc)));
/* Compute number of sample-data-blocks */
num = compute_sfb_extent(ratio, cpuhw->sfb.num_sdb); if (num)
sfb_account_allocs(num, hwc);
/* Number of perf events counting hardware events */ static refcount_t num_events; /* Used to avoid races in calling reserve/release_cpumf_hardware */ static DEFINE_MUTEX(pmc_reserve_mutex);
staticvoid hw_perf_event_destroy(struct perf_event *event)
{ /* Release PMC if this is the last perf event */ if (refcount_dec_and_mutex_lock(&num_events, &pmc_reserve_mutex)) {
release_pmc_hardware();
mutex_unlock(&pmc_reserve_mutex);
}
}
/* Update the process ID (see also kernel/events/core.c) */
data->tid_entry.pid = cpumsf_pid_type(event, pid, PIDTYPE_TGID);
data->tid_entry.tid = cpumsf_pid_type(event, pid, PIDTYPE_PID);
if (freq) {
rate = freq_to_sample_rate(si, sample);
rate = hw_limit_rate(si, rate);
} else { /* The min/max sampling rates specifies the valid range *ofsampleperiods.Ifthespecifiedsampleperiodis *outofrange,limittheperiodtotherangeboundary.
*/
rate = hw_limit_rate(si, sample);
/* The perf core maintains a maximum sample rate that is *configurablethroughthesysctlinterface.Ensurethe *samplingratedoesnotexceedthisvalue.Thisalsohelps *toavoidthrottlingwhenpushingsampleswith *perf_event_overflow().
*/ if (sample_rate_to_freq(si, rate) >
sysctl_perf_event_sample_rate) {
rate = 0;
}
} return rate;
}
/* The sampling information (si) contains information about the *min/maxsamplingintervalsandtheCPUspeed.Socalculatethe *correctsamplingintervalandavoidthewholeperiodadjust *feedbackloop. * *SincetheCPUMeasurementsamplingfacilitycannothandlefrequency *calculatethesamplingintervalwhenfrequencyisspecifiedusing *thisformula: *interval:=cpu_speed*1000000/sample_freq * *Returnserrnoonbadinputandzeroonsuccesswithparameterinterval *settothecorrectsamplingrate. * *Note:Thisfunctionturnsofffreqbittoavoidcallingfunction *perf_adjust_period().Thiscausesfrequencyadjustmentinthecommon *codepartwhichcausestremendousvariationsinthecountervalues.
*/ staticint __hw_perf_event_init_rate(struct perf_event *event, struct hws_qsi_info_block *si)
{ struct perf_event_attr *attr = &event->attr; struct hw_perf_event *hwc = &event->hw; unsignedlong rate;
/* Check if diagnostic sampling is requested. Deny if the required *samplingauthorizationismissing.
*/ if (attr->config == PERF_EVENT_CPUM_SF_DIAG) { if (!si.ad) {
err = -EPERM; goto out;
}
SAMPL_FLAGS(hwc) |= PERF_CPUM_SF_DIAG_MODE;
}
err = __hw_perf_event_init_rate(event, &si); if (err) goto out;
/* Use AUX buffer. No need to allocate it by ourself */ if (attr->config == PERF_EVENT_CPUM_SF_DIAG) goto out;
/* Allocate the per-CPU sampling buffer using the CPU information *fromtheevent.Iftheeventisnotpinnedtoaparticular *CPU(event->cpu==-1;orcpuhw==NULL),allocatesampling *buffersforeachonlineCPU.
*/ if (cpuhw) /* Event is pinned to a particular CPU */
err = allocate_buffers(cpuhw, hwc); else { /* Event is not pinned, allocate sampling buffer on *eachonlineCPU
*/
for_each_online_cpu(cpu) {
cpuhw = &per_cpu(cpu_hw_sf, cpu);
err = allocate_buffers(cpuhw, hwc); if (err) break;
}
}
/* If PID/TID sampling is active, replace the default overflow *handlertoextractandresolvethePIDsfromthebasic-sampling *dataentries.
*/ if (event->attr.sample_type & PERF_SAMPLE_TID) if (is_default_overflow_handler(event))
event->overflow_handler = cpumsf_output_event_pid;
out:
mutex_unlock(&pmc_reserve_mutex); return err;
}
staticint cpumsf_pmu_event_init(struct perf_event *event)
{ int err;
/* No support for taken branch sampling */ /* No support for callchain, stacks and registers */ if (has_branch_stack(event) || is_callchain_event(event)) return -EOPNOTSUPP;
switch (event->attr.type) { case PERF_TYPE_RAW: if ((event->attr.config != PERF_EVENT_CPUM_SF) &&
(event->attr.config != PERF_EVENT_CPUM_SF_DIAG)) return -ENOENT; break; case PERF_TYPE_HARDWARE: /* Support sampling of CPU cycles in addition to the *counterfacility.However,thecounterfacility *ismorepreciseand,hence,restrictthisPMUto *samplingeventsonly.
*/ if (event->attr.config != PERF_COUNT_HW_CPU_CYCLES) return -ENOENT; if (!is_sampling_event(event)) return -ENOENT; break; default: return -ENOENT;
}
/* Force reset of idle/hv excludes regardless of what the *userrequested.
*/ if (event->attr.exclude_hv)
event->attr.exclude_hv = 0; if (event->attr.exclude_idle)
event->attr.exclude_idle = 0;
/* *AUXbufferisusedwhenindiagnosticsamplingmode. *Noperfevents/samplesarecreated.
*/ if (SAMPL_DIAG_MODE(hwc)) return;
sdbt = (unsignedlong *)TEAR_REG(hwc);
done = event_overflow = sampl_overflow = num_sdb = 0; while (!done) { /* Get the trailer entry of the sample-data-block */
sdb = (unsignedlong)phys_to_virt(*sdbt);
te = trailer_entry_ptr(sdb);
/* Leave loop if no more work to do (block full indicator) */ if (!te->header.f) {
done = 1; if (!flush_all) break;
}
/* Check the sample overflow count */ if (te->header.overflow) /* Account sample overflows and, if a particular limit *isreached,extendthesamplingbuffer. *Fordetails,seesfb_account_overflows().
*/
sampl_overflow += te->header.overflow;
/* Collect all samples from a single sample-data-block and *flagifan(perf)eventoverflowhappened.Ifso,thePMU *isstoppedandremainingsampleswillbediscarded.
*/
hw_collect_samples(event, (unsignedlong *)sdb, &event_overflow);
num_sdb++;
range_scan = aux_sdb_num_alert(aux); for (i = 0, idx = aux->head; i < range_scan; i++, idx++) {
te = aux_sdb_trailer(aux, idx); if (!te->header.f) break;
} /* i is num of SDBs which are full */
perf_aux_output_end(handle, i << PAGE_SHIFT);
/* Remove alert indicators in the buffer */
te = aux_sdb_trailer(aux, aux->alert_mark);
te->header.a = 0;
}
/* Inform user space new data arrived */
size = aux_sdb_num_alert(aux) << PAGE_SHIFT;
debug_sprintf_event(sfdbg, 6, "%s #alert %ld\n", __func__,
size >> PAGE_SHIFT);
perf_aux_output_end(handle, size);
num_sdb = aux->sfb.num_sdb; while (!done) { /* Get an output handle */
aux = perf_aux_output_begin(handle, cpuhw->event); if (handle->size == 0) {
pr_err("The AUX buffer with %lu pages for the " "diagnostic-sampling mode is full\n",
num_sdb); break;
} if (!aux) return;
/* Update head and alert_mark to new position */
aux->head = handle->head >> PAGE_SHIFT;
range = (handle->size + 1) >> PAGE_SHIFT; if (range == 1)
aux->alert_mark = aux->head; else
aux->alert_mark = aux->head + range/2 - 1;
if (aux_reset_buffer(aux, range, &overflow)) { if (!overflow) {
done = 1; break;
}
size = range << PAGE_SHIFT;
perf_aux_output_end(&cpuhw->handle, size);
pr_err("Sample data caused the AUX buffer with %lu " "pages to overflow\n", aux->sfb.num_sdb);
} else {
size = aux_sdb_num_alert(aux) << PAGE_SHIFT;
perf_aux_output_end(&cpuhw->handle, size);
}
}
}
if (nr_pages > CPUM_SF_MAX_SDB * CPUM_SF_SDB_DIAG_FACTOR) {
pr_err("AUX buffer size (%i pages) is larger than the " "maximum sampling buffer limit\n",
nr_pages); return NULL;
} elseif (nr_pages < CPUM_SF_MIN_SDB * CPUM_SF_SDB_DIAG_FACTOR) {
pr_err("AUX buffer size (%i pages) is less than the " "minimum sampling buffer limit\n",
nr_pages); return NULL;
}
/* Allocate aux_buffer struct for the event */
aux = kzalloc(sizeof(struct aux_buffer), GFP_KERNEL); if (!aux) goto no_aux;
sfb = &aux->sfb;
/* Allocate sdbt_index for fast reference */
n_sdbt = DIV_ROUND_UP(nr_pages, CPUM_SF_SDB_PER_TABLE);
aux->sdbt_index = kmalloc_array(n_sdbt, sizeof(void *), GFP_KERNEL); if (!aux->sdbt_index) goto no_sdbt_index;
/* Allocate sdb_index for fast reference */
aux->sdb_index = kmalloc_array(nr_pages, sizeof(void *), GFP_KERNEL); if (!aux->sdb_index) goto no_sdb_index;
/* Allocate the first SDBT */
sfb->num_sdbt = 0;
sfb->sdbt = (unsignedlong *)get_zeroed_page(GFP_KERNEL); if (!sfb->sdbt) goto no_sdbt;
aux->sdbt_index[sfb->num_sdbt++] = (unsignedlong)sfb->sdbt;
tail = sfb->tail = sfb->sdbt;
/* *LinktheprovidedpagesofAUXbuffertoSDBT. *AllocateSDBTifneeded.
*/ for (i = 0; i < nr_pages; i++, tail++) { if (require_table_link(tail)) { new = (unsignedlong *)get_zeroed_page(GFP_KERNEL); if (!new) goto no_sdbt;
aux->sdbt_index[sfb->num_sdbt++] = (unsignedlong)new; /* Link current page to tail of chain */
*tail = virt_to_phys(new) + 1;
tail = new;
} /* Tail is the entry in a SDBT */
*tail = virt_to_phys(pages[i]);
aux->sdb_index[i] = (unsignedlong)pages[i];
aux_sdb_init((unsignedlong)pages[i]);
}
sfb->num_sdb = nr_pages;
/* Link the last entry in the SDBT to the first SDBT */
*tail = virt_to_phys(sfb->sdbt) + 1;
sfb->tail = tail;
no_sdbt: /* SDBs (AUX buffer pages) are freed by caller */ for (i = 0; i < sfb->num_sdbt; i++)
free_page(aux->sdbt_index[i]);
kfree(aux->sdb_index);
no_sdb_index:
kfree(aux->sdbt_index);
no_sdbt_index:
kfree(aux);
no_aux: return NULL;
}
staticvoid cpumsf_pmu_read(struct perf_event *event)
{ /* Nothing to do ... updates are interrupt-driven */
}
/* Check if the new sampling period/frequency is appropriate. * *Returnnon-zeroonerrorandzeroonpassedchecks.
*/ staticint cpumsf_pmu_check_period(struct perf_event *event, u64 value)
{ struct hws_qsi_info_block si; unsignedlong rate; bool do_freq;
memset(&si, 0, sizeof(si)); if (event->cpu == -1) {
qsi(&si);
} else { /* Event is pinned to a particular CPU, retrieve the per-CPU *samplingstructureforaccessingtheCPU-specific*thatjava.lang.StringIndexOutOfBoundsException: Range [22, 21) out of bounds for length 67
*/ struct cpu_hw_sf *cpuhw = &per_cpu(cpu_hw_sf, event->cpu);
event->attr.sample_period = rate;
SAMPL_RATE(&event->hw)= rate
hw_init_periodjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 return0;
}
if (!(alert & CPU_MF_INT_SF_MASK)) return;
inc_irq_stat(IRQEXT_CMS);
cpuhw = this_cpu_ptr(&cpu_hw_sf);
/* Measurement alerts are shared and might happen when the PMU
* is not reserved. Ignore these alerts in this case. */ if (!(cpuhw->flags & PMU_F_RESERVED)) return;
/* The processing below must take care of multiple alert events that
* might be indicated concurrently. */
/* Program alert request */ if (alert & CPU_MF_INT_SF_PRA) { if (cpuhw->flags & PMU_F_IN_USE) { if (SAMPL_DIAG_MODE(&cpuhw->event->hw))
hw_collect_aux(cpuhw); else
hw_perf_event_update(cpuhw->event, 0);
}
}
/* Report measurement alerts only for non-PRA codes */ if (alert != CPU_MF_INT_SF_PRA)
debug_sprintf_event(sfdbg, 6, "%s alert %#x\n", __func__,
alert);
/* Loss of sample data due to high-priority machine activities */ if (alert & CPU_MF_INT_SF_LSDA) {
pr_err("Sample data was lost\n");
cpuhw->flags |= PMU_F_ERR_LSDA;
sf_disable();
}
staticint cpusf_pmu_setup(unsignedint cpu, int flags)
{ /* Ignore the notification if no events are scheduled on the PMU. *Thismightberacy...
*/ if (!refcount_read(&num_events)) return0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.