at v2.6.20 188 lines 4.0 kB view raw
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 74 spin_lock(&oprofilefs_lock); 75 buffer_size = fs_buffer_size; 76 buffer_watershed = fs_buffer_watershed; 77 spin_unlock(&oprofilefs_lock); 78 79 if (buffer_watershed >= buffer_size) 80 return -EINVAL; 81 82 event_buffer = vmalloc(sizeof(unsigned long) * buffer_size); 83 if (!event_buffer) 84 goto out; 85 86 err = 0; 87out: 88 return err; 89} 90 91 92void free_event_buffer(void) 93{ 94 vfree(event_buffer); 95} 96 97 98static int event_buffer_open(struct inode * inode, struct file * file) 99{ 100 int err = -EPERM; 101 102 if (!capable(CAP_SYS_ADMIN)) 103 return -EPERM; 104 105 if (test_and_set_bit(0, &buffer_opened)) 106 return -EBUSY; 107 108 /* Register as a user of dcookies 109 * to ensure they persist for the lifetime of 110 * the open event file 111 */ 112 err = -EINVAL; 113 file->private_data = dcookie_register(); 114 if (!file->private_data) 115 goto out; 116 117 if ((err = oprofile_setup())) 118 goto fail; 119 120 /* NB: the actual start happens from userspace 121 * echo 1 >/dev/oprofile/enable 122 */ 123 124 return 0; 125 126fail: 127 dcookie_unregister(file->private_data); 128out: 129 clear_bit(0, &buffer_opened); 130 return err; 131} 132 133 134static int event_buffer_release(struct inode * inode, struct file * file) 135{ 136 oprofile_stop(); 137 oprofile_shutdown(); 138 dcookie_unregister(file->private_data); 139 buffer_pos = 0; 140 atomic_set(&buffer_ready, 0); 141 clear_bit(0, &buffer_opened); 142 return 0; 143} 144 145 146static ssize_t event_buffer_read(struct file * file, char __user * buf, 147 size_t count, loff_t * offset) 148{ 149 int retval = -EINVAL; 150 size_t const max = buffer_size * sizeof(unsigned long); 151 152 /* handling partial reads is more trouble than it's worth */ 153 if (count != max || *offset) 154 return -EINVAL; 155 156 wait_event_interruptible(buffer_wait, atomic_read(&buffer_ready)); 157 158 if (signal_pending(current)) 159 return -EINTR; 160 161 /* can't currently happen */ 162 if (!atomic_read(&buffer_ready)) 163 return -EAGAIN; 164 165 mutex_lock(&buffer_mutex); 166 167 atomic_set(&buffer_ready, 0); 168 169 retval = -EFAULT; 170 171 count = buffer_pos * sizeof(unsigned long); 172 173 if (copy_to_user(buf, event_buffer, count)) 174 goto out; 175 176 retval = count; 177 buffer_pos = 0; 178 179out: 180 mutex_unlock(&buffer_mutex); 181 return retval; 182} 183 184struct file_operations event_buffer_fops = { 185 .open = event_buffer_open, 186 .release = event_buffer_release, 187 .read = event_buffer_read, 188};