diff options
author | Tim Moore <moore@blackbox.bricoworks.com> | 2008-09-08 16:14:03 +0200 |
---|---|---|
committer | Tim Moore <moore@blackbox.bricoworks.com> | 2008-09-08 16:14:03 +0200 |
commit | e6342ff8c11bbed1d2058507b918cf3c140e1322 (patch) | |
tree | 4c7db07136368155ea272f848d5ac46819a6ae84 /runtime/addr-map.c | |
parent | e36b5fc35d4033d5718eb8e101968b79d0360202 (diff) | |
download | systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.tar.gz systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.tar.xz systemtap-steved-e6342ff8c11bbed1d2058507b918cf3c140e1322.zip |
PR1288: runtime functions for avoiding certain addresses
Diffstat (limited to 'runtime/addr-map.c')
-rw-r--r-- | runtime/addr-map.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/runtime/addr-map.c b/runtime/addr-map.c new file mode 100644 index 00000000..8231b57f --- /dev/null +++ b/runtime/addr-map.c @@ -0,0 +1,181 @@ +/* -*- linux-c -*- + * Map of addresses to disallow. + * Copyright (C) 2005-2008 Red Hat Inc. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#ifndef _ADDR_MAP_C_ +#define _ADDR_MAP_C_ 1 + +/** @file addr-map + * @brief Implements functions used by the deref macro to blacklist + * certain addresses. + */ + +struct addr_map_entry +{ + unsigned long min; + unsigned long max; +}; + +struct addr_map +{ + size_t size; + struct addr_map_entry entries[0]; +}; + +static DEFINE_SPINLOCK(addr_map_lock); + +struct addr_map* blackmap; + +/* Find address of entry where we can insert a new one. */ +static size_t +upper_bound(unsigned long addr, struct addr_map* map) +{ + size_t start = 0; + size_t end = map->size; + struct addr_map_entry *entry = 0; + + if (end == 0) + return 0; + do + { + size_t new_idx; + if (addr < map->entries[start].min) + return start; + if (addr >= map->entries[end-1].max) + return end; + new_idx = (end + start) / 2; + entry = &map->entries[new_idx]; + if (addr < entry->min) + end = new_idx; + else + start = new_idx + 1; + } while (end != start); + return entry - &map->entries[0]; +} + +static struct addr_map_entry* +lookup_addr_aux(unsigned long addr, struct addr_map* map) +{ + size_t start = 0; + size_t end; + if (!map) + return 0; + end = map->size; + if (map->size == 0) + return 0; + + do + { + int entry_idx; + struct addr_map_entry *entry = 0; + if (addr < map->entries[start].min || addr >= map->entries[end - 1].max) + return 0; + entry_idx = (end + start) / 2; + entry = &map->entries[entry_idx]; + if (entry->min <= addr && entry->max > addr) + return entry; + if (addr < entry->min) + end = entry_idx; + else + start = entry_idx + 1; + } while (start < end); + return 0; +} + +int +lookup_bad_addr(unsigned long addr) +{ + struct addr_map_entry* result = 0; + spin_lock(&addr_map_lock); + result = lookup_addr_aux(addr, blackmap); + spin_unlock(&addr_map_lock); + if (result) + return 1; + else + return 0; +} + + +int +add_bad_addr_entry(unsigned long min_addr, unsigned long max_addr, + struct addr_map_entry** existing_min, + struct addr_map_entry** existing_max) +{ + struct addr_map* new_map = 0; + struct addr_map* old_map = 0; + struct addr_map_entry* min_entry = 0; + struct addr_map_entry* max_entry = 0; + struct addr_map_entry* new_entry = 0; + size_t existing = 0; + + while (1) + { + size_t old_size; + spin_lock(&addr_map_lock); + old_map = blackmap; + if (!blackmap) + { + existing = 0; + old_size = 0; + } + else + { + min_entry = lookup_addr_aux(min_addr, blackmap); + max_entry = lookup_addr_aux(max_addr, blackmap); + if (min_entry || max_entry) + { + if (existing_min) + *existing_min = min_entry; + if (existing_max) + *existing_max = max_entry; + spin_unlock(&addr_map_lock); + return 1; + } + existing = upper_bound(min_addr, old_map); + old_size = old_map->size; + } + spin_unlock(&addr_map_lock); + new_map = kmalloc(sizeof(*new_map) + + sizeof(*new_entry) * (old_size + 1), + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + spin_lock(&addr_map_lock); + if (blackmap != old_map) + { + kfree(new_map); + spin_unlock(&addr_map_lock); + continue; + } + new_entry = &new_map->entries[existing]; + new_entry->min = min_addr; + new_entry->max = max_addr; + if (old_map) + { + memcpy(&new_map->entries, old_map->entries, + existing * sizeof(*new_entry)); + if (old_map->size > existing) + memcpy(new_entry + 1, &old_map->entries[existing + 1], + (old_map->size - existing) * sizeof(*new_entry)); + } + new_map->size = blackmap->size + 1; + blackmap = new_map; + spin_unlock(&addr_map_lock); + if (old_map) + kfree(old_map); + return 0; + } +} + +void +delete_bad_addr_entry(struct addr_map_entry* entry) +{ +} + +#endif |