// SPDX-License-Identifier: GPL-2.0-or-later /* * Parisc performance counters * Copyright (C) 2001 Randolph Chung <tausq@debian.org> * * This code is derived, with permission, from HP/UX sources.
*/
/* * Edited comment from original sources: * * This driver programs the PCX-U/PCX-W performance counters * on the PA-RISC 2.0 chips. The driver keeps all images now * internally to the kernel to hopefully eliminate the possibility * of a bad image halting the CPU. Also, there are different * images for the PCX-W and later chips vs the PCX-U chips. * * Only 1 process is allowed to access the driver at any time, * so the only protection that is needed is at open and close. * A variable "perf_enabled" is used to hold the state of the * driver. The spinlock "perf_lock" is used to protect the * modification of the state during open/close operations so * multiple processes don't get into the driver simultaneously. * * This driver accesses the processor directly vs going through * the PDC INTRIGUE calls. This is done to eliminate bugs introduced * in various PDC revisions. The code is much more maintainable * and reliable this way vs having to debug on every version of PDC * on every box.
*/
/****************************************************************************** * Function Definitions
*****************************************************************************/
/* * configure: * * Configure the cpu with a given data image. First turn off the counters, * then download the image, then turn the counters back on.
*/ staticint perf_config(uint32_t *image_ptr)
{ long error;
uint32_t raddr[4];
/* Stop the counters*/
error = perf_stop_counters(raddr); if (error != 0) {
printk("perf_config: perf_stop_counters = %ld\n", error); return -EINVAL;
}
printk("Preparing to write image\n"); /* Write the image to the chip */
error = perf_write_image((uint64_t *)image_ptr); if (error != 0) {
printk("perf_config: DOWNLOAD = %ld\n", error); return -EINVAL;
}
printk("Preparing to start counters\n");
/* Start the counters */
perf_start_counters();
returnsizeof(uint32_t);
}
/* * Open the device and initialize all of its memory. The device is only * opened once, but can be "queried" by multiple processes that know its * file descriptor.
*/ staticint perf_open(struct inode *inode, struct file *file)
{
spin_lock(&perf_lock); if (perf_enabled) {
spin_unlock(&perf_lock); return -EBUSY;
}
perf_enabled = 1;
spin_unlock(&perf_lock);
return 0;
}
/* * Close the device.
*/ staticint perf_release(struct inode *inode, struct file *file)
{
spin_lock(&perf_lock);
perf_enabled = 0;
spin_unlock(&perf_lock);
return 0;
}
/* * Read does nothing for this driver
*/ static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos)
{ return 0;
}
/* * write: * * This routine downloads the image to the chip. It must be * called on the processor that the download should happen * on.
*/ static ssize_t perf_write(struct file *file, constchar __user *buf,
size_t count, loff_t *ppos)
{
size_t image_size __maybe_unused;
uint32_t image_type;
uint32_t interface_type;
uint32_t test;
if (copy_from_user(&image_type, buf, sizeof(uint32_t))) return -EFAULT;
/* Get the interface type and test type */
interface_type = (image_type >> 16) & 0xffff;
test = (image_type & 0xffff);
/* Make sure everything makes sense */
/* First check the machine type is correct for
the requested image */ if (((perf_processor_interface == CUDA_INTF) &&
(interface_type != CUDA_INTF)) ||
((perf_processor_interface == ONYX_INTF) &&
(interface_type != ONYX_INTF))) return -EINVAL;
/* Next check to make sure the requested image
is valid */ if (((interface_type == CUDA_INTF) &&
(test >= MAX_CUDA_IMAGES)) ||
((interface_type == ONYX_INTF) &&
(test >= MAX_ONYX_IMAGES))) return -EINVAL;
/* Copy the image into the processor */ if (interface_type == CUDA_INTF) return perf_config(cuda_images[test]); else return perf_config(onyx_images[test]);
return count;
}
/* * Patch the images that need to know the IVA addresses.
*/ staticvoid perf_patch_images(void)
{ #if 0 /* FIXME!! */ /* * NOTE: this routine is VERY specific to the current TLB image. * If the image is changed, this routine might also need to be changed.
*/ externvoid $i_itlb_miss_2_0(); externvoid $i_dtlb_miss_2_0(); externvoid PA2_0_iva();
/* * We can only use the lower 32-bits, the upper 32-bits should be 0 * anyway given this is in the kernel
*/
uint32_t itlb_addr = (uint32_t)&($i_itlb_miss_2_0);
uint32_t dtlb_addr = (uint32_t)&($i_dtlb_miss_2_0);
uint32_t IVAaddress = (uint32_t)&PA2_0_iva;
/* * ioctl routine * All routines effect the processor that they are executed on. Thus you * must be running on the processor that you wish to change.
*/
staticlong perf_ioctl(struct file *file, unsignedint cmd, unsignedlong arg)
{ long error_start;
uint32_t raddr[4]; int error = 0;
switch (cmd) {
case PA_PERF_ON: /* Start the counters */
perf_start_counters(); break;
ret = misc_register(&perf_dev); if (ret) {
printk(KERN_ERR "Performance monitoring counters: " "cannot register misc device.\n"); return ret;
}
/* Patch the images to match the system */
perf_patch_images();
/* TODO: this only lets us access the first cpu.. what to do for SMP? */
cpu_device = per_cpu(cpu_data, 0).dev;
printk("Performance monitoring counters enabled for %s\n",
per_cpu(cpu_data, 0).dev->name);
/* * perf_stop_counters * * Stop the performance counters and save counts * in a per_processor array.
*/ staticint perf_stop_counters(uint32_t *raddr)
{
uint64_t userbuf[MAX_RDR_WORDS];
if (perf_processor_interface == ONYX_INTF) {
uint64_t tmp64; /* * Read the counters
*/ if (!perf_rdr_read_ubuf(16, userbuf)) return -13;
/* Counter0 is bits 1398 to 1429 */
tmp64 = (userbuf[21] << 22) & 0x00000000ffc00000;
tmp64 |= (userbuf[22] >> 42) & 0x00000000003fffff; /* OR sticky0 (bit 1430) to counter0 bit 32 */
tmp64 |= (userbuf[22] >> 10) & 0x0000000080000000;
raddr[0] = (uint32_t)tmp64;
/* Counter1 is bits 1431 to 1462 */
tmp64 = (userbuf[22] >> 9) & 0x00000000ffffffff; /* OR sticky1 (bit 1463) to counter1 bit 32 */
tmp64 |= (userbuf[22] << 23) & 0x0000000080000000;
raddr[1] = (uint32_t)tmp64;
/* Counter2 is bits 1464 to 1495 */
tmp64 = (userbuf[22] << 24) & 0x00000000ff000000;
tmp64 |= (userbuf[23] >> 40) & 0x0000000000ffffff; /* OR sticky2 (bit 1496) to counter2 bit 32 */
tmp64 |= (userbuf[23] >> 8) & 0x0000000080000000;
raddr[2] = (uint32_t)tmp64;
/* Counter3 is bits 1497 to 1528 */
tmp64 = (userbuf[23] >> 7) & 0x00000000ffffffff; /* OR sticky3 (bit 1529) to counter3 bit 32 */
tmp64 |= (userbuf[23] << 25) & 0x0000000080000000;
raddr[3] = (uint32_t)tmp64;
/* * Zero out the counters
*/
/* * The counters and sticky-bits comprise the last 132 bits * (1398 - 1529) of RDR16 on a U chip. We'll zero these * out the easy way: zero out last 10 bits of dword 21, * all of dword 22 and 58 bits (plus 6 don't care bits) of * dword 23.
*/
userbuf[21] &= 0xfffffffffffffc00ul; /* 0 to last 10 bits */
userbuf[22] = 0;
userbuf[23] = 0;
/* * Write back the zeroed bytes + the image given * the read was destructive.
*/
perf_rdr_write(16, userbuf);
} else {
/* * Read RDR-15 which contains the counters and sticky bits
*/ if (!perf_rdr_read_ubuf(15, userbuf)) { return -13;
}
/* * Clear out the counters
*/
perf_rdr_clear(15);
/* Write RUNWAY DEBUG registers */ for (i = 0; i < 8; i++) {
__raw_writeq(*memaddr++, runway + RUNWAY_DEBUG);
}
return 0;
}
/* * perf_rdr_write * * Write the given RDR register with the contents * of the given buffer.
*/ staticvoid perf_rdr_write(uint32_t rdr_num, uint64_t *buffer)
{ conststruct rdr_tbl_ent *tentry;
int32_t i;
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.