mirror of
https://github.com/torvalds/linux.git
synced 2024-12-07 19:41:31 +00:00
s390/cpum_sf: sampling buffer setup to handle virtual addresses
The CPU Measurement Sampling Facility (CPUM_SF) installs large buffers to save samples collected by hardware. These buffers are organized as Sample Data Buffer Tables (SDBT) and Sample Data Buffers (SDB). SDBs contain the samples which are extracted and saved in the perf ring buffer. The SDBTs are chained using real addresses and refer to SDBs using real addresses. Adds proper virtual to phyiscal address translation to the buffer chaining. The current constraint which requires virtual equals real address layout is removed. Signed-off-by: Thomas Richter <tmricht@linux.ibm.com> Acked-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
This commit is contained in:
parent
4012fc20e2
commit
1f8e50722f
@ -125,7 +125,7 @@ static inline unsigned long long trailer_timestamp(struct hws_trailer_entry *te)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Return pointer to trailer entry of an sample data block */
|
/* Return pointer to trailer entry of an sample data block */
|
||||||
static inline unsigned long *trailer_entry_ptr(unsigned long v)
|
static inline struct hws_trailer_entry *trailer_entry_ptr(unsigned long v)
|
||||||
{
|
{
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ static inline unsigned long *trailer_entry_ptr(unsigned long v)
|
|||||||
ret += PAGE_SIZE;
|
ret += PAGE_SIZE;
|
||||||
ret -= sizeof(struct hws_trailer_entry);
|
ret -= sizeof(struct hws_trailer_entry);
|
||||||
|
|
||||||
return (unsigned long *)ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -148,7 +148,7 @@ static inline int is_link_entry(unsigned long *s)
|
|||||||
/* Return pointer to the linked sdbt */
|
/* Return pointer to the linked sdbt */
|
||||||
static inline unsigned long *get_next_sdbt(unsigned long *s)
|
static inline unsigned long *get_next_sdbt(unsigned long *s)
|
||||||
{
|
{
|
||||||
return (unsigned long *)(*s & ~0x1UL);
|
return phys_to_virt(*s & ~0x1UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -202,7 +202,7 @@ static void free_sampling_buffer(struct sf_buffer *sfb)
|
|||||||
} else {
|
} else {
|
||||||
/* Process SDB pointer */
|
/* Process SDB pointer */
|
||||||
if (*curr) {
|
if (*curr) {
|
||||||
free_page(*curr);
|
free_page((unsigned long)phys_to_virt(*curr));
|
||||||
curr++;
|
curr++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,11 +222,11 @@ static int alloc_sample_data_block(unsigned long *sdbt, gfp_t gfp_flags)
|
|||||||
sdb = get_zeroed_page(gfp_flags);
|
sdb = get_zeroed_page(gfp_flags);
|
||||||
if (!sdb)
|
if (!sdb)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
te = (struct hws_trailer_entry *)trailer_entry_ptr(sdb);
|
te = trailer_entry_ptr(sdb);
|
||||||
te->header.a = 1;
|
te->header.a = 1;
|
||||||
|
|
||||||
/* Link SDB into the sample-data-block-table */
|
/* Link SDB into the sample-data-block-table */
|
||||||
*sdbt = sdb;
|
*sdbt = virt_to_phys((void *)sdb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -285,7 +285,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
|
|||||||
}
|
}
|
||||||
sfb->num_sdbt++;
|
sfb->num_sdbt++;
|
||||||
/* Link current page to tail of chain */
|
/* Link current page to tail of chain */
|
||||||
*tail = (unsigned long)(void *) new + 1;
|
*tail = virt_to_phys((void *)new) + 1;
|
||||||
tail_prev = tail;
|
tail_prev = tail;
|
||||||
tail = new;
|
tail = new;
|
||||||
}
|
}
|
||||||
@ -315,7 +315,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Link sampling buffer to its origin */
|
/* Link sampling buffer to its origin */
|
||||||
*tail = (unsigned long) sfb->sdbt + 1;
|
*tail = virt_to_phys(sfb->sdbt) + 1;
|
||||||
sfb->tail = tail;
|
sfb->tail = tail;
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 4, "%s: new buffer"
|
debug_sprintf_event(sfdbg, 4, "%s: new buffer"
|
||||||
@ -353,7 +353,7 @@ static int alloc_sampling_buffer(struct sf_buffer *sfb, unsigned long num_sdb)
|
|||||||
* realloc_sampling_buffer() invocation.
|
* realloc_sampling_buffer() invocation.
|
||||||
*/
|
*/
|
||||||
sfb->tail = sfb->sdbt;
|
sfb->tail = sfb->sdbt;
|
||||||
*sfb->tail = (unsigned long)(void *) sfb->sdbt + 1;
|
*sfb->tail = virt_to_phys((void *)sfb->sdbt) + 1;
|
||||||
|
|
||||||
/* Allocate requested number of sample-data-blocks */
|
/* Allocate requested number of sample-data-blocks */
|
||||||
rc = realloc_sampling_buffer(sfb, num_sdb, GFP_KERNEL);
|
rc = realloc_sampling_buffer(sfb, num_sdb, GFP_KERNEL);
|
||||||
@ -1222,8 +1222,8 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt,
|
|||||||
struct hws_trailer_entry *te;
|
struct hws_trailer_entry *te;
|
||||||
struct hws_basic_entry *sample;
|
struct hws_basic_entry *sample;
|
||||||
|
|
||||||
te = (struct hws_trailer_entry *) trailer_entry_ptr(*sdbt);
|
te = trailer_entry_ptr((unsigned long)sdbt);
|
||||||
sample = (struct hws_basic_entry *) *sdbt;
|
sample = (struct hws_basic_entry *)sdbt;
|
||||||
while ((unsigned long *) sample < (unsigned long *) te) {
|
while ((unsigned long *) sample < (unsigned long *) te) {
|
||||||
/* Check for an empty sample */
|
/* Check for an empty sample */
|
||||||
if (!sample->def || sample->LS)
|
if (!sample->def || sample->LS)
|
||||||
@ -1304,7 +1304,7 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
|
|||||||
union hws_trailer_header old, prev, new;
|
union hws_trailer_header old, prev, new;
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
struct hws_trailer_entry *te;
|
struct hws_trailer_entry *te;
|
||||||
unsigned long *sdbt;
|
unsigned long *sdbt, sdb;
|
||||||
int done;
|
int done;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1321,7 +1321,8 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
|
|||||||
done = event_overflow = sampl_overflow = num_sdb = 0;
|
done = event_overflow = sampl_overflow = num_sdb = 0;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
/* Get the trailer entry of the sample-data-block */
|
/* Get the trailer entry of the sample-data-block */
|
||||||
te = (struct hws_trailer_entry *) trailer_entry_ptr(*sdbt);
|
sdb = (unsigned long)phys_to_virt(*sdbt);
|
||||||
|
te = trailer_entry_ptr(sdb);
|
||||||
|
|
||||||
/* Leave loop if no more work to do (block full indicator) */
|
/* Leave loop if no more work to do (block full indicator) */
|
||||||
if (!te->header.f) {
|
if (!te->header.f) {
|
||||||
@ -1339,16 +1340,17 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
|
|||||||
sampl_overflow += te->header.overflow;
|
sampl_overflow += te->header.overflow;
|
||||||
|
|
||||||
/* Timestamps are valid for full sample-data-blocks only */
|
/* Timestamps are valid for full sample-data-blocks only */
|
||||||
debug_sprintf_event(sfdbg, 6, "%s: sdbt %#lx "
|
debug_sprintf_event(sfdbg, 6, "%s: sdbt %#lx/%#lx "
|
||||||
"overflow %llu timestamp %#llx\n",
|
"overflow %llu timestamp %#llx\n",
|
||||||
__func__, (unsigned long)sdbt, te->header.overflow,
|
__func__, sdb, (unsigned long)sdbt,
|
||||||
|
te->header.overflow,
|
||||||
(te->header.f) ? trailer_timestamp(te) : 0ULL);
|
(te->header.f) ? trailer_timestamp(te) : 0ULL);
|
||||||
|
|
||||||
/* Collect all samples from a single sample-data-block and
|
/* Collect all samples from a single sample-data-block and
|
||||||
* flag if an (perf) event overflow happened. If so, the PMU
|
* flag if an (perf) event overflow happened. If so, the PMU
|
||||||
* is stopped and remaining samples will be discarded.
|
* is stopped and remaining samples will be discarded.
|
||||||
*/
|
*/
|
||||||
hw_collect_samples(event, sdbt, &event_overflow);
|
hw_collect_samples(event, (unsigned long *)sdb, &event_overflow);
|
||||||
num_sdb++;
|
num_sdb++;
|
||||||
|
|
||||||
/* Reset trailer (using compare-double-and-swap) */
|
/* Reset trailer (using compare-double-and-swap) */
|
||||||
@ -1421,7 +1423,7 @@ static struct hws_trailer_entry *aux_sdb_trailer(struct aux_buffer *aux,
|
|||||||
|
|
||||||
index = AUX_SDB_INDEX(aux, index);
|
index = AUX_SDB_INDEX(aux, index);
|
||||||
sdb = aux->sdb_index[index];
|
sdb = aux->sdb_index[index];
|
||||||
return (struct hws_trailer_entry *)trailer_entry_ptr(sdb);
|
return trailer_entry_ptr(sdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1747,7 +1749,7 @@ static void aux_sdb_init(unsigned long sdb)
|
|||||||
{
|
{
|
||||||
struct hws_trailer_entry *te;
|
struct hws_trailer_entry *te;
|
||||||
|
|
||||||
te = (struct hws_trailer_entry *)trailer_entry_ptr(sdb);
|
te = trailer_entry_ptr(sdb);
|
||||||
|
|
||||||
/* Save clock base */
|
/* Save clock base */
|
||||||
te->clock_base = 1;
|
te->clock_base = 1;
|
||||||
@ -1978,7 +1980,7 @@ static int cpumsf_pmu_add(struct perf_event *event, int flags)
|
|||||||
cpuhw->lsctl.h = 1;
|
cpuhw->lsctl.h = 1;
|
||||||
cpuhw->lsctl.interval = SAMPL_RATE(&event->hw);
|
cpuhw->lsctl.interval = SAMPL_RATE(&event->hw);
|
||||||
if (!SAMPL_DIAG_MODE(&event->hw)) {
|
if (!SAMPL_DIAG_MODE(&event->hw)) {
|
||||||
cpuhw->lsctl.tear = (unsigned long) cpuhw->sfb.sdbt;
|
cpuhw->lsctl.tear = virt_to_phys(cpuhw->sfb.sdbt);
|
||||||
cpuhw->lsctl.dear = *(unsigned long *) cpuhw->sfb.sdbt;
|
cpuhw->lsctl.dear = *(unsigned long *) cpuhw->sfb.sdbt;
|
||||||
TEAR_REG(&event->hw) = (unsigned long) cpuhw->sfb.sdbt;
|
TEAR_REG(&event->hw) = (unsigned long) cpuhw->sfb.sdbt;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user