char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

alex-spacemitggml-cpu: Add IME2 Instruction Support for the SpacemiT Backend (#22863)81b0d882a

char/aliceai-t5-moe
26.5 KiB760 linesraw
1#include "spine_mem_pool.h"
2
3#include "common.h"
4#include "ime_env.h"
5#include "spine_tcm.h"
6
7#include <fcntl.h>
8#include <sys/ioctl.h>
9#include <sys/mman.h>
10#include <unistd.h>
11
12#include <algorithm>
13#include <cerrno>
14#include <cstdint>
15#include <cstdlib>
16#include <limits>
17#include <memory>
18#include <mutex>
19#include <unordered_map>
20#include <vector>
21
22namespace ggml::cpu::riscv64_spacemit {
23namespace {
24
25constexpr size_t   SPINE_MEM_POOL_CHUNK_SIZE         = 512ull * 1024ull * 1024ull;
26constexpr size_t   SPINE_SHARE_MEM_POOL_CHUNK_SIZE   = 512ull * 1024ull;
27constexpr size_t   SPINE_MEM_POOL_1G_REGION_SIZE     = 1ull << 30;
28constexpr uint64_t HUGETLB_1G_FLAG_REQUIRE_PUD       = 1ull << 0;
29constexpr char     SPINE_MEM_POOL_HUGETLB_1G_DEV[]   = "/dev/hugetlb_1g";
30constexpr char     SPINE_MEM_POOL_TCM_SYNC_MEM_DEV[] = "/dev/tcm_sync_mem";
31
32struct hugetlb_1g_region {
33    uint64_t size{ 0 };
34    uint64_t dma_addr{ 0 };
35    uint64_t flags{ 0 };
36    uint64_t reserved{ 0 };
37};
38
39#define HUGETLB_1G_IOC_MAGIC 'M'
40#define HUGETLB_1G_IOC_ALLOC _IOWR(HUGETLB_1G_IOC_MAGIC, 0x00, struct hugetlb_1g_region)
41#define HUGETLB_1G_IOC_FREE  _IO(HUGETLB_1G_IOC_MAGIC, 0x01)
42
43struct free_block {
44    size_t offset{ 0 };
45    size_t size{ 0 };
46};
47
48struct pool_chunk {
49    uint8_t *               base{ nullptr };
50    size_t                  size{ 0 };
51    int                     fd{ -1 };
52    std::vector<free_block> free_blocks;
53};
54
55struct pool_allocation {
56    void * chunk_base{ nullptr };
57    size_t chunk_size{ 0 };
58    void * base{ nullptr };
59    size_t size{ 0 };
60};
61
62bool is_power_of_two(size_t value) {
63    return value != 0 && (value & (value - 1)) == 0;
64}
65
66bool align_up(size_t value, size_t alignment, size_t * aligned_value) {
67    if (aligned_value == nullptr || alignment == 0) {
68        return false;
69    }
70
71    const size_t remainder = value % alignment;
72    if (remainder == 0) {
73        *aligned_value = value;
74        return true;
75    }
76
77    const size_t padding = alignment - remainder;
78    if (value > std::numeric_limits<size_t>::max() - padding) {
79        return false;
80    }
81
82    *aligned_value = value + padding;
83    return true;
84}
85
86bool align_up_uintptr(uintptr_t value, size_t alignment, uintptr_t * aligned_value) {
87    if (aligned_value == nullptr || alignment == 0) {
88        return false;
89    }
90
91    const uintptr_t remainder = value % alignment;
92    if (remainder == 0) {
93        *aligned_value = value;
94        return true;
95    }
96
97    const uintptr_t padding = alignment - remainder;
98    if (value > std::numeric_limits<uintptr_t>::max() - padding) {
99        return false;
100    }
101
102    *aligned_value = value + padding;
103    return true;
104}
105
106class spine_mem_pool_manager {
107  public:
108    explicit spine_mem_pool_manager(size_t default_chunk_size) : default_chunk_size_(default_chunk_size) {}
109
110    virtual ~spine_mem_pool_manager() = default;
111
112    void * alloc(size_t size, size_t alignment) {
113        if (size == 0 || !is_power_of_two(alignment)) {
114            return nullptr;
115        }
116
117        size_t aligned_size = 0;
118        if (!align_up(size, alignment, &aligned_size)) {
119            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: align_up failed for size %zu alignment %zu\n", __func__, size,
120                           alignment);
121            return nullptr;
122        }
123
124        pool_allocation allocation;
125
126        std::lock_guard<std::mutex> lock(mutex_);
127
128        if (!try_alloc_locked(aligned_size, alignment, &allocation)) {
129            if (!add_chunk_locked(aligned_size, alignment)) {
130                return nullptr;
131            }
132
133            if (!try_alloc_locked(aligned_size, alignment, &allocation)) {
134                GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: allocation retry failed for size %zu alignment %zu\n",
135                               __func__, aligned_size, alignment);
136                return nullptr;
137            }
138        }
139
140        try {
141            const auto [allocation_it, inserted] = allocations_.emplace(allocation.base, allocation);
142            if (!inserted) {
143                GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: duplicate allocation key %p\n", __func__, allocation.base);
144                rollback_allocation_locked(allocation);
145                return nullptr;
146            }
147        } catch (const std::bad_alloc &) {
148            rollback_allocation_locked(allocation);
149            throw;
150        }
151
152        return allocation.base;
153    }
154
155    void free(void * base) {
156        if (base == nullptr) {
157            return;
158        }
159
160        std::lock_guard<std::mutex> lock(mutex_);
161
162        auto allocation_it = allocations_.find(base);
163        if (allocation_it == allocations_.end()) {
164            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: unknown allocation %p\n", __func__, base);
165            return;
166        }
167
168        pool_allocation allocation = allocation_it->second;
169        allocations_.erase(allocation_it);
170
171        auto chunk_it = find_chunk_locked(allocation);
172        if (chunk_it == chunks_.end()) {
173            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: unknown chunk for allocation %p size %zu\n", __func__,
174                           allocation.base, allocation.size);
175            return;
176        }
177
178        auto * chunk_base = chunk_it->base;
179        auto * alloc_base = static_cast<uint8_t *>(allocation.base);
180        if (alloc_base < chunk_base || alloc_base >= chunk_base + chunk_it->size) {
181            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: allocation %p out of chunk range %p..%p\n", __func__,
182                           allocation.base, chunk_base, chunk_base + chunk_it->size);
183            return;
184        }
185
186        const size_t offset = static_cast<size_t>(alloc_base - chunk_base);
187        if (offset > chunk_it->size || allocation.size > chunk_it->size - offset) {
188            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: allocation %p size %zu exceeds chunk size %zu\n", __func__,
189                           allocation.base, allocation.size, chunk_it->size);
190            return;
191        }
192
193        insert_free_block_locked(*chunk_it, { offset, allocation.size });
194        maybe_release_empty_chunk_locked(chunk_it);
195    }
196
197  protected:
198    void release_chunks() {
199        std::lock_guard<std::mutex> lock(mutex_);
200
201        allocations_.clear();
202        for (auto & chunk : chunks_) {
203            dealloc_chunk(&chunk);
204        }
205        chunks_.clear();
206    }
207
208    size_t default_chunk_size() const { return default_chunk_size_; }
209
210    static void clear_chunk(pool_chunk * chunk) {
211        chunk->base = nullptr;
212        chunk->size = 0;
213        chunk->fd   = -1;
214        chunk->free_blocks.clear();
215    }
216
217    virtual bool alloc_chunk(size_t min_size, size_t alignment, void * hint_addr, pool_chunk * chunk) = 0;
218    virtual void dealloc_chunk(pool_chunk * chunk)                                                    = 0;
219
220  private:
221    struct alloc_candidate {
222        size_t    chunk_index{ 0 };
223        size_t    block_index{ 0 };
224        size_t    aligned_offset{ 0 };
225        uintptr_t address{ std::numeric_limits<uintptr_t>::max() };
226        bool      valid{ false };
227    };
228
229    std::vector<pool_chunk>::iterator find_chunk_locked(const pool_allocation & allocation) {
230        return std::find_if(chunks_.begin(), chunks_.end(), [&](const pool_chunk & chunk) {
231            return chunk.base == allocation.chunk_base && chunk.size == allocation.chunk_size;
232        });
233    }
234
235    bool add_chunk_locked(size_t min_size, size_t alignment) {
236        pool_chunk   chunk;
237        const size_t chunk_request = default_chunk_size_ == 0 ? min_size : std::max(min_size, default_chunk_size_);
238        void *       hint_addr     = nullptr;
239
240        for (const auto & existing_chunk : chunks_) {
241            auto * chunk_end = existing_chunk.base + existing_chunk.size;
242            if (hint_addr == nullptr || chunk_end > hint_addr) {
243                hint_addr = chunk_end;
244            }
245        }
246
247        if (!alloc_chunk(chunk_request, alignment, hint_addr, &chunk)) {
248            return false;
249        }
250
251        if (chunk.base == nullptr || chunk.size < min_size) {
252            GGML_LOG_ERROR(
253                "CPU_RISCV64_SPACEMIT: %s: invalid chunk returned for request size %zu, chunk_base=%p chunk_size=%zu\n",
254                __func__, min_size, chunk.base, chunk.size);
255            dealloc_chunk(&chunk);
256            return false;
257        }
258
259        try {
260            chunk.free_blocks.push_back({ 0, chunk.size });
261            chunks_.push_back(std::move(chunk));
262        } catch (const std::bad_alloc &) {
263            dealloc_chunk(&chunk);
264            throw;
265        }
266
267        return true;
268    }
269
270    void rollback_allocation_locked(const pool_allocation & allocation) {
271        auto chunk_it = find_chunk_locked(allocation);
272        if (chunk_it == chunks_.end()) {
273            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: failed to rollback allocation %p, owning chunk not found\n",
274                           __func__, allocation.base);
275            return;
276        }
277
278        auto * chunk_base = chunk_it->base;
279        auto * alloc_base = static_cast<uint8_t *>(allocation.base);
280        if (alloc_base < chunk_base || alloc_base >= chunk_base + chunk_it->size) {
281            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: failed to rollback allocation %p, chunk range is invalid\n",
282                           __func__, allocation.base);
283            return;
284        }
285
286        const size_t offset = static_cast<size_t>(alloc_base - chunk_base);
287        if (offset > chunk_it->size || allocation.size > chunk_it->size - offset) {
288            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: failed to rollback allocation %p size %zu\n", __func__,
289                           allocation.base, allocation.size);
290            return;
291        }
292
293        insert_free_block_locked(*chunk_it, { offset, allocation.size });
294        maybe_release_empty_chunk_locked(chunk_it);
295    }
296
297    bool try_alloc_locked(size_t size, size_t alignment, pool_allocation * allocation) {
298        alloc_candidate best;
299
300        for (size_t chunk_index = 0; chunk_index < chunks_.size(); ++chunk_index) {
301            const auto & chunk = chunks_[chunk_index];
302            for (size_t block_index = 0; block_index < chunk.free_blocks.size(); ++block_index) {
303                const auto & block = chunk.free_blocks[block_index];
304
305                uintptr_t  aligned_addr = 0;
306                const auto block_addr   = reinterpret_cast<uintptr_t>(chunk.base + block.offset);
307                if (!align_up_uintptr(block_addr, alignment, &aligned_addr)) {
308                    continue;
309                }
310
311                if (aligned_addr < block_addr) {
312                    continue;
313                }
314
315                const size_t aligned_offset = block.offset + static_cast<size_t>(aligned_addr - block_addr);
316                const size_t padding        = aligned_offset - block.offset;
317                if (padding > block.size || size > block.size - padding) {
318                    continue;
319                }
320
321                if (!best.valid || aligned_addr < best.address) {
322                    best.chunk_index    = chunk_index;
323                    best.block_index    = block_index;
324                    best.aligned_offset = aligned_offset;
325                    best.address        = aligned_addr;
326                    best.valid          = true;
327                }
328            }
329        }
330
331        if (!best.valid) {
332            return false;
333        }
334
335        auto &           chunk     = chunks_[best.chunk_index];
336        const free_block block     = chunk.free_blocks[best.block_index];
337        const size_t     padding   = best.aligned_offset - block.offset;
338        const size_t     alloc_end = best.aligned_offset + size;
339        const size_t     block_end = block.offset + block.size;
340
341        chunk.free_blocks.erase(chunk.free_blocks.begin() + best.block_index);
342        auto insert_it = chunk.free_blocks.begin() + best.block_index;
343        if (padding != 0) {
344            insert_it = chunk.free_blocks.insert(insert_it, { block.offset, padding });
345            ++insert_it;
346        }
347        if (alloc_end < block_end) {
348            chunk.free_blocks.insert(insert_it, { alloc_end, block_end - alloc_end });
349        }
350
351        allocation->chunk_base = chunk.base;
352        allocation->chunk_size = chunk.size;
353        allocation->base       = chunk.base + best.aligned_offset;
354        allocation->size       = size;
355        return true;
356    }
357
358    void maybe_release_empty_chunk_locked(std::vector<pool_chunk>::iterator chunk_it) {
359        if (chunk_it->free_blocks.size() != 1) {
360            return;
361        }
362
363        const auto & block = chunk_it->free_blocks.front();
364        if (block.offset != 0 || block.size != chunk_it->size) {
365            return;
366        }
367
368        dealloc_chunk(&*chunk_it);
369        chunks_.erase(chunk_it);
370    }
371
372    void insert_free_block_locked(pool_chunk & chunk, free_block block) {
373        auto it = chunk.free_blocks.begin();
374        while (it != chunk.free_blocks.end() && it->offset < block.offset) {
375            ++it;
376        }
377
378        if (it != chunk.free_blocks.begin()) {
379            const auto & prev = *(it - 1);
380            if (prev.offset + prev.size > block.offset) {
381                GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: overlapping free block at offset %zu size %zu\n", __func__,
382                               block.offset, block.size);
383                return;
384            }
385        }
386
387        if (it != chunk.free_blocks.end() && block.offset + block.size > it->offset) {
388            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: overlapping next free block at offset %zu size %zu\n", __func__,
389                           block.offset, block.size);
390            return;
391        }
392
393        it = chunk.free_blocks.insert(it, block);
394
395        if (it != chunk.free_blocks.begin()) {
396            auto prev = it - 1;
397            if (prev->offset + prev->size == it->offset) {
398                it->offset = prev->offset;
399                it->size += prev->size;
400                it = chunk.free_blocks.erase(prev);
401            }
402        }
403
404        if (it + 1 != chunk.free_blocks.end() && it->offset + it->size == (it + 1)->offset) {
405            it->size += (it + 1)->size;
406            chunk.free_blocks.erase(it + 1);
407        }
408    }
409
410    std::mutex                                  mutex_;
411    std::vector<pool_chunk>                     chunks_;
412    std::unordered_map<void *, pool_allocation> allocations_;
413    size_t                                      default_chunk_size_{ 0 };
414};
415
416class spine_mem_pool_posix final : public spine_mem_pool_manager {
417  public:
418    spine_mem_pool_posix() : spine_mem_pool_manager(0) {}
419
420    ~spine_mem_pool_posix() override { release_chunks(); }
421
422  private:
423    bool alloc_chunk(size_t min_size, size_t alignment, void * hint_addr, pool_chunk * chunk) override {
424        (void) hint_addr;
425
426        const size_t alloc_alignment = std::max(alignment, sizeof(void *));
427        void *       base            = nullptr;
428        const int    rc              = posix_memalign(&base, alloc_alignment, min_size);
429        if (rc != 0) {
430            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: posix_memalign failed for size %zu alignment %zu, rc=%d\n",
431                           __func__, min_size, alloc_alignment, rc);
432            return false;
433        }
434
435        chunk->base = static_cast<uint8_t *>(base);
436        chunk->size = min_size;
437        chunk->fd   = -1;
438        return true;
439    }
440
441    void dealloc_chunk(pool_chunk * chunk) override {
442        std::free(chunk->base);
443        clear_chunk(chunk);
444    }
445};
446
447class spine_mem_pool_transparent_hugepage final : public spine_mem_pool_manager {
448  public:
449    spine_mem_pool_transparent_hugepage() : spine_mem_pool_manager(SPINE_MEM_POOL_CHUNK_SIZE) {}
450
451    ~spine_mem_pool_transparent_hugepage() override { release_chunks(); }
452
453  private:
454    bool alloc_chunk(size_t min_size, size_t alignment, void * hint_addr, pool_chunk * chunk) override {
455        (void) alignment;
456
457        size_t chunk_size = 0;
458        if (!align_up(min_size, default_chunk_size(), &chunk_size)) {
459            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: failed to round chunk size for %zu\n", __func__, min_size);
460            return false;
461        }
462
463        void * map_addr = mmap(hint_addr, chunk_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
464        if (map_addr == MAP_FAILED) {
465            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: mmap failed for chunk size %zu, errno=%d\n", __func__, chunk_size,
466                           errno);
467            return false;
468        }
469
470        if (madvise(map_addr, chunk_size, MADV_HUGEPAGE) != 0) {
471            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: madvise(MADV_HUGEPAGE) failed for chunk size %zu, errno=%d\n",
472                           __func__, chunk_size, errno);
473            munmap(map_addr, chunk_size);
474            return false;
475        }
476
477        chunk->base = static_cast<uint8_t *>(map_addr);
478        chunk->size = chunk_size;
479        chunk->fd   = -1;
480        return true;
481    }
482
483    void dealloc_chunk(pool_chunk * chunk) override {
484        if (chunk->base != nullptr && chunk->size != 0 && munmap(chunk->base, chunk->size) != 0) {
485            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: munmap failed for chunk %p size %zu, errno=%d\n", __func__,
486                           chunk->base, chunk->size, errno);
487        }
488
489        clear_chunk(chunk);
490    }
491};
492
493class spine_mem_pool_hugetlb_1g final : public spine_mem_pool_manager {
494  public:
495    spine_mem_pool_hugetlb_1g() : spine_mem_pool_manager(SPINE_MEM_POOL_1G_REGION_SIZE) {}
496
497    ~spine_mem_pool_hugetlb_1g() override { release_chunks(); }
498
499  private:
500    bool alloc_chunk(size_t min_size, size_t alignment, void * hint_addr, pool_chunk * chunk) override {
501        (void) alignment;
502        (void) hint_addr;
503
504        size_t region_size = 0;
505        if (!align_up(min_size, SPINE_MEM_POOL_1G_REGION_SIZE, &region_size)) {
506            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: failed to round hugetlb_1g size for %zu\n", __func__, min_size);
507            return false;
508        }
509
510        const int fd = open(SPINE_MEM_POOL_HUGETLB_1G_DEV, O_RDWR);
511        if (fd < 0) {
512            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: open(%s) failed, errno=%d\n", __func__,
513                           SPINE_MEM_POOL_HUGETLB_1G_DEV, errno);
514            return false;
515        }
516
517        hugetlb_1g_region region;
518        region.size  = region_size;
519        region.flags = HUGETLB_1G_FLAG_REQUIRE_PUD;
520        if (ioctl(fd, HUGETLB_1G_IOC_ALLOC, &region) < 0) {
521            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: HUGETLB_1G_IOC_ALLOC failed for size %zu, errno=%d\n", __func__,
522                           region_size, errno);
523            close(fd);
524            return false;
525        }
526
527        void * map_addr = mmap(nullptr, region.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
528        if (map_addr == MAP_FAILED) {
529            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: mmap failed for hugetlb_1g size %llu, errno=%d\n", __func__,
530                           static_cast<unsigned long long>(region.size), errno);
531            ioctl(fd, HUGETLB_1G_IOC_FREE);
532            close(fd);
533            return false;
534        }
535
536        chunk->base = static_cast<uint8_t *>(map_addr);
537        chunk->size = region.size;
538        chunk->fd   = fd;
539        return true;
540    }
541
542    void dealloc_chunk(pool_chunk * chunk) override {
543        if (chunk->base != nullptr && chunk->size != 0 && munmap(chunk->base, chunk->size) != 0) {
544            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: munmap failed for hugetlb_1g chunk %p size %zu, errno=%d\n",
545                           __func__, chunk->base, chunk->size, errno);
546        }
547
548        if (chunk->fd >= 0) {
549            if (ioctl(chunk->fd, HUGETLB_1G_IOC_FREE) < 0) {
550                GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: HUGETLB_1G_IOC_FREE failed for chunk %p, errno=%d\n",
551                               __func__, chunk->base, errno);
552            }
553
554            close(chunk->fd);
555        }
556
557        clear_chunk(chunk);
558    }
559};
560
561class spine_mem_pool_shared_mem final : public spine_mem_pool_manager {
562  public:
563    spine_mem_pool_shared_mem() : spine_mem_pool_manager(SPINE_SHARE_MEM_POOL_CHUNK_SIZE) {}
564
565    ~spine_mem_pool_shared_mem() override { release_chunks(); }
566
567  private:
568    bool alloc_chunk(size_t min_size, size_t alignment, void * hint_addr, pool_chunk * chunk) override {
569        (void) alignment;
570
571        if (hint_addr != nullptr) {
572            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: shared_mem does not support multiple active chunks\n", __func__);
573            return false;
574        }
575
576        if (min_size > default_chunk_size()) {
577            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: shared_mem request %zu exceeds chunk size %zu\n", __func__,
578                           min_size, default_chunk_size());
579            return false;
580        }
581
582        const int fd = open(SPINE_MEM_POOL_TCM_SYNC_MEM_DEV, O_RDWR | O_SYNC);
583        if (fd < 0) {
584            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: open(%s) failed, errno=%d\n", __func__,
585                           SPINE_MEM_POOL_TCM_SYNC_MEM_DEV, errno);
586            return false;
587        }
588
589        void * map_addr = mmap(nullptr, default_chunk_size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
590        if (map_addr == MAP_FAILED) {
591            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: mmap failed for %s size %zu, errno=%d\n", __func__,
592                           SPINE_MEM_POOL_TCM_SYNC_MEM_DEV, default_chunk_size(), errno);
593            close(fd);
594            return false;
595        }
596
597        chunk->base = static_cast<uint8_t *>(map_addr);
598        chunk->size = default_chunk_size();
599        chunk->fd   = fd;
600        return true;
601    }
602
603    void dealloc_chunk(pool_chunk * chunk) override {
604        if (chunk->base != nullptr && chunk->size != 0 && munmap(chunk->base, chunk->size) != 0) {
605            GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: munmap failed for shared_mem chunk %p size %zu, errno=%d\n",
606                           __func__, chunk->base, chunk->size, errno);
607        }
608
609        if (chunk->fd >= 0) {
610            close(chunk->fd);
611        }
612
613        clear_chunk(chunk);
614    }
615};
616
617spine_mem_pool_manager & get_spine_mem_pool_manager() {
618    static std::once_flag                          pool_once;
619    static std::unique_ptr<spine_mem_pool_manager> selected_pool;
620    static spine_mem_pool_backend                  selected_backend = spine_mem_pool_backend::none;
621
622    spine_mem_pool_backend backend = global_spine_env_info.mem_backend;
623    if (backend == spine_mem_pool_backend::none) {
624        backend = spine_mem_pool_backend::transparent_hugepage;
625    }
626
627    std::call_once(pool_once, [&]() {
628        selected_backend = backend;
629
630        switch (selected_backend) {
631            case spine_mem_pool_backend::posix_memalign:
632                selected_pool = std::make_unique<spine_mem_pool_posix>();
633                break;
634            case spine_mem_pool_backend::transparent_hugepage:
635                selected_pool = std::make_unique<spine_mem_pool_transparent_hugepage>();
636                break;
637            case spine_mem_pool_backend::hugetlb_1g:
638                selected_pool = std::make_unique<spine_mem_pool_hugetlb_1g>();
639                break;
640            case spine_mem_pool_backend::none:
641                selected_backend = spine_mem_pool_backend::transparent_hugepage;
642                selected_pool    = std::make_unique<spine_mem_pool_transparent_hugepage>();
643                break;
644        }
645    });
646
647    if (backend != selected_backend) {
648        GGML_LOG_ERROR(
649            "CPU_RISCV64_SPACEMIT: %s: mem pool backend is process-global and mutually exclusive, requested=%d but "
650            "selected=%d\n",
651            __func__, static_cast<int>(backend), static_cast<int>(selected_backend));
652    }
653
654    if (selected_pool) {
655        return *selected_pool;
656    }
657
658    throw std::bad_alloc();
659}
660
661spine_mem_pool_manager & get_spine_mem_pool_shared_mem_manager() {
662    static std::once_flag                             shared_mem_pool_once;
663    static std::unique_ptr<spine_mem_pool_shared_mem> shared_mem_pool;
664
665    std::call_once(shared_mem_pool_once, [&]() { shared_mem_pool = std::make_unique<spine_mem_pool_shared_mem>(); });
666
667    if (shared_mem_pool) {
668        return *shared_mem_pool;
669    }
670
671    throw std::bad_alloc();
672}
673
674}  // namespace
675
676bool spine_mem_pool_tcm_init(spine_mem_pool_tcm_info * info) noexcept {
677    if (info == nullptr) {
678        return false;
679    }
680
681    *info = {};
682
683    if (spine_tcm_open_handle(NULL) != 0 || !spine_tcm_is_available()) {
684        return false;
685    }
686
687    spine_tcm_mem_info_t mem_info;
688    if (spine_tcm_mem_info(&mem_info) != 0) {
689        return false;
690    }
691
692    info->available   = true;
693    info->blk_size    = mem_info.blk_size;
694    info->blk_num     = mem_info.blk_num;
695    info->is_fake_tcm = mem_info.is_fake_tcm != 0;
696    return true;
697}
698
699void * spine_mem_pool_tcm_mem_get(int cpu_id) noexcept {
700    return spine_tcm_mem_get(cpu_id);
701}
702
703void * spine_mem_pool_tcm_mem_wait(int cpu_id) noexcept {
704    return spine_tcm_mem_try_wait(cpu_id, 1000 * 1000);
705}
706
707int spine_mem_pool_tcm_mem_release(int cpu_id) noexcept {
708    return spine_tcm_mem_release(cpu_id);
709}
710
711void * spine_mem_pool_alloc(size_t size, size_t alignment) noexcept {
712    try {
713        return get_spine_mem_pool_manager().alloc(size, alignment);
714    } catch (const std::bad_alloc &) {
715        GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: bad_alloc while allocating size %zu\n", __func__, size);
716        return nullptr;
717    }
718}
719
720void * spine_mem_pool_shared_mem_alloc(size_t size, size_t alignment) noexcept {
721    try {
722        return get_spine_mem_pool_shared_mem_manager().alloc(size, alignment);
723    } catch (const std::bad_alloc &) {
724        GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: bad_alloc while allocating shared memory size %zu\n", __func__, size);
725        return nullptr;
726    }
727}
728
729void spine_mem_pool_free(void * base) noexcept {
730    try {
731        get_spine_mem_pool_manager().free(base);
732    } catch (const std::bad_alloc &) {
733        GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: bad_alloc while freeing allocation %p\n", __func__, base);
734    }
735}
736
737void spine_mem_pool_shared_mem_free(void * base) noexcept {
738    try {
739        get_spine_mem_pool_shared_mem_manager().free(base);
740    } catch (const std::bad_alloc &) {
741        GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: bad_alloc while freeing shared allocation %p\n", __func__, base);
742    }
743}
744
745}  // namespace ggml::cpu::riscv64_spacemit
746
747extern "C" {
748void * ggml_backend_cpu_riscv64_spacemit_alloc_shared(size_t size, size_t alignment) {
749    void * result = ggml::cpu::riscv64_spacemit::spine_mem_pool_shared_mem_alloc(size, alignment);
750    if (result == nullptr) {
751        GGML_LOG_ERROR("CPU_RISCV64_SPACEMIT: %s: failed to allocate shared memory size %zu alignment %zu\n", __func__,
752                       size, alignment);
753    }
754    return result;
755}
756
757void ggml_backend_cpu_riscv64_spacemit_free_shared(void * ptr) {
758    ggml::cpu::riscv64_spacemit::spine_mem_pool_shared_mem_free(ptr);
759}
760}