1/** 2 * @file event_buffer.c 3 * 4 * @remark Copyright 2002 OProfile authors 5 * @remark Read the file COPYING 6 * 7 * @author John Levon <levon@movementarian.org> 8 * 9 * This is the global event buffer that the user-space 10 * daemon reads from. The event buffer is an untyped array 11 * of unsigned longs. Entries are prefixed by the 12 * escape value ESCAPE_CODE followed by an identifying code. 13 */ 14 15#include <linux/vmalloc.h> 16#include <linux/oprofile.h> 17#include <linux/sched.h> 18#include <linux/capability.h> 19#include <linux/dcookies.h> 20#include <linux/fs.h> 21#include <asm/uaccess.h> 22 23#include "oprof.h" 24#include "event_buffer.h" 25#include "oprofile_stats.h" 26 27DEFINE_MUTEX(buffer_mutex); 28 29static unsigned long buffer_opened; 30static DECLARE_WAIT_QUEUE_HEAD(buffer_wait); 31static unsigned long * event_buffer; 32static unsigned long buffer_size; 33static unsigned long buffer_watershed; 34static size_t buffer_pos; 35/* atomic_t because wait_event checks it outside of buffer_mutex */ 36static atomic_t buffer_ready = ATOMIC_INIT(0); 37 38/* Add an entry to the event buffer. When we 39 * get near to the end we wake up the process 40 * sleeping on the read() of the file. 41 */ 42void add_event_entry(unsigned long value) 43{ 44 if (buffer_pos == buffer_size) { 45 atomic_inc(&oprofile_stats.event_lost_overflow); 46 return; 47 } 48 49 event_buffer[buffer_pos] = value; 50 if (++buffer_pos == buffer_size - buffer_watershed) { 51 atomic_set(&buffer_ready, 1); 52 wake_up(&buffer_wait); 53 } 54} 55 56 57/* Wake up the waiting process if any. This happens 58 * on "echo 0 >/dev/oprofile/enable" so the daemon 59 * processes the data remaining in the event buffer. 60 */ 61void wake_up_buffer_waiter(void) 62{ 63 mutex_lock(&buffer_mutex); 64 atomic_set(&buffer_ready, 1); 65 wake_up(&buffer_wait); 66 mutex_unlock(&buffer_mutex); 67} 68 69 70int alloc_event_buffer(void) 71{ 72 int err = -ENOMEM; 73 unsigned long flags; 74 75 spin_lock_irqsave(&oprofilefs_lock, flags); 76 buffer_size = fs_buffer_size; 77 buffer_watershed = fs_buffer_watershed; 78 spin_unlock_irqrestore(&oprofilefs_lock, flags); 79 80 if (buffer_watershed >= buffer_size) 81 return -EINVAL; 82 83 event_buffer = vmalloc(sizeof(unsigned long) * buffer_size); 84 if (!event_buffer) 85 goto out; 86 87 err = 0; 88out: 89 return err; 90} 91 92 93void free_event_buffer(void) 94{ 95 vfree(event_buffer); 96} 97 98 99static int event_buffer_open(struct inode * inode, struct file * file) 100{ 101 int err = -EPERM; 102 103 if (!capable(CAP_SYS_ADMIN)) 104 return -EPERM; 105 106 if (test_and_set_bit(0, &buffer_opened)) 107 return -EBUSY; 108 109 /* Register as a user of dcookies 110 * to ensure they persist for the lifetime of 111 * the open event file 112 */ 113 err = -EINVAL; 114 file->private_data = dcookie_register(); 115 if (!file->private_data) 116 goto out; 117 118 if ((err = oprofile_setup())) 119 goto fail; 120 121 /* NB: the actual start happens from userspace 122 * echo 1 >/dev/oprofile/enable 123 */ 124 125 return 0; 126 127fail: 128 dcookie_unregister(file->private_data); 129out: 130 clear_bit(0, &buffer_opened); 131 return err; 132} 133 134 135static int event_buffer_release(struct inode * inode, struct file * file) 136{ 137 oprofile_stop(); 138 oprofile_shutdown(); 139 dcookie_unregister(file->private_data); 140 buffer_pos = 0; 141 atomic_set(&buffer_ready, 0); 142 clear_bit(0, &buffer_opened); 143 return 0; 144} 145 146 147static ssize_t event_buffer_read(struct file * file, char __user * buf, 148 size_t count, loff_t * offset) 149{ 150 int retval = -EINVAL; 151 size_t const max = buffer_size * sizeof(unsigned long); 152 153 /* handling partial reads is more trouble than it's worth */ 154 if (count != max || *offset) 155 return -EINVAL; 156 157 wait_event_interruptible(buffer_wait, atomic_read(&buffer_ready)); 158 159 if (signal_pending(current)) 160 return -EINTR; 161 162 /* can't currently happen */ 163 if (!atomic_read(&buffer_ready)) 164 return -EAGAIN; 165 166 mutex_lock(&buffer_mutex); 167 168 atomic_set(&buffer_ready, 0); 169 170 retval = -EFAULT; 171 172 count = buffer_pos * sizeof(unsigned long); 173 174 if (copy_to_user(buf, event_buffer, count)) 175 goto out; 176 177 retval = count; 178 buffer_pos = 0; 179 180out: 181 mutex_unlock(&buffer_mutex); 182 return retval; 183} 184 185const struct file_operations event_buffer_fops = { 186 .open = event_buffer_open, 187 .release = event_buffer_release, 188 .read = event_buffer_read, 189}; 190