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