kexec-tools: Add generic range code This patch contains the core of the range handling code. All functions operate on struct range which in this implementation is a sorted single linked list. Scalability is not an issue here -> linked list.
Signed-off-by: Magnus Damm <[EMAIL PROTECTED]> --- kexec/Makefile | 1 kexec/range.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kexec/range.h | 32 +++++++ 3 files changed, 281 insertions(+) --- 0001/kexec/Makefile +++ work/kexec/Makefile 2006-12-22 15:12:53.000000000 +0900 @@ -11,6 +11,7 @@ KCFLAGS:= $(CFLAGS) $(EXTRA_CFLAGS) -Ike KEXEC_C_SRCS:= kexec/kexec.c KEXEC_C_SRCS+= kexec/ifdown.c +KEXEC_C_SRCS+= kexec/range.c KEXEC_C_SRCS+= kexec/kexec-elf.c KEXEC_C_SRCS+= kexec/kexec-elf-exec.c KEXEC_C_SRCS+= kexec/kexec-elf-core.c --- /dev/null +++ work/kexec/range.c 2006-12-22 16:43:42.000000000 +0900 @@ -0,0 +1,248 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include "kexec.h" +#include "crashdump.h" +#include "range.h" + +/* private data structures and constants */ + +struct range_link { + struct range_link *next; + unsigned long start; + unsigned long size; + int type; +}; + +#define MODE_SPACE 0 +#define MODE_OVERLAP 1 +#define MODE_INCLUDED 2 +#define MODE_INCLUDED_SPLIT 3 + +int range_set(struct range *range, + unsigned long start, unsigned long size, + int type) +{ + struct range_link **p; + struct range_link *r = NULL; + struct range_link *r2; + struct range_link *new_links[2]; + unsigned long tmp; + int k, n; + int mode = MODE_SPACE; + + dfprintf(stdout, "inserting range %ld, %ld, %d\n", start, size, type); + + /* find first overlapping link */ + + for (p = &range->first; *p; p = &(*p)->next) { + if (start > ((*p)->start + (*p)->size)) + continue; + + if ((start + size) > (*p)->start) + mode = MODE_OVERLAP; + + if ((start >= (*p)->start) + && (start + size) <= ((*p)->start + (*p)->size)) + mode = MODE_INCLUDED; + + if ((start > (*p)->start) + && (start + size) < ((*p)->start + (*p)->size)) + mode = MODE_INCLUDED_SPLIT; + + r = *p; + break; + } + + dfprintf(stdout, "mode = %d\n", mode); + + if (mode != MODE_SPACE && type != RANGE_NOTHING && r->type == type) { + + /* handle no-alloc merge case */ + + if (mode == MODE_INCLUDED || mode == MODE_INCLUDED_SPLIT) { + dfprintf(stdout, "range already set, nothing to do\n"); + return 0; + } + + /* expand existing link */ + + if (start < r->start) { + dfprintf(stdout, "merge mode, adjusting left\n"); + r->size += r->start - start; + r->start = start; + } + + if ((start + size) > (r->start + r->size)) { + dfprintf(stdout, "merge mode, adjusting right\n"); + r->size = size + (start - r->start); + } + + p = &r->next; + } + else { + /* allocate links first for easy error handling */ + + n = (type != RANGE_NOTHING) + (mode == MODE_INCLUDED_SPLIT); + + for (k = 0; k < n; k++) { + r2 = malloc(sizeof(*r2)); + if (!r2) { + while (k > 0) { + free(new_links[k - 1]); + k--; + } + + dfprintf(stdout, "out of memory\n"); + return -1; + } + + memset(r2, 0, sizeof(*r2)); + new_links[k] = r2; + + dfprintf(stdout, "allocated link %d\n", k); + } + + if (mode == MODE_INCLUDED_SPLIT) { + dfprintf(stdout, "INCLUDED_SPLIT, allocated link %d\n", + type != RANGE_NOTHING); + + /* create a new link for the first part */ + + r2 = new_links[type != RANGE_NOTHING]; + + r2->start = r->start; + r2->size = start - r->start; + r2->type = r->type; + r2->next = *p; + + dfprintf(stdout, "first new link is %ld, %ld, %d\n", + r2->start, r2->size, r2->type); + + /* cut & merge code below handles the second link */ + + *p = r2; + p = &r2->next; + } + + if ((mode == MODE_OVERLAP || mode == MODE_INCLUDED) + && r->start < start) { + dfprintf(stdout, "OVERLAP/INCLUDED, adjusting link\n"); + r->size = start - r->start; + p = &r->next; + } + + r = NULL; + + if (type != RANGE_NOTHING) { + dfprintf(stdout, "using allocated link 0\n"); + + r = new_links[0]; + + r->start = start; + r->size = size; + r->type = type; + r->next = *p; + *p = r; + + p = &r->next; + } + } + + dfprintf(stdout, "(*p)->start = %ld\n", (*p) ? (*p)->start : -1); + + /* remove links */ + + while (*p) { + tmp = (*p)->start + (*p)->size; + + if ((start + size) <= tmp) { + + /* cut or merge final partially overlapping link */ + + if ((start + size) < (*p)->start) + break; + + if ((type != (*p)->type) || !r) { + dfprintf(stdout, "cutting final link %ld!\n", + (*p)->start); + + (*p)->size = tmp - (start + size); + (*p)->start = start + size; + + if ((*p)->size != 0) + break; + } + else { + dfprintf(stdout, "merging tail link %ld!\n", + (*p)->start); + + r->size = tmp - r->start; + } + } + + dfprintf(stdout, "removing link %ld!\n", (*p)->start); + + r2 = (*p)->next; + free(*p); + *p = r2; + } + + return 0; +} + +int range_for_each(struct range *range, + int (*callback)(void *data, + int nr, + unsigned long start, + unsigned long size, + int type), + void *data) +{ + struct range_link *p; + int k = 0; + int ret = 0; + + for (p = range->first; p; p = p->next) { + + ret = callback(data, k, p->start, p->size, p->type); + if (ret < 0) + return ret; + + k++; + } + + return k; +} + +static void range_dump_callback(void *data, + int nr, + unsigned long start, + unsigned long size, + int type) +{ + fprintf(stdout, "%d: %ld - %ld (%ld): %d\n", + nr, start, start + size - 1, size, type); +} + +void range_dump(struct range *range, char *str) +{ + fprintf(stdout, "range_dump: %s\n", str); + + range_for_each(range, range_dump_callback, NULL); + + fprintf(stdout, "range_dump: %s - done\n", str); +} + +void range_cleanup(struct range *range) +{ + range_set(range, 0, ~0, RANGE_NOTHING); +} --- /dev/null +++ work/kexec/range.h 2006-12-22 15:12:54.000000000 +0900 @@ -0,0 +1,32 @@ +#ifndef RANGE_H +#define RANGE_H + +struct range_link; + +struct range { + struct range_link *first; +}; + +#define RANGE_NOTHING 0 + +static inline void range_init(struct range *range) +{ + range->first = NULL; +} + +int range_set(struct range *range, + unsigned long start, unsigned long size, + int type); + +void range_dump(struct range *range, char *str); +void range_cleanup(struct range *range); + +int range_for_each(struct range *range, + int (*callback)(void *data, + int nr, + unsigned long start, + unsigned long size, + int type), + void *data); + +#endif /* RANGE_H */ _______________________________________________ fastboot mailing list fastboot@lists.osdl.org https://lists.osdl.org/mailman/listinfo/fastboot