android_kernel_modules_leno.../debug_tools/vtunedk/src/vtsspp/uec.c

302 lines
8.3 KiB
C

/*
Copyright (C) 2010-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.
*/
#include "vtss_config.h"
#include "globals.h"
#include "uec.h"
#include <linux/slab.h>
#include <asm/uaccess.h>
/**
// Universal Event Collector: system mode implementation
*/
/// for safety reasons (quick initialization)
static int put_record_stub(uec_t* uec, void *part0, size_t size0, void *part1, size_t size1, int mode)
{
return 0;
}
int init_uec(uec_t* uec, size_t size, char *name, int instance)
{
int order;
/// initialize methods
uec->put_record = put_record_async;
uec->init = init_uec;
uec->destroy = destroy_uec;
uec->pull = pull_uec;
if (size == 0) { /// change name request
ERROR("UEC size is 0");
return VTSS_ERR_INTERNAL;
}
order = get_order(size);
if (!(uec->buffer = (char*)__get_free_pages((GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN), order))) {
return VTSS_ERR_NOMEMORY;
}
uec->last_rec_ptr = uec->head = uec->head_ = uec->tail = uec->tail_ = uec->buffer;
uec->hsize = uec->tsize = (PAGE_SIZE << order);
uec->ovfl = 0;
uec->spill_active = 0;
uec->writer_count = 0;
uec->reader_count = 0;
spin_lock_init(&uec->lock);
/// notify on the creation of a new trace
// uec->callback(uec, UECCB_NEWTRACE, uec->context);
return 0;
}
void destroy_uec(uec_t* uec)
{
uec->put_record = put_record_stub;
if (uec->buffer) {
free_pages((unsigned long)uec->buffer, get_order(uec->hsize));
uec->buffer = NULL;
}
}
#define safe_memcpy(dst, src, size) memcpy(dst, src, size)
#define spill_uec() /* empty */
int put_record_async(uec_t* uec, void *part0, size_t size0, void *part1, size_t size1, int mode)
{
size_t tsize; /// total record size
size_t fsize = 0; /// free area length
size_t psize; /// partial size
size_t tmp;
char *last_rec_ptr;
char *head = 0;
char *tail = 0;
size_t hsize = 0;
unsigned long flags;
if (!uec->buffer || !part0 || !size0 || ((!size1) ^ (!part1))) {
return VTSS_ERR_BADARG;
}
tsize = size0 + size1;
/// lock UEC
uec_lock(&uec->lock);
/// sample the uec variables
head = (char*)uec->head_;
tail = (char*)uec->tail;
last_rec_ptr = head;
hsize = uec->hsize;
/// is buffer full?
if (uec->ovfl) {
*((unsigned int*)uec->last_rec_ptr) |= UEC_OVERFLOW;
/// signal overflow to overlying components
// uec->callback(uec, UECCB_OVERFLOW, uec->context);
uec_unlock(&uec->lock);
spill_uec();
return VTSS_ERR_BUFFERFULL;
}
/// compute free size
if (tail <= head) {
fsize = uec->hsize - (size_t)(head - tail);
} else {
fsize = (size_t)(tail - head);
}
/// handle 'no room' case
if (fsize < tsize) {
uec->ovfl = 1;
*((unsigned int*)uec->last_rec_ptr) |= UEC_OVERFLOW;
/// signal overflow to overlying components
// uec->callback(uec, UECCB_OVERFLOW, uec->context);
uec_unlock(&uec->lock);
spill_uec();
return VTSS_ERR_NOMEMORY;
}
/// allocate uec region
psize = (size_t)(uec->buffer + hsize - head);
if (psize > tsize) {
uec->head_ = head + tsize;
} else {
uec->head_ = uec->buffer + tsize - psize;
}
if (uec->head_ == tail) {
uec->ovfl = 1;
}
/// increment the writers' count
uec->writer_count++;
/// unlock UEC
uec_unlock(&uec->lock);
/// do the write to the allocated uec region
if (tail <= head) {
if (psize > tsize) {
memcpy(head, part0, size0);
head += size0;
if (size1) {
safe_memcpy(head, part1, size1);
head += size1;
}
} else {
tmp = size0 > psize ? psize : size0;
memcpy(head, part0, tmp);
head += tmp;
if (tmp == psize) {
head = uec->buffer;
if ((size0 - tmp) > 0) {
memcpy(head, ((char*)part0) + tmp, size0 - tmp);
head += size0 - tmp;
}
}
if (size1) {
psize -= tmp;
if (psize) {
safe_memcpy(head, part1, psize);
head = uec->buffer;
if ((size1 - psize) > 0) {
memcpy(head, ((char*)part1) + psize, size1 - psize);
head += size1 - psize;
}
} else {
safe_memcpy(head, part1, size1);
head += size1;
}
}
}
} else /// tail > head
{
memcpy(head, part0, size0);
head += size0;
if (size1) {
safe_memcpy(head, part1, size1);
head += size1;
}
}
/// lock UEC
uec_lock(&uec->lock);
/// decrement the writers' count
uec->writer_count--;
uec->last_rec_ptr = last_rec_ptr;
/// update uec variables
if (!uec->writer_count) {
uec->head = uec->head_;
}
/// unlock UEC
uec_unlock(&uec->lock);
spill_uec();
return 0;
}
int pull_uec(uec_t* uec, char __user* buffer, size_t len)
{
int rc = 0;
char *head;
char *tail;
size_t size;
int ovfl;
size_t copylen = 0;
size_t partlen;
unsigned long flags;
/// sample the UEC state, copy the sampled contents to the specified buffer,
/// and free the read part of the UEC buffer
if (!uec->buffer || !buffer || !len) {
return VTSS_ERR_BADARG;
}
/// sample data region
uec_lock(&uec->lock);
if (uec->spill_active) {
uec_unlock(&uec->lock);
return VTSS_ERR_BUSY;
}
uec->spill_active = 1;
head = (char*)uec->head;
tail = (char*)uec->tail;
size = uec->tsize;
ovfl = uec->ovfl;
if ((head == tail && !ovfl) || (head == tail && ovfl && head != uec->head_)) {
uec->spill_active = 0;
uec_unlock(&uec->lock);
return 0; ///empty
}
uec_unlock(&uec->lock);
/// spill the sampled region
if (head > tail) {
copylen = (size_t)(head - tail);
copylen = copylen > len ? len : copylen;
rc = copy_to_user(buffer, (void*)tail, copylen);
tail += copylen;
} else if (head < tail || (head == tail && ovfl)) {
copylen = partlen = (size_t)(size - (tail - uec->buffer));
copylen = copylen > len ? len : copylen;
rc = copy_to_user(buffer, (void*)tail, copylen);
tail += copylen;
if (copylen == partlen && copylen < len) {
/// copy the second part
partlen = (size_t)(head - uec->buffer);
partlen = partlen > (len - copylen) ? (len - copylen) : partlen;
rc |= copy_to_user(&((char*)buffer)[copylen], (void*)uec->buffer, partlen);
copylen += partlen;
/// assert(copylen <= len);
tail = uec->buffer + partlen;
}
}
uec_lock(&uec->lock);
uec->tail = tail;
uec->ovfl = 0;
uec->spill_active = 0;
uec_unlock(&uec->lock);
return rc ? -1 : copylen;
}