Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 3 Feb 2023 15:50:35 GMT
From:      Warner Losh <imp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 08779e839ac1 - main - kboot: Create segment handling code at main level
Message-ID:  <202302031550.313FoZ0k022932@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=08779e839ac11c410d314ce225e2246ac4d2e8c0

commit 08779e839ac11c410d314ce225e2246ac4d2e8c0
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2023-02-03 15:37:53 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2023-02-03 15:41:39 +0000

    kboot: Create segment handling code at main level
    
    Create segment handling code up to the top level. Move it all into
    seg.c, and make necessary adjustments for it being in a new file,
    including inventing print_avail() and first_avail() to print the array
    and find the first large enough memory hole.  aarch64 will use this,
    and I'll refactor the other platforms to use it as I make them work.
    
    Sponsored by:           Netflix
    Discussed with:         kevans
    Differential Revision:  https://reviews.freebsd.org/D38308
---
 stand/kboot/Makefile |   1 +
 stand/kboot/kboot.h  |   8 +++
 stand/kboot/seg.c    | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 206 insertions(+)

diff --git a/stand/kboot/Makefile b/stand/kboot/Makefile
index 35d71cb064fc..cc972bc06ae3 100644
--- a/stand/kboot/Makefile
+++ b/stand/kboot/Makefile
@@ -30,6 +30,7 @@ SRCS=	\
 		init.c \
 		kbootfdt.c \
 		main.c \
+		seg.c \
 		termios.c \
 		util.c \
 		vers.c
diff --git a/stand/kboot/kboot.h b/stand/kboot/kboot.h
index c50f3f7d53a1..2d89933b51aa 100644
--- a/stand/kboot/kboot.h
+++ b/stand/kboot/kboot.h
@@ -35,6 +35,14 @@ const char *hostdisk_gen_probe(void);
 void hostdisk_zfs_probe(void);
 bool hostdisk_zfs_find_default(void);
 
+/* seg.c */
+void init_avail(void);
+void need_avail(int n);
+void add_avail(uint64_t start, uint64_t end, uint64_t type);
+void remove_avail(uint64_t start, uint64_t end, uint64_t type);
+uint64_t first_avail(uint64_t align, uint64_t min_size, uint64_t type);
+void print_avail(void);
+
 /* util.c */
 bool file2str(const char *fn, char *buffer, size_t buflen);
 bool file2u64(const char *fn, uint64_t *val);
diff --git a/stand/kboot/seg.c b/stand/kboot/seg.c
new file mode 100644
index 000000000000..41ded9b4083c
--- /dev/null
+++ b/stand/kboot/seg.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2023, Netflix, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "stand.h"
+#include "kboot.h"
+
+#include <sys/param.h>
+
+static struct memory_segments *segs;
+static int nr_seg = 0;
+static int segalloc = 0;
+
+void
+init_avail(void)
+{
+	if (segs)
+		free(segs);
+	nr_seg = 0;
+	segalloc = 16;
+	segs = malloc(sizeof(*segs) * segalloc);
+	if (segs == NULL)
+		panic("not enough memory to get memory map\n");
+}
+
+/*
+ * Make sure at least n items can be accessed in the segs array.  Note the
+ * realloc here will invalidate cached pointers (potentially), so addresses
+ * into the segs array must be recomputed after this call.
+ */
+void
+need_avail(int n)
+{
+	if (n <= segalloc)
+		return;
+
+	while (n > segalloc)
+		segalloc *= 2;
+	segs = realloc(segs, segalloc * sizeof(*segs));
+	if (segs == NULL)
+		panic("not enough memory to get memory map\n");
+}
+
+/*
+ * Always called for a new range, so always just append a range,
+ * unless it's continuous with the prior range.
+ */
+void
+add_avail(uint64_t start, uint64_t end, uint64_t type)
+{
+	/*
+	 * This range is contiguous with the previous range, and is
+	 * the same type: we can collapse the two.
+	 */
+	if (nr_seg >= 1 &&
+	    segs[nr_seg - 1].end + 1 == start &&
+	    segs[nr_seg - 1].type == type) {
+		segs[nr_seg - 1].end = end;
+		return;
+	}
+
+	/*
+	 * Otherwise we need to add a new range at the end, but don't need to
+	 * adjust the current end.
+	 */
+	need_avail(nr_seg + 1);
+	segs[nr_seg].start = start;
+	segs[nr_seg].end = end;
+	segs[nr_seg].type = type;
+	nr_seg++;
+}
+
+/*
+ * All or part of a prior entry needs to be modified. Given the structure of the
+ * code, we know that it will always be modifying the last time and/or extending
+ * the one before it if its contiguous.
+ */
+void
+remove_avail(uint64_t start, uint64_t end, uint64_t type)
+{
+	struct memory_segments *s;
+
+	/*
+	 * simple case: we are extending a previously removed item.
+	 */
+	if (nr_seg >= 2) {
+		s = &segs[nr_seg - 2];
+		if (s->end + 1 == start &&
+		    s->type == type) {
+			s->end = end;
+			/* Now adjust the ending element */
+			s++;
+			if (s->end == end) {
+				/* we've used up the 'free' space */
+				nr_seg--;
+				return;
+			}
+			/* Otherwise adjust the 'free' space */
+			s->start = end + 1;
+			return;
+		}
+	}
+
+	/*
+	 * OK, we have four cases:
+	 * (1) The new chunk is at the start of the free space, but didn't catch the above
+	 *     folding for whatever reason (different type, start of space). In this case,
+	 *     we allocate 1 additional item. The current end is copied to the new end. The
+	 *     current end is set to <start, end, type> and the new end's start is set to end + 1.
+	 * (2) The new chunk is in the middle of the free space. In this case we allocate 2
+	 *     additional items. We copy the current end to the new end, set the new end's start
+	 *     to end + 1, the old end's end to start - 1 and the new item is <start, end, type>
+	 * (3) The new chunk is at the end of the current end. In this case we allocate 1 more
+	 *     and adjust the current end's end to start - 1 and set the new end to <start, end, type>.
+	 * (4) The new chunk is exactly the current end, except for type. In this case, we just adjust
+	 *     the type.
+	 * We can assume we always have at least one chunk since that's created with new_avail() above
+	 * necessarily before we are called to subset it.
+	 */
+	s = &segs[nr_seg - 1];
+	if (s->start == start) {
+		if (s->end == end) { /* (4) */
+			s->type = type;
+			return;
+		}
+		/* chunk at start of old chunk -> (1) */
+		need_avail(nr_seg + 1);
+		s = &segs[nr_seg - 1];	/* Realloc may change pointers */
+		s[1] = s[0];
+		s->start = start;
+		s->end = end;
+		s->type = type;
+		s[1].start = end + 1;
+		nr_seg++;
+		return;
+	}
+	if (s->end == end) {	/* At end of old chunk (3) */
+		need_avail(nr_seg + 1);
+		s = &segs[nr_seg - 1];	/* Realloc may change pointers */
+		s[1] = s[0];
+		s->end = start - 1;
+		s[1].start = start;
+		s[1].type = type;
+		nr_seg++;
+		return;
+	}
+	/* In the middle, need to split things up (2) */
+	need_avail(nr_seg + 2);
+	s = &segs[nr_seg - 1];	/* Realloc may change pointers */
+	s[2] = s[1] = s[0];
+	s->end = start - 1;
+	s[1].start = start;
+	s[1].end = end;
+	s[1].type = type;
+	s[2].start = end + 1;
+	nr_seg += 2;
+}
+
+void
+print_avail(void)
+{
+	printf("Found %d RAM segments:\n", nr_seg);
+
+	for (int i = 0; i < nr_seg; i++) {
+		printf("%#jx-%#jx type %lu\n",
+		    (uintmax_t)segs[i].start,
+		    (uintmax_t)segs[i].end,
+		    (u_long)segs[i].type);
+	}
+}
+
+uint64_t
+first_avail(uint64_t align, uint64_t min_size, uint64_t memtype)
+{
+	uint64_t s, len;
+
+	for (int i = 0; i < nr_seg; i++) {
+		if (segs[i].type != memtype)	/* Not candidate */
+			continue;
+		s = roundup(segs[i].start, align);
+		if (s >= segs[i].end)		/* roundup past end */
+			continue;
+		len = segs[i].end - s + 1;
+		if (len >= min_size) {
+			printf("Found a big enough hole at in seg %d at %#jx (%#jx-%#jx)\n",
+			    i,
+			    (uintmax_t)s,
+			    (uintmax_t)segs[i].start,
+			    (uintmax_t)segs[i].end);
+			return (s);
+		}
+	}
+
+	return (0);
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202302031550.313FoZ0k022932>