X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Foprofile%2Fevent_buffer.c;h=5df60a6b67766adb402a30fee44d7b7782bc5e08;hb=dc7a08166f3a5f23e79e839a8a88849bd3397c32;hp=04d641714d347f0f7e00243a5918c5eb7b6d8a6d;hpb=59cc185ada89245204c658ebcf64422968736672;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c index 04d6417..5df60a6 100644 --- a/drivers/oprofile/event_buffer.c +++ b/drivers/oprofile/event_buffer.c @@ -19,28 +19,39 @@ #include #include #include - + #include "oprof.h" #include "event_buffer.h" #include "oprofile_stats.h" DEFINE_MUTEX(buffer_mutex); - + static unsigned long buffer_opened; static DECLARE_WAIT_QUEUE_HEAD(buffer_wait); -static unsigned long * event_buffer; +static unsigned long *event_buffer; static unsigned long buffer_size; static unsigned long buffer_watershed; static size_t buffer_pos; /* atomic_t because wait_event checks it outside of buffer_mutex */ static atomic_t buffer_ready = ATOMIC_INIT(0); -/* Add an entry to the event buffer. When we - * get near to the end we wake up the process - * sleeping on the read() of the file. +/* + * Add an entry to the event buffer. When we get near to the end we + * wake up the process sleeping on the read() of the file. To protect + * the event_buffer this function may only be called when buffer_mutex + * is set. */ void add_event_entry(unsigned long value) { + /* + * This shouldn't happen since all workqueues or handlers are + * canceled or flushed before the event buffer is freed. + */ + if (!event_buffer) { + WARN_ON_ONCE(1); + return; + } + if (buffer_pos == buffer_size) { atomic_inc(&oprofile_stats.event_lost_overflow); return; @@ -66,43 +77,46 @@ void wake_up_buffer_waiter(void) mutex_unlock(&buffer_mutex); } - + int alloc_event_buffer(void) { - int err = -ENOMEM; + unsigned long flags; + + spin_lock_irqsave(&oprofilefs_lock, flags); + buffer_size = oprofile_buffer_size; + buffer_watershed = oprofile_buffer_watershed; + spin_unlock_irqrestore(&oprofilefs_lock, flags); - spin_lock(&oprofilefs_lock); - buffer_size = fs_buffer_size; - buffer_watershed = fs_buffer_watershed; - spin_unlock(&oprofilefs_lock); - if (buffer_watershed >= buffer_size) return -EINVAL; - + + buffer_pos = 0; event_buffer = vmalloc(sizeof(unsigned long) * buffer_size); if (!event_buffer) - goto out; + return -ENOMEM; - err = 0; -out: - return err; + return 0; } void free_event_buffer(void) { + mutex_lock(&buffer_mutex); vfree(event_buffer); + buffer_pos = 0; + event_buffer = NULL; + mutex_unlock(&buffer_mutex); } - -static int event_buffer_open(struct inode * inode, struct file * file) + +static int event_buffer_open(struct inode *inode, struct file *file) { int err = -EPERM; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (test_and_set_bit(0, &buffer_opened)) + if (test_and_set_bit_lock(0, &buffer_opened)) return -EBUSY; /* Register as a user of dcookies @@ -113,38 +127,38 @@ static int event_buffer_open(struct inode * inode, struct file * file) file->private_data = dcookie_register(); if (!file->private_data) goto out; - + if ((err = oprofile_setup())) goto fail; /* NB: the actual start happens from userspace * echo 1 >/dev/oprofile/enable */ - + return 0; fail: dcookie_unregister(file->private_data); out: - clear_bit(0, &buffer_opened); + __clear_bit_unlock(0, &buffer_opened); return err; } -static int event_buffer_release(struct inode * inode, struct file * file) +static int event_buffer_release(struct inode *inode, struct file *file) { oprofile_stop(); oprofile_shutdown(); dcookie_unregister(file->private_data); buffer_pos = 0; atomic_set(&buffer_ready, 0); - clear_bit(0, &buffer_opened); + __clear_bit_unlock(0, &buffer_opened); return 0; } -static ssize_t event_buffer_read(struct file * file, char __user * buf, - size_t count, loff_t * offset) +static ssize_t event_buffer_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) { int retval = -EINVAL; size_t const max = buffer_size * sizeof(unsigned long); @@ -164,24 +178,30 @@ static ssize_t event_buffer_read(struct file * file, char __user * buf, mutex_lock(&buffer_mutex); + /* May happen if the buffer is freed during pending reads. */ + if (!event_buffer) { + retval = -EINTR; + goto out; + } + atomic_set(&buffer_ready, 0); retval = -EFAULT; count = buffer_pos * sizeof(unsigned long); - + if (copy_to_user(buf, event_buffer, count)) goto out; retval = count; buffer_pos = 0; - + out: mutex_unlock(&buffer_mutex); return retval; } - -struct file_operations event_buffer_fops = { + +const struct file_operations event_buffer_fops = { .open = event_buffer_open, .release = event_buffer_release, .read = event_buffer_read,