18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * @file event_buffer.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * @remark Copyright 2002 OProfile authors 58c2ecf20Sopenharmony_ci * @remark Read the file COPYING 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * @author John Levon <levon@movementarian.org> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This is the global event buffer that the user-space 108c2ecf20Sopenharmony_ci * daemon reads from. The event buffer is an untyped array 118c2ecf20Sopenharmony_ci * of unsigned longs. Entries are prefixed by the 128c2ecf20Sopenharmony_ci * escape value ESCAPE_CODE followed by an identifying code. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 168c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 178c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 188c2ecf20Sopenharmony_ci#include <linux/capability.h> 198c2ecf20Sopenharmony_ci#include <linux/dcookies.h> 208c2ecf20Sopenharmony_ci#include <linux/fs.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "oprof.h" 248c2ecf20Sopenharmony_ci#include "event_buffer.h" 258c2ecf20Sopenharmony_ci#include "oprofile_stats.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciDEFINE_MUTEX(buffer_mutex); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic unsigned long buffer_opened; 308c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(buffer_wait); 318c2ecf20Sopenharmony_cistatic unsigned long *event_buffer; 328c2ecf20Sopenharmony_cistatic unsigned long buffer_size; 338c2ecf20Sopenharmony_cistatic unsigned long buffer_watershed; 348c2ecf20Sopenharmony_cistatic size_t buffer_pos; 358c2ecf20Sopenharmony_ci/* atomic_t because wait_event checks it outside of buffer_mutex */ 368c2ecf20Sopenharmony_cistatic atomic_t buffer_ready = ATOMIC_INIT(0); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * Add an entry to the event buffer. When we get near to the end we 408c2ecf20Sopenharmony_ci * wake up the process sleeping on the read() of the file. To protect 418c2ecf20Sopenharmony_ci * the event_buffer this function may only be called when buffer_mutex 428c2ecf20Sopenharmony_ci * is set. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_civoid add_event_entry(unsigned long value) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci /* 478c2ecf20Sopenharmony_ci * This shouldn't happen since all workqueues or handlers are 488c2ecf20Sopenharmony_ci * canceled or flushed before the event buffer is freed. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci if (!event_buffer) { 518c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (buffer_pos == buffer_size) { 568c2ecf20Sopenharmony_ci atomic_inc(&oprofile_stats.event_lost_overflow); 578c2ecf20Sopenharmony_ci return; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci event_buffer[buffer_pos] = value; 618c2ecf20Sopenharmony_ci if (++buffer_pos == buffer_size - buffer_watershed) { 628c2ecf20Sopenharmony_ci atomic_set(&buffer_ready, 1); 638c2ecf20Sopenharmony_ci wake_up(&buffer_wait); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Wake up the waiting process if any. This happens 698c2ecf20Sopenharmony_ci * on "echo 0 >/dev/oprofile/enable" so the daemon 708c2ecf20Sopenharmony_ci * processes the data remaining in the event buffer. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_civoid wake_up_buffer_waiter(void) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci mutex_lock(&buffer_mutex); 758c2ecf20Sopenharmony_ci atomic_set(&buffer_ready, 1); 768c2ecf20Sopenharmony_ci wake_up(&buffer_wait); 778c2ecf20Sopenharmony_ci mutex_unlock(&buffer_mutex); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciint alloc_event_buffer(void) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci unsigned long flags; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&oprofilefs_lock, flags); 868c2ecf20Sopenharmony_ci buffer_size = oprofile_buffer_size; 878c2ecf20Sopenharmony_ci buffer_watershed = oprofile_buffer_watershed; 888c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&oprofilefs_lock, flags); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (buffer_watershed >= buffer_size) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci buffer_pos = 0; 948c2ecf20Sopenharmony_ci event_buffer = vmalloc(array_size(buffer_size, sizeof(unsigned long))); 958c2ecf20Sopenharmony_ci if (!event_buffer) 968c2ecf20Sopenharmony_ci return -ENOMEM; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_civoid free_event_buffer(void) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci mutex_lock(&buffer_mutex); 1058c2ecf20Sopenharmony_ci vfree(event_buffer); 1068c2ecf20Sopenharmony_ci buffer_pos = 0; 1078c2ecf20Sopenharmony_ci event_buffer = NULL; 1088c2ecf20Sopenharmony_ci mutex_unlock(&buffer_mutex); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int event_buffer_open(struct inode *inode, struct file *file) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int err = -EPERM; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!perfmon_capable()) 1178c2ecf20Sopenharmony_ci return -EPERM; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(0, &buffer_opened)) 1208c2ecf20Sopenharmony_ci return -EBUSY; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Register as a user of dcookies 1238c2ecf20Sopenharmony_ci * to ensure they persist for the lifetime of 1248c2ecf20Sopenharmony_ci * the open event file 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci err = -EINVAL; 1278c2ecf20Sopenharmony_ci file->private_data = dcookie_register(); 1288c2ecf20Sopenharmony_ci if (!file->private_data) 1298c2ecf20Sopenharmony_ci goto out; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if ((err = oprofile_setup())) 1328c2ecf20Sopenharmony_ci goto fail; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* NB: the actual start happens from userspace 1358c2ecf20Sopenharmony_ci * echo 1 >/dev/oprofile/enable 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return nonseekable_open(inode, file); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cifail: 1418c2ecf20Sopenharmony_ci dcookie_unregister(file->private_data); 1428c2ecf20Sopenharmony_ciout: 1438c2ecf20Sopenharmony_ci __clear_bit_unlock(0, &buffer_opened); 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int event_buffer_release(struct inode *inode, struct file *file) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci oprofile_stop(); 1518c2ecf20Sopenharmony_ci oprofile_shutdown(); 1528c2ecf20Sopenharmony_ci dcookie_unregister(file->private_data); 1538c2ecf20Sopenharmony_ci buffer_pos = 0; 1548c2ecf20Sopenharmony_ci atomic_set(&buffer_ready, 0); 1558c2ecf20Sopenharmony_ci __clear_bit_unlock(0, &buffer_opened); 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic ssize_t event_buffer_read(struct file *file, char __user *buf, 1618c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int retval = -EINVAL; 1648c2ecf20Sopenharmony_ci size_t const max = buffer_size * sizeof(unsigned long); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* handling partial reads is more trouble than it's worth */ 1678c2ecf20Sopenharmony_ci if (count != max || *offset) 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci wait_event_interruptible(buffer_wait, atomic_read(&buffer_ready)); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (signal_pending(current)) 1738c2ecf20Sopenharmony_ci return -EINTR; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* can't currently happen */ 1768c2ecf20Sopenharmony_ci if (!atomic_read(&buffer_ready)) 1778c2ecf20Sopenharmony_ci return -EAGAIN; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci mutex_lock(&buffer_mutex); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* May happen if the buffer is freed during pending reads. */ 1828c2ecf20Sopenharmony_ci if (!event_buffer) { 1838c2ecf20Sopenharmony_ci retval = -EINTR; 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci atomic_set(&buffer_ready, 0); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci retval = -EFAULT; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci count = buffer_pos * sizeof(unsigned long); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (copy_to_user(buf, event_buffer, count)) 1948c2ecf20Sopenharmony_ci goto out; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci retval = count; 1978c2ecf20Sopenharmony_ci buffer_pos = 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciout: 2008c2ecf20Sopenharmony_ci mutex_unlock(&buffer_mutex); 2018c2ecf20Sopenharmony_ci return retval; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciconst struct file_operations event_buffer_fops = { 2058c2ecf20Sopenharmony_ci .open = event_buffer_open, 2068c2ecf20Sopenharmony_ci .release = event_buffer_release, 2078c2ecf20Sopenharmony_ci .read = event_buffer_read, 2088c2ecf20Sopenharmony_ci .llseek = no_llseek, 2098c2ecf20Sopenharmony_ci}; 210