android_kernel_modules_leno.../debug_tools/vtunedk/src/output.c

952 lines
29 KiB
C

/*COPYRIGHT**
Copyright (C) 2005-2014 Intel Corporation. All Rights Reserved.
This file is part of SEP Development Kit
SEP Development Kit is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
SEP Development Kit is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SEP Development Kit; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
As a special exception, you may use this file as part of a free software
library without restriction. Specifically, if other files instantiate
templates or use macros or inline functions from this file, or you compile
this file and link it with other files to produce an executable, this
file does not by itself cause the resulting executable to be covered by
the GNU General Public License. This exception does not however
invalidate any other reasons why the executable file might be covered by
the GNU General Public License.
**COPYRIGHT*/
#include "lwpmudrv_defines.h"
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include "lwpmudrv_types.h"
#include "rise_errors.h"
#include "lwpmudrv.h"
#include "lwpmudrv_ioctl.h"
#include "lwpmudrv_ecb.h"
#include "lwpmudrv_struct.h"
#include "control.h"
#include "output.h"
#define OTHER_C_DEVICES 1 // one for module
/*
* Global data: Buffer control structure
*/
static wait_queue_head_t flush_queue;
static atomic_t flush_writers;
extern S32 abnormal_terminate;
static volatile int flush = 0;
#if defined(CONTINUOUS_PROFILER)
static int cp = 1;
#endif
#if defined (DRV_USE_NMI)
struct timer_list *output_signal_timer;
#endif
/*
* @fn output_Free_Buffers(output, size)
*
* @param IN outbuf - The output buffer to manipulate
*
* @brief Deallocate the memory associated with the buffer descriptor
*
*/
static VOID
output_Free_Buffers (
BUFFER_DESC buffer,
size_t size
)
{
int j;
OUTPUT outbuf;
if (buffer == NULL) {
return;
}
outbuf = &BUFFER_DESC_outbuf(buffer);
for (j = 0; j < OUTPUT_NUM_BUFFERS; j++) {
CONTROL_Free_Memory(OUTPUT_buffer(outbuf,j));
OUTPUT_buffer(outbuf,j) = NULL;
}
return;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn int OUTPUT_Reserve_Buffer_Space (OUTPUT outbuf,
* U32 size)
*
* @param outbuf IN output buffer to manipulate
* @param size IN The size of data to reserve
*
* @result outloc - to the location where data is to be written
*
* Reserve space in the output buffers for data. If a buffer is full,
* signal the caller that the flush routine needs to be called.
*
* <I>Special Notes:</I>
*
*/
extern void*
OUTPUT_Reserve_Buffer_Space (
BUFFER_DESC bd,
U32 size
)
{
char *outloc = NULL;
OUTPUT outbuf = &BUFFER_DESC_outbuf(bd);
#if defined(CONTINUOUS_PROFILER)
if (flush) {
return NULL;
}
#endif
if (OUTPUT_remaining_buffer_size(outbuf) >= size) {
outloc = (OUTPUT_buffer(outbuf,OUTPUT_current_buffer(outbuf)) +
(OUTPUT_total_buffer_size(outbuf) - OUTPUT_remaining_buffer_size(outbuf)));
}
else {
U32 i, j, start;
OUTPUT_buffer_full(outbuf,OUTPUT_current_buffer(outbuf)) =
OUTPUT_total_buffer_size(outbuf) - OUTPUT_remaining_buffer_size(outbuf);
//
// Massive Naive assumption: Must find a way to fix it.
// In spite of the loop.
// The next buffer to fill are monotonically increasing
// indicies.
//
#if defined(CONTINUOUS_PROFILER)
if (!cp) {
signal_full = TRUE;
}
#else
OUTPUT_signal_full(outbuf) = TRUE;
#endif
start = OUTPUT_current_buffer(outbuf);
for (i = start+1; i < start+OUTPUT_NUM_BUFFERS; i++) {
j = i%OUTPUT_NUM_BUFFERS;
#if defined(CONTINUOUS_PROFILER)
//don't check if buffer has data when doing CP
if (!OUTPUT_buffer_full(outbuf,j) || cp) {
#else
if (!OUTPUT_buffer_full(outbuf,j)) {
#endif
OUTPUT_current_buffer(outbuf) = j;
OUTPUT_remaining_buffer_size(outbuf) = OUTPUT_total_buffer_size(outbuf);
outloc = OUTPUT_buffer(outbuf,j);
}
#if !(defined(CONFIG_PREEMPT_RT) || defined(DRV_USE_NMI))
else {
OUTPUT_signal_full(outbuf) = FALSE;
SEP_PRINT_DEBUG("Warning: Output buffers are full. Might be dropping some samples.\n");
break;
}
#endif
}
}
if (outloc) {
OUTPUT_remaining_buffer_size(outbuf) -= size;
memset(outloc, 0, size);
}
#if !(defined(CONFIG_PREEMPT_RT) || defined (DRV_USE_NMI))
if (OUTPUT_signal_full(outbuf)) {
wake_up_interruptible_sync(&BUFFER_DESC_queue(bd));
OUTPUT_signal_full(outbuf) = FALSE;
}
#endif
return outloc;
}
/* ------------------------------------------------------------------------- */
/*!
*
* @fn int OUTPUT_Buffer_Fill (BUFFER_DESC buf,
* PVOID data,
* U16 size)
*
* @brief Place a record (can be module, marker, etc) in a buffer
*
* @param data - pointer to a buffer to copy
* @param size - size of the buffer to cpu
*
* @return number of bytes copied into buffer
*
* Start by ensuring that output buffer space is available.
* If so, then copy the input data to the output buffer and make the necessary
* adjustments to manage the output buffers.
* If not, signal the read event for this buffer and get another buffer.
*
* <I>Special Notes:</I>
*
*/
static int
output_Buffer_Fill (
BUFFER_DESC bd,
PVOID data,
U16 size
)
{
char *outloc;
outloc = OUTPUT_Reserve_Buffer_Space (bd, size);
if (outloc) {
memcpy(outloc, data, size);
return size;
}
return 0;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn int OUTPUT_Module_Fill (PVOID data,
* U16 size)
*
* @brief Place a module record in a buffer
*
* @param data - pointer to a buffer to copy
* @param size - size of the buffer to cpu
*
* @return number of bytes copied into buffer
*
*
*/
extern int
OUTPUT_Module_Fill (
PVOID data,
U16 size
)
{
int ret_size;
OUTPUT outbuf = &BUFFER_DESC_outbuf(module_buf);
spin_lock(&OUTPUT_buffer_lock(outbuf));
ret_size = output_Buffer_Fill(module_buf, data, size);
spin_unlock(&OUTPUT_buffer_lock(outbuf));
return ret_size;
}
/* ------------------------------------------------------------------------- */
/*!
* @fn ssize_t output_Read(struct file *filp,
* char *buf,
* size_t count,
* loff_t *f_pos,
* BUFFER_DESC kernel_buf)
*
* @brief Return a sample buffer to user-mode. If not full or flush, wait
*
* @param *filp a file pointer
* @param *buf a sampling buffer
* @param count size of the user's buffer
* @param f_pos file pointer (current offset in bytes)
* @param kernel_buf the kernel output buffer structure
*
* @return number of bytes read. zero indicates end of file. Neg means error
*
* Place no more than count bytes into the user's buffer.
* Block if unavailable on "BUFFER_DESC_queue(buf)"
*
* <I>Special Notes:</I>
*
*/
static ssize_t
output_Read (
struct file *filp,
char *buf,
size_t count,
loff_t *f_pos,
BUFFER_DESC kernel_buf
)
{
ssize_t to_copy;
ssize_t uncopied;
OUTPUT outbuf = &BUFFER_DESC_outbuf(kernel_buf);
U32 cur_buf, i;
/* Buffer is filled by output_fill_modules. */
cur_buf = OUTPUT_current_buffer(outbuf);
for (i=0; i<OUTPUT_NUM_BUFFERS; i++) { //iterate through all buffers
cur_buf++;
if (cur_buf >= OUTPUT_NUM_BUFFERS) { cur_buf = 0; } //circularly
if ((to_copy = OUTPUT_buffer_full(outbuf, cur_buf))) {
break;
}
}
SEP_PRINT_DEBUG("buffer %d has %d bytes ready\n", (S32)cur_buf, (S32)to_copy);
if (!flush && to_copy == 0) {
#if defined(CONFIG_PREEMPT_RT)
do {
unsigned long delay;
delay = msecs_to_jiffies(1000);
wait_event_interruptible_timeout(BUFFER_DESC_queue(kernel_buf),
flush||OUTPUT_buffer_full(outbuf, cur_buf), delay);
} while (!(flush||OUTPUT_buffer_full(outbuf, cur_buf)));
#else
if (wait_event_interruptible(BUFFER_DESC_queue(kernel_buf),
flush||OUTPUT_buffer_full(outbuf, cur_buf))) {
return OS_RESTART_SYSCALL;
}
#endif
SEP_PRINT_DEBUG("Get to copy\n", (S32)cur_buf);
to_copy = OUTPUT_buffer_full(outbuf, cur_buf);
SEP_PRINT_DEBUG("output_Read awakened, buffer %d has %d bytes\n",cur_buf, (int)to_copy );
}
/* Ensure that the user's buffer is large enough */
if (to_copy > count) {
SEP_PRINT_DEBUG("user buffer is too small\n");
return OS_NO_MEM;
}
/* Copy data to user space. Note that we use cur_buf as the source */
if (abnormal_terminate == 0) {
uncopied = copy_to_user(buf,
OUTPUT_buffer(outbuf, cur_buf),
to_copy);
/* Mark the buffer empty */
OUTPUT_buffer_full(outbuf, cur_buf) = 0;
*f_pos += to_copy-uncopied;
if (uncopied) {
SEP_PRINT_DEBUG("only copied %d of %lld bytes of module records\n",
(S32)to_copy, (long long)uncopied);
return (to_copy - uncopied);
}
}
else {
to_copy = 0;
SEP_PRINT_DEBUG("to copy set to 0\n");
}
// At end-of-file, decrement the count of active buffer writers
if (to_copy == 0) {
DRV_BOOL flush_val = atomic_dec_and_test(&flush_writers);
SEP_PRINT_DEBUG("output_Read decremented flush_writers\n");
if (flush_val == TRUE) {
wake_up_interruptible_sync(&flush_queue);
}
}
return to_copy;
}
#if defined(CONTINUOUS_PROFILER)
/* ------------------------------------------------------------------------- */
/*!
* @fn ssize_t output_Read_Cp(struct file *filp,
* char *buf,
* size_t count,
* loff_t *f_pos,
* BUFFER_DESC kernel_buf)
*
* @brief Return a sample buffer to user-mode. If not full or flush, wait
*
* @param *filp a file pointer
* @param *buf a sampling buffer
* @param count size of the user's buffer
* @param f_pos file pointer (current offset in bytes)
* @param kernel_buf the kernel output buffer structure
*
* @return number of bytes read. zero indicates end of file. Neg means error
*
* Place no more than count bytes into the user's buffer.
* Block if unavailable on "BUFFER_DESC_queue(buf)"
*
* <I>Special Notes:</I>
*
*/
static ssize_t
output_Read_Cp (
struct file *filp,
char *buf,
size_t count,
loff_t *f_pos,
BUFFER_DESC kernel_buf
)
{
ssize_t to_copy, total_copied = 0;
ssize_t uncopied = 0;
OUTPUT outbuf = &BUFFER_DESC_outbuf(kernel_buf);
U32 cur_buf, other_buf, outloc_val;
char *outloc = NULL;
/* Buffer is filled by output_fill_modules. */
if (!flush) {
#if defined(CONFIG_PREEMPT_RT)
do {
unsigned long delay;
delay = msecs_to_jiffies(1000);
wait_event_interruptible_timeout(BUFFER_DESC_queue(kernel_buf), flush, delay);
} while (!flush);
#else
SEP_PRINT("cp read, going to sleep\n");
if (wait_event_interruptible(BUFFER_DESC_queue(kernel_buf), flush)) {
return OS_RESTART_SYSCALL;
}
#endif
}
SEP_PRINT("cp wakeup!\n");
cur_buf = OUTPUT_current_buffer(outbuf);
other_buf = (cur_buf + 1)%OUTPUT_NUM_BUFFERS;
outloc_val = (OUTPUT_BUFFER_SIZE - OUTPUT_remaining_buffer_size(outbuf));
outloc = (OUTPUT_buffer(outbuf,cur_buf)) + outloc_val;
/* Copy data to user space */
if (abnormal_terminate == 0) {
//Current buffer(current location to buffer_full)
if (OUTPUT_buffer_full(outbuf, cur_buf) > 0) {
to_copy = OUTPUT_buffer_full(outbuf, cur_buf) - outloc_val;
/* Ensure that the user's buffer is large enough */
if (to_copy > count - total_copied) {
SEP_PRINT("1 cp user buffer is too small\nto_copy=%d count=%d\n",(S32)to_copy,(U32)count);
return OS_NO_MEM;
}
uncopied = copy_to_user(buf + total_copied,
outloc,
to_copy);
SEP_PRINT("1 uncopied=%d, to_copy=%d\n", (S32)uncopied, (S32)to_copy);
*f_pos += to_copy-uncopied;
if (uncopied) {
SEP_PRINT("1 cp only copied %d of %lld bytes of module records\n",
(S32)to_copy, (long long)uncopied);
return (to_copy - uncopied);
}
total_copied += to_copy;
}
//Other buffer(start to buffer_full)
if ((to_copy = OUTPUT_buffer_full(outbuf, other_buf))) {
/* Ensure that the user's buffer is large enough */
if (to_copy > count - total_copied) {
SEP_PRINT("2 cp user buffer is too small\nto_copy=%d count=%d\n",(S32)to_copy,(U32)count);
return OS_NO_MEM;
}
uncopied = copy_to_user(buf + total_copied,
OUTPUT_buffer(outbuf, other_buf),
to_copy);
SEP_PRINT("2 uncopied=%d, to_copy=%d\n", (S32)uncopied, (S32)to_copy);
*f_pos += to_copy-uncopied;
if (uncopied) {
SEP_PRINT("2 only copied %d of %lld bytes of module records\n",
(S32)to_copy, (long long)uncopied);
return (to_copy - uncopied);
}
total_copied += to_copy;
}
//Current buffer(start to current location)
if ((to_copy = outloc_val)) {
/* Ensure that the user's buffer is large enough */
if (to_copy > count - total_copied) {
SEP_PRINT("3 cp user buffer is too small\nto_copy=%d count=%d\n",(S32)to_copy,(U32)count);
return OS_NO_MEM;
}
uncopied = copy_to_user(buf + total_copied,
OUTPUT_buffer(outbuf, cur_buf),
to_copy);
SEP_PRINT("3 uncopied=%d, to_copy=%d\n", (S32)uncopied, (S32)to_copy);
*f_pos += to_copy-uncopied;
if (uncopied) {
SEP_PRINT("3 only copied %d of %lld bytes of module records\n",
(S32)to_copy, (long long)uncopied);
return (to_copy - uncopied);
}
total_copied += to_copy;
}
SEP_PRINT("cp finish total_copied=%d\n", (S32)total_copied);
/* Mark the buffers empty */
OUTPUT_buffer_full(outbuf, cur_buf) = 0;
OUTPUT_buffer_full(outbuf, other_buf) = 0;
OUTPUT_remaining_buffer_size(outbuf) = OUTPUT_BUFFER_SIZE;
}
else {
to_copy = 0;
SEP_PRINT("cp to copy set to 0\n");
}
// At end-of-file, decrement the count of active buffer writers
if (total_copied == 0) {
DRV_BOOL flush_val = atomic_dec_and_test(&flush_writers);
SEP_PRINT("cp output_Read decremented flush_writers\n");
if (flush_val == TRUE) {
wake_up_interruptible_sync(&flush_queue);
}
}
return total_copied;
}
#endif
/* ------------------------------------------------------------------------- */
/*!
* @fn ssize_t OUTPUT_Module_Read(struct file *filp,
* char *buf,
* size_t count,
* loff_t *f_pos)
*
* @brief Return a module buffer to user-mode. If not full or flush, wait
*
* @param *filp a file pointer
* @param *buf a sampling buffer
* @param count size of the user's buffer
* @param f_pos file pointer (current offset in bytes)
* @param buf the kernel output buffer structure
*
* @return number of bytes read. zero indicates end of file. Neg means error
*
* Place no more than count bytes into the user's buffer.
* Block on "BUFFER_DESC_queue(kernel_buf)" if buffer isn't full.
*
* <I>Special Notes:</I>
*
*/
extern ssize_t
OUTPUT_Module_Read (
struct file *filp,
char *buf,
size_t count,
loff_t *f_pos
)
{
SEP_PRINT_DEBUG("read request for modules on minor\n");
#if defined(CONTINUOUS_PROFILER)
if (cp) {
return output_Read_Cp(filp, buf, count, f_pos, module_buf);
}
else {
return output_Read(filp, buf, count, f_pos, module_buf);
}
#else
return output_Read(filp, buf, count, f_pos, module_buf);
#endif
}
/* ------------------------------------------------------------------------- */
/*!
* @fn ssize_t OUTPUT_Sample_Read(struct file *filp,
* char *buf,
* size_t count,
* loff_t *f_pos)
*
* @brief Return a sample buffer to user-mode. If not full or flush, wait
*
* @param *filp a file pointer
* @param *buf a sampling buffer
* @param count size of the user's buffer
* @param f_pos file pointer (current offset in bytes)
* @param buf the kernel output buffer structure
*
* @return number of bytes read. zero indicates end of file. Neg means error
*
* Place no more than count bytes into the user's buffer.
* Block on "BUFFER_DESC_queue(kernel_buf)" if buffer isn't full.
*
* <I>Special Notes:</I>
*
*/
extern ssize_t
OUTPUT_Sample_Read (
struct file *filp,
char *buf,
size_t count,
loff_t *f_pos
)
{
int i;
i = iminor(filp->f_dentry->d_inode); // kernel pointer - not user pointer
SEP_PRINT_DEBUG("read request for samples on minor %d\n", i);
#if defined(CONTINUOUS_PROFILER)
if (cp) {
return output_Read_Cp(filp, buf, count, f_pos, &(cpu_buf[i]));
}
else {
return output_Read(filp, buf, count, f_pos, &(cpu_buf[i]));
}
#else
return output_Read(filp, buf, count, f_pos, &(cpu_buf[i]));
#endif
}
/*
* @fn output_Initialized_Buffers()
*
* @result OUTPUT
* @param BUFFER_DESC desc - descriptor for the buffer being initialized
* @param U32 factor - multiplier for OUTPUT_BUFFER_SIZE.
* 1 for cpu buffers, 2 for module buffers.
*
* @brief Allocate, initialize, and return an output data structure
*
* <I>Special Notes:</I>
* Multiple (OUTPUT_NUM_BUFFERS) buffers will be allocated
* Each buffer is of size (OUTPUT_BUFFER_SIZE)
* Each field in the buffer is initialized
* The event queue for the OUTPUT is initialized
*
*/
static BUFFER_DESC
output_Initialized_Buffers (
BUFFER_DESC desc,
U32 factor
)
{
OUTPUT outbuf;
int j;
/*
* Allocate the BUFFER_DESC, then allocate its buffers
*/
if (desc == NULL) {
desc = (BUFFER_DESC)CONTROL_Allocate_Memory(sizeof(BUFFER_DESC_NODE));
if (desc == NULL) {
SEP_PRINT_DEBUG("OUTPUT Initialize_Buffer: Failed Allocation\n");
return(desc);
}
}
outbuf = &(BUFFER_DESC_outbuf(desc));
spin_lock_init(&OUTPUT_buffer_lock(outbuf));
for (j = 0; j < OUTPUT_NUM_BUFFERS; j++) {
if (OUTPUT_buffer(outbuf,j) == NULL) {
OUTPUT_buffer(outbuf,j) = CONTROL_Allocate_Memory(OUTPUT_BUFFER_SIZE * factor);
}
OUTPUT_buffer_full(outbuf,j) = 0;
if (!OUTPUT_buffer(outbuf,j)) {
SEP_PRINT_DEBUG("OUTPUT Initialize_Buffer: Failed Allocation\n");
/*return NULL to tell the caller that allocation failed*/
return NULL;
}
}
/*
* Initialize the remaining fields in the BUFFER_DESC
*/
OUTPUT_current_buffer(outbuf) = 0;
OUTPUT_signal_full(outbuf) = FALSE;
OUTPUT_remaining_buffer_size(outbuf) = OUTPUT_BUFFER_SIZE * factor;
OUTPUT_total_buffer_size(outbuf) = OUTPUT_BUFFER_SIZE * factor;
init_waitqueue_head(&BUFFER_DESC_queue(desc));
return(desc);
}
#if defined (DRV_USE_NMI)
/* ------------------------------------------------------------------------- */
/*!
* @fn VOID output_Timer_Callback (
* )
*
* @brief Callback for output timers. The function checks if any buffers
* are full, and if full, signals the reader threads.
*
* @param none
*
* @return NONE
*
* <I>Special Notes:</I>
* This callback was added to handle out-of-band event delivery
* when running in NMI mode
*/
static void
output_Timer_Callback (
unsigned long delay
)
{
int i, n;
OUTPUT outbuf = &BUFFER_DESC_outbuf(module_buf);
if (outbuf == NULL) {
return;
}
if (OUTPUT_signal_full(outbuf)) {
wake_up_interruptible_sync(&BUFFER_DESC_queue(module_buf));
OUTPUT_signal_full(outbuf) = FALSE;
}
if (cpu_buf != NULL) {
n = GLOBAL_STATE_num_cpus(driver_state);
for (i = 0; i < n; i++) {
outbuf = &BUFFER_DESC_outbuf(&cpu_buf[i]);
if (outbuf == NULL) {
return;
}
if (OUTPUT_signal_full(outbuf)) {
wake_up_interruptible_sync(&BUFFER_DESC_queue(&cpu_buf[i]));
OUTPUT_signal_full(outbuf) = FALSE;
}
}
}
output_signal_timer->expires = jiffies + delay;
add_timer(output_signal_timer);
}
#endif
/*
* @fn extern void OUTPUT_Initialize(buffer, len)
*
* @param buffer - seed name of the output file
* @param len - length of the seed name
* @returns None
* @brief Allocate, initialize, and return all output data structure
*
* <I>Special Notes:</I>
* Initialize the output structures.
* For each CPU in the system, allocate the output buffers.
* Initialize a module buffer and temp file to hold module information
* Initialize the read queues for each sample buffer
*
*/
extern OS_STATUS
OUTPUT_Initialize (
char *buffer,
unsigned long len
)
{
BUFFER_DESC unused;
int i;
OS_STATUS status = OS_SUCCESS;
flush = 0;
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
unused = output_Initialized_Buffers(&cpu_buf[i], 1);
if (!unused) {
SEP_PRINT_ERROR("OUTPUT_Initialize: Failed to allocate cpu output buffers\n");
OUTPUT_Destroy();
return OS_NO_MEM;
}
}
/*
* Just need one module buffer
*/
module_buf = output_Initialized_Buffers(module_buf, MODULE_BUFF_SIZE);
if (!module_buf) {
SEP_PRINT_ERROR("OUTPUT_Initialize: Failed to create module output buffers\n");
OUTPUT_Destroy();
return OS_NO_MEM;
}
return status;
}
#if defined (DRV_USE_NMI)
/*
* @fn extern OS_STATUS OUTPUT_Initialize_Timers(void)
*
* @brief Allocate and initialize timers for output buffer management
*
* <I>Special Notes:</I>
* When running in NMI mode, synchronous wait/signal calls cannot be called
* to signal buffer full conditions. This timer will check if the buffer on
* any cpu is full (every second), and if it is full, signals the reader.
*
*/
extern OS_STATUS
OUTPUT_Initialize_Timers(
void
)
{
OS_STATUS status = OS_SUCCESS;
unsigned long delay = (unsigned long) msecs_to_jiffies(500);
output_signal_timer = (struct timer_list *) CONTROL_Allocate_Memory(sizeof(struct timer_list));
if (!output_signal_timer) {
SEP_PRINT_DEBUG("OUTPUT Initialize: Failed allocation for output timer\n");
OUTPUT_Destroy();
return OS_NO_MEM;
}
init_timer(output_signal_timer);
output_signal_timer->function = output_Timer_Callback;
output_signal_timer->data = delay;
output_signal_timer->expires = jiffies + delay;
add_timer(output_signal_timer);
return status;
}
/*
* @fn extern void OUTPUT_Delete_Timer()
*
* @brief Delete the timer added for buffer management
*
* <I>Special Notes:</I>
*
*/
extern void
OUTPUT_Delete_Timers(
void
)
{
del_timer(output_signal_timer);
CONTROL_Free_Memory(output_signal_timer);
output_signal_timer = NULL;
return;
}
#endif
/*
* @fn OS_STATUS OUTPUT_Flush()
*
* @brief Flush the module buffers and sample buffers
*
* @return OS_STATUS
*
* For each CPU in the system, set buffer full to the byte count to flush.
* Flush the modules buffer, as well.
*
*/
extern int
OUTPUT_Flush (
VOID
)
{
int i;
int writers = 0;
OUTPUT outbuf;
/*
* Flush all remaining data to files
* set up a flush event
*/
init_waitqueue_head(&flush_queue);
SEP_PRINT_DEBUG("flush: waiting for %d writers\n",(GLOBAL_STATE_num_cpus(driver_state)+ OTHER_C_DEVICES));
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
if (CPU_STATE_initial_mask(&pcb[i]) == 0) {
continue;
}
outbuf = &(cpu_buf[i].outbuf);
writers += 1;
#if defined(CONTINUOUS_PROFILER)
if (!cp) {
OUTPUT_buffer_full(outbuf,OUTPUT_current_buffer(outbuf)) =
OUTPUT_BUFFER_SIZE - OUTPUT_remaining_buffer_size(outbuf);
}
#else
OUTPUT_buffer_full(outbuf,OUTPUT_current_buffer(outbuf)) =
OUTPUT_total_buffer_size(outbuf) - OUTPUT_remaining_buffer_size(outbuf);
#endif
}
atomic_set(&flush_writers, writers + OTHER_C_DEVICES);
// Flip the switch to terminate the output threads
// Do not do this earlier, as threads may terminate before all the data is flushed
flush = 1;
for (i = 0; i < GLOBAL_STATE_num_cpus(driver_state); i++) {
if (CPU_STATE_initial_mask(&pcb[i]) == 0) {
continue;
}
outbuf = &BUFFER_DESC_outbuf(&cpu_buf[i]);
#if defined(CONTINUOUS_PROFILER)
if (!cp) {
OUTPUT_buffer_full(outbuf,OUTPUT_current_buffer(outbuf)) =
OUTPUT_BUFFER_SIZE - OUTPUT_remaining_buffer_size(outbuf);
}
SEP_PRINT("OUTPUT_Flush - waking up cpu_buf[%d]\n", i);
#else
OUTPUT_buffer_full(outbuf,OUTPUT_current_buffer(outbuf)) =
OUTPUT_total_buffer_size(outbuf) - OUTPUT_remaining_buffer_size(outbuf);
#endif
wake_up_interruptible_sync(&BUFFER_DESC_queue(&cpu_buf[i]));
}
// Flush all data from the module buffers
outbuf = &BUFFER_DESC_outbuf(module_buf);
#if defined(CONTINUOUS_PROFILER)
if (!cp) {
OUTPUT_buffer_full(outbuf,OUTPUT_current_buffer(outbuf)) =
OUTPUT_BUFFER_SIZE - OUTPUT_remaining_buffer_size(outbuf);
}
#else
OUTPUT_buffer_full(outbuf,OUTPUT_current_buffer(outbuf)) =
OUTPUT_total_buffer_size(outbuf) - OUTPUT_remaining_buffer_size(outbuf);
#endif
SEP_PRINT_DEBUG("OUTPUT_Flush - waking up module_queue\n");
wake_up_interruptible_sync(&BUFFER_DESC_queue(module_buf));
//Wait for buffers to empty
if (wait_event_interruptible(flush_queue, atomic_read(&flush_writers)==0)) {
return OS_RESTART_SYSCALL;
}
SEP_PRINT_DEBUG("OUTPUT_Flush - awakened from flush_queue\n");
flush = 0;
return 0;
}
/*
* @fn extern void OUTPUT_Destroy()
*
* @param buffer - seed name of the output file
* @param len - length of the seed name
* @returns OS_STATUS
* @brief Deallocate output structures
*
* <I>Special Notes:</I>
* Free the module buffers
* For each CPU in the system, free the sampling buffers
*/
extern int
OUTPUT_Destroy (
VOID
)
{
int i, n;
OUTPUT outbuf;
if (module_buf) {
outbuf = &BUFFER_DESC_outbuf(module_buf);
output_Free_Buffers(module_buf, OUTPUT_total_buffer_size(outbuf));
}
if (cpu_buf != NULL) {
n = GLOBAL_STATE_num_cpus(driver_state);
for (i = 0; i < n; i++) {
outbuf = &BUFFER_DESC_outbuf(&cpu_buf[i]);
output_Free_Buffers(&cpu_buf[i], OUTPUT_total_buffer_size(outbuf));
}
}
return 0;
}