runtime/linux/alloc.c - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#ifndef _STAPLINUX_ALLOC_C_
#define _STAPLINUX_ALLOC_C_
#include <linux/percpu.h>
static int _stp_allocated_net_memory = 0;
#define STP_ALLOC_FLAGS ((GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN) \
& ~__GFP_WAIT)
#define STP_ALLOC_SLEEP_FLAGS (GFP_KERNEL | __GFP_NORETRY)
static int _stp_allocated_memory = 0;
#ifdef DEBUG_MEM
static DEFINE_SPINLOCK(_stp_mem_lock);
#define MEM_MAGIC 0xc11cf77f
#define MEM_FENCE_SIZE 32
enum _stp_memtype { MEM_KMALLOC, MEM_VMALLOC, MEM_PERCPU };
typedef struct {
char *alloc;
char *free;
} _stp_malloc_type;
static const _stp_malloc_type const _stp_malloc_types[] = {
{"kmalloc", "kfree"},
{"vmalloc", "vfree"},
{"alloc_percpu", "free_percpu"}
};
struct _stp_mem_entry {
struct list_head list;
int32_t magic;
enum _stp_memtype type;
size_t len;
void *addr;
};
#define MEM_DEBUG_SIZE (2*MEM_FENCE_SIZE+sizeof(struct _stp_mem_entry))
static LIST_HEAD(_stp_mem_list);
static void _stp_check_mem_fence (char *addr, int size)
{
char *ptr;
int i;
ptr = addr - MEM_FENCE_SIZE;
while (ptr < addr) {
if (*ptr != 0x55) {
printk("SYSTEMTAP ERROR: Memory fence corrupted before allocated memory\n");
printk("at addr %p. (Allocation starts at %p)", ptr, addr);
return;
}
ptr++;
}
ptr = addr + size;
while (ptr < addr + size + MEM_FENCE_SIZE) {
if (*ptr != 0x55) {
printk("SYSTEMTAP ERROR: Memory fence corrupted after allocated memory\n");
printk("at addr %p. (Allocation ends at %p)", ptr, addr + size - 1);
return;
}
ptr++;
}
}
static void *_stp_mem_debug_setup(void *addr, size_t size, enum _stp_memtype type)
{
struct list_head *p;
struct _stp_mem_entry *m;
memset(addr, 0x55, MEM_FENCE_SIZE);
addr += MEM_FENCE_SIZE;
memset(addr + size, 0x55, MEM_FENCE_SIZE);
p = (struct list_head *)(addr + size + MEM_FENCE_SIZE);
m = (struct _stp_mem_entry *)p;
m->magic = MEM_MAGIC;
m->type = type;
m->len = size;
m->addr = addr;
spin_lock(&_stp_mem_lock);
list_add(p, &_stp_mem_list);
spin_unlock(&_stp_mem_lock);
return addr;
}
static void _stp_mem_debug_percpu(struct _stp_mem_entry *m, void *addr, size_t size)
{
struct list_head *p = (struct list_head *)m;
m->magic = MEM_MAGIC;
m->type = MEM_PERCPU;
m->len = size;
m->addr = addr;
spin_lock(&_stp_mem_lock);
list_add(p, &_stp_mem_list);
spin_unlock(&_stp_mem_lock);
}
static void _stp_mem_debug_free(void *addr, enum _stp_memtype type)
{
int found = 0;
struct list_head *p, *tmp;
struct _stp_mem_entry *m = NULL;
spin_lock(&_stp_mem_lock);
list_for_each_safe(p, tmp, &_stp_mem_list) {
m = list_entry(p, struct _stp_mem_entry, list);
if (m->addr == addr) {
list_del(p);
found = 1;
break;
}
}
spin_unlock(&_stp_mem_lock);
if (!found) {
printk("SYSTEMTAP ERROR: Free of unallocated memory %p type=%s\n",
addr, _stp_malloc_types[type].free);
return;
}
if (m->magic != MEM_MAGIC) {
printk("SYSTEMTAP ERROR: Memory at %p corrupted!!\n", addr);
return;
}
if (m->type != type) {
printk("SYSTEMTAP ERROR: Memory allocated with %s and freed with %s\n",
_stp_malloc_types[m->type].alloc,
_stp_malloc_types[type].free);
}
switch (m->type) {
case MEM_KMALLOC:
_stp_check_mem_fence(addr, m->len);
kfree(addr - MEM_FENCE_SIZE);
break;
case MEM_PERCPU:
free_percpu(addr);
kfree(p);
break;
case MEM_VMALLOC:
_stp_check_mem_fence(addr, m->len);
vfree(addr - MEM_FENCE_SIZE);
break;
default:
printk("SYSTEMTAP ERROR: Attempted to free memory at addr %p len=%d with unknown allocation type.\n", addr, (int)m->len);
}
return;
}
static void _stp_mem_debug_validate(void *addr)
{
int found = 0;
struct list_head *p, *tmp;
struct _stp_mem_entry *m = NULL;
spin_lock(&_stp_mem_lock);
list_for_each_safe(p, tmp, &_stp_mem_list) {
m = list_entry(p, struct _stp_mem_entry, list);
if (m->addr == addr) {
found = 1;
break;
}
}
spin_unlock(&_stp_mem_lock);
if (!found) {
printk("SYSTEMTAP ERROR: Couldn't validate memory %p\n",
addr);
return;
}
if (m->magic != MEM_MAGIC) {
printk("SYSTEMTAP ERROR: Memory at %p corrupted!!\n", addr);
return;
}
switch (m->type) {
case MEM_KMALLOC:
_stp_check_mem_fence(addr, m->len);
break;
case MEM_PERCPU:
break;
case MEM_VMALLOC:
_stp_check_mem_fence(addr, m->len);
break;
default:
printk("SYSTEMTAP ERROR: Attempted to validate memory at addr %p len=%d with unknown allocation type.\n", addr, (int)m->len);
}
return;
}
#endif
#ifdef STP_MAXMEMORY
#ifndef STAPCONF_GRSECURITY
#define _STP_MODULE_CORE_SIZE (THIS_MODULE->core_size)
#else
#define _STP_MODULE_CORE_SIZE (THIS_MODULE->core_size_rw)
#endif
#endif
static void *_stp_kmalloc_gfp(size_t size, gfp_t gfp_mask)
{
void *ret;
#ifdef STP_MAXMEMORY
if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size)
> (STP_MAXMEMORY * 1024)) {
return NULL;
}
#endif
#ifdef DEBUG_MEM
ret = kmalloc(size + MEM_DEBUG_SIZE, gfp_mask);
if (likely(ret)) {
_stp_allocated_memory += size;
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
}
#else
ret = kmalloc(size, gfp_mask);
if (likely(ret)) {
_stp_allocated_memory += size;
}
#endif
return ret;
}
static void *_stp_kmalloc(size_t size)
{
return _stp_kmalloc_gfp(size, STP_ALLOC_FLAGS);
}
static void *_stp_kzalloc_gfp(size_t size, gfp_t gfp_mask)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
{
void *ret;
#ifdef STP_MAXMEMORY
if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size)
> (STP_MAXMEMORY * 1024)) {
return NULL;
}
#endif
#ifdef DEBUG_MEM
ret = kmalloc(size + MEM_DEBUG_SIZE, gfp_mask);
if (likely(ret)) {
_stp_allocated_memory += size;
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
memset (ret, 0, size);
}
#else
ret = kmalloc(size, gfp_mask);
if (likely(ret)) {
_stp_allocated_memory += size;
memset (ret, 0, size);
}
#endif return ret;
}
#else {
void *ret;
#ifdef STP_MAXMEMORY
if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size)
> (STP_MAXMEMORY * 1024)) {
return NULL;
}
#endif
#ifdef DEBUG_MEM
ret = kzalloc(size + MEM_DEBUG_SIZE, gfp_mask);
if (likely(ret)) {
_stp_allocated_memory += size;
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
}
#else
ret = kzalloc(size, gfp_mask);
if (likely(ret)) {
_stp_allocated_memory += size;
}
#endif
return ret;
}
#endif
static void *_stp_kzalloc(size_t size)
{
return _stp_kzalloc_gfp(size, STP_ALLOC_FLAGS);
}
#ifndef STAPCONF_VZALLOC
static void *vzalloc(unsigned long size)
{
void *ret = vmalloc(size);
if (ret)
memset(ret, 0, size);
return ret;
}
#endif
static void *_stp_vzalloc(size_t size)
{
void *ret;
#ifdef STP_MAXMEMORY
if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size)
> (STP_MAXMEMORY * 1024)) {
return NULL;
}
#endif
#ifdef DEBUG_MEM
ret = vzalloc(size + MEM_DEBUG_SIZE);
if (likely(ret)) {
_stp_allocated_memory += size;
ret = _stp_mem_debug_setup(ret, size, MEM_VMALLOC);
}
#else
ret = vzalloc(size);
if (likely(ret)) {
_stp_allocated_memory += size;
}
#endif
return ret;
}
#ifndef STAPCONF_VMALLOC_NODE
static void *vmalloc_node(unsigned long size, int node __attribute__((unused)))
{
return vmalloc(size);
}
#endif
#ifndef STAPCONF_VZALLOC_NODE
static void *vzalloc_node(unsigned long size, int node)
{
void *ret = vmalloc_node(size, node);
if (ret)
memset(ret, 0, size);
return ret;
}
#endif
static void *_stp_vzalloc_node(size_t size, int node)
{
void *ret;
#ifdef STP_MAXMEMORY
if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size)
> (STP_MAXMEMORY * 1024)) {
return NULL;
}
#endif
#ifdef DEBUG_MEM
ret = vzalloc_node(size + MEM_DEBUG_SIZE, node);
if (likely(ret)) {
_stp_allocated_memory += size;
ret = _stp_mem_debug_setup(ret, size, MEM_VMALLOC);
}
#else
ret = vzalloc_node(size, node);
if (likely(ret)) {
_stp_allocated_memory += size;
}
#endif
return ret;
}
#ifdef PCPU_MIN_UNIT_SIZE
#define _STP_MAX_PERCPU_SIZE PCPU_MIN_UNIT_SIZE
#else
#define _STP_MAX_PERCPU_SIZE 131072
#endif
static void *_stp_alloc_percpu(size_t size)
{
void *ret;
if (size > _STP_MAX_PERCPU_SIZE)
return NULL;
#ifdef STP_MAXMEMORY
if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory
+ (size * num_online_cpus()))
> (STP_MAXMEMORY * 1024)) {
return NULL;
}
#endif
#ifdef STAPCONF_ALLOC_PERCPU_ALIGN
ret = __alloc_percpu(size, 8);
#else
ret = __alloc_percpu(size);
#endif
#ifdef DEBUG_MEM
if (likely(ret)) {
struct _stp_mem_entry *m = kmalloc(sizeof(struct _stp_mem_entry), GFP_KERNEL);
if (unlikely(m == NULL)) {
free_percpu(ret);
return NULL;
}
_stp_allocated_memory += size * num_online_cpus();
_stp_mem_debug_percpu(m, ret, size);
}
#else
if (likely(ret)) {
_stp_allocated_memory += size * num_online_cpus();
}
#endif
return ret;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
#define _stp_kmalloc_node(size,node) _stp_kmalloc(size)
#define _stp_kmalloc_node_gfp(size,node,gfp) _stp_kmalloc_gfp(size,gfp)
#define _stp_kzalloc_node(size,node) _stp_kzalloc(size)
#define _stp_kzalloc_node_gfp(size,node,gfp) _stp_kzalloc_gfp(size,gfp)
#else
static void *_stp_kmalloc_node_gfp(size_t size, int node, gfp_t gfp_mask)
{
void *ret;
#ifdef STP_MAXMEMORY
if ((_STP_MODULE_CORE_SIZE + _stp_allocated_memory + size)
> (STP_MAXMEMORY * 1024)) {
return NULL;
}
#endif
#ifdef DEBUG_MEM
ret = kmalloc_node(size + MEM_DEBUG_SIZE, gfp_mask, node);
if (likely(ret)) {
_stp_allocated_memory += size;
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
}
#else
ret = kmalloc_node(size, gfp_mask, node);
if (likely(ret)) {
_stp_allocated_memory += size;
}
#endif
return ret;
}
static void *_stp_kzalloc_node_gfp(size_t size, int node, gfp_t gfp_mask)
{
void *ret = _stp_kmalloc_node_gfp(size, node, gfp_mask);
if (likely(ret)) {
memset (ret, 0, size);
}
return ret;
}
static void *_stp_kmalloc_node(size_t size, int node)
{
return _stp_kmalloc_node_gfp(size, node, STP_ALLOC_FLAGS);
}
static void *_stp_kzalloc_node(size_t size, int node)
{
return _stp_kzalloc_node_gfp(size, node, STP_ALLOC_FLAGS);
}
#endif
static void _stp_kfree(void *addr)
{
#ifdef DEBUG_MEM
_stp_mem_debug_free(addr, MEM_KMALLOC);
#else
kfree(addr);
#endif
}
static void _stp_vfree(void *addr)
{
#ifdef DEBUG_MEM
_stp_mem_debug_free(addr, MEM_VMALLOC);
#else
vfree(addr);
#endif
}
static void _stp_free_percpu(void *addr)
{
#ifdef DEBUG_MEM
_stp_mem_debug_free(addr, MEM_PERCPU);
#else
free_percpu(addr);
#endif
}
static void _stp_mem_debug_done(void)
{
#ifdef DEBUG_MEM
struct list_head *p, *tmp;
struct _stp_mem_entry *m;
spin_lock(&_stp_mem_lock);
list_for_each_safe(p, tmp, &_stp_mem_list) {
m = list_entry(p, struct _stp_mem_entry, list);
list_del(p);
printk("SYSTEMTAP ERROR: Memory %p len=%d allocation type: %s. Not freed.\n",
m->addr, (int)m->len, _stp_malloc_types[m->type].alloc);
if (m->magic != MEM_MAGIC) {
printk("SYSTEMTAP ERROR: Memory at %p len=%d corrupted!!\n", m->addr, (int)m->len);
goto done;
}
switch (m->type) {
case MEM_KMALLOC:
_stp_check_mem_fence(m->addr, m->len);
kfree(m->addr - MEM_FENCE_SIZE);
break;
case MEM_PERCPU:
free_percpu(m->addr);
kfree(p);
break;
case MEM_VMALLOC:
_stp_check_mem_fence(m->addr, m->len);
vfree(m->addr - MEM_FENCE_SIZE);
break;
default:
printk("SYSTEMTAP ERROR: Attempted to free memory at addr %p len=%d with unknown allocation type.\n", m->addr, (int)m->len);
}
}
done:
spin_unlock(&_stp_mem_lock);
return;
#endif
}
#endif