1/*
2 * Copyright (c) 2007, 2008, 2009, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <unistd.h>
11#include <barrelfish/barrelfish.h>
12#include <barrelfish/cpu_arch.h>
13#include <posixcompat.h> // for sbrk_get_*() declarations
14
15// Uncomment this if you want to measure sbrk() collective runtime
16// TODO: make this a Config.hs flag maybe? -SG, 2018-10-18.
17// #define SBRK_COLLECT_STATS
18
19#if __SIZEOF_POINTER__ == 8
20#ifdef __x86_64__
21// Large memory area + large pages on x86_64
22#define SBRK_REGION_BYTES (128UL * HUGE_PAGE_SIZE) // 64GB
23#define SBRK_FLAGS (VREGION_FLAGS_READ_WRITE | VREGION_FLAGS_LARGE)
24#define SBRK_MIN_MAPPING (16 * LARGE_PAGE_SIZE)
25#define SBRK_REGION_ALIGNMENT LARGE_PAGE_SIZE
26#else
27// Large memory area + normal pages
28#define SBRK_REGION_BYTES (8UL * 512UL * LARGE_PAGE_SIZE) // 8GB
29#define SBRK_FLAGS (VREGION_FLAGS_READ_WRITE)
30#define SBRK_MIN_MAPPING (16 * LARGE_PAGE_SIZE)
31#define SBRK_REGION_ALIGNMENT BASE_PAGE_SIZE
32#endif
33#else
34// still huge, but slightly more achievable in a 32-bit address space!
35#define SBRK_REGION_BYTES (64 * 1024 * BASE_PAGE_SIZE) // 256MB
36#define SBRK_FLAGS (VREGION_FLAGS_READ_WRITE)
37#define SBRK_MIN_MAPPING (2 * BASE_PAGE_SIZE)
38#define SBRK_REGION_ALIGNMENT BASE_PAGE_SIZE
39#endif
40
41
42static void *base = NULL;
43static size_t offset = 0; ///< How much is currently used
44static size_t goffset = 0; ///< Maximum ever allocated
45static struct memobj_append memobj_;
46static struct memobj *memobj = NULL;
47static struct vregion vregion_;
48static struct vregion *vregion = NULL;
49
50struct memobj_anon* sbrk_get_memobj(void)
51{
52    assert(memobj != NULL);
53    return (struct memobj_anon*) memobj;
54}
55
56struct vregion* sbrk_get_vregion(void)
57{
58    assert(vregion != NULL);
59    return vregion;
60}
61
62void* sbrk_get_base(void)
63{
64    assert(base != NULL);
65    return base;
66}
67
68size_t sbrk_get_offset(void)
69{
70    assert(offset != 0);
71    return offset;
72}
73
74#ifdef SBRK_COLLECT_STATS
75uint64_t sbrk_times = 0;
76
77static inline unsigned long bf_ticks(void)
78{
79   unsigned int a, d;
80   __asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
81   return ((unsigned long) a) | (((unsigned long) d) << 32);
82}
83#endif
84
85void *sbrk(intptr_t increment)
86{
87#ifdef SBRK_COLLECT_STATS
88    uint64_t start = bf_ticks();
89#endif
90    errval_t err;
91    size_t orig_offset;
92
93    /* we're using an append memobj for sbrk */
94    if (!memobj) { // Initialize
95        err = vspace_map_append_nomalloc(&base, &memobj_, &vregion_,
96                                         SBRK_REGION_BYTES, NULL,
97                                         SBRK_FLAGS, SBRK_REGION_ALIGNMENT);
98        if (err_is_fail(err)) {
99            DEBUG_ERR(err, "vspace_map_anon_nomalloc failed");
100            return (void *)-1;
101        }
102        memobj = (struct memobj *) &memobj_;
103        vregion = &vregion_;
104
105        //debug_printf("%s:%u reserved region: %p..%p\n", __FUNCTION__, __LINE__,
106        //             base, base + SBRK_REGION_BYTES);
107
108    }
109
110    if (increment < 0) {
111      if (-increment > offset) {
112        USER_PANIC("sbrk() called with negative increment beyond offset");
113      } else {
114        orig_offset = offset;
115        offset += increment;
116
117        void *ret = base + orig_offset;
118        return ret;
119      }
120    } else if (increment == 0) {
121        return base + offset;
122    } else if (offset + increment > SBRK_REGION_BYTES) {
123        debug_printf("sbrk() exceeded static region limit of %zu bytes, offset: %zu\n",
124                     (size_t)SBRK_REGION_BYTES, offset);
125        return (void *)-1;
126    } else if (offset + increment <= goffset) {
127        orig_offset = offset;
128        offset += increment;
129
130        void *ret = base + orig_offset;
131        return ret;
132    }
133
134    size_t inc_bytes = offset + increment - goffset;
135    if (inc_bytes < SBRK_MIN_MAPPING) {
136        inc_bytes = SBRK_MIN_MAPPING;
137    }
138
139
140    struct capref frame;
141    err = frame_alloc(&frame, inc_bytes, &inc_bytes);
142    if (err_is_fail(err)) {
143        debug_err(__FILE__, __func__, __LINE__, err, "frame_alloc failed");
144        return (void *)-1;
145    }
146
147    err = memobj->f.fill(memobj, goffset, frame, 0);
148    if (err_is_fail(err)) {
149        debug_err(__FILE__, __func__, __LINE__, err, "memobj->f.fill failed");
150        cap_destroy(frame);
151        return (void *)-1;
152    }
153
154    err = memobj->f.pagefault(memobj, vregion, goffset, 0);
155    if (err_is_fail(err)) {
156        debug_err(__FILE__, __func__, __LINE__, err,
157                  "memobj->f.pagefault failed");
158        return (void *)-1;
159    }
160
161    goffset += inc_bytes;
162
163    orig_offset = offset;
164    offset += increment;
165#ifdef SBRK_COLLECT_TIMES
166    uint64_t end = bf_ticks();
167    sbrk_times += end - start;
168#endif
169
170    void *ret = base + orig_offset;
171    return ret;
172}
173