1
0
Fork 0
forked from nuttx/nuttx-update
local-nuttx-update/mm/mm_gran/mm_grantable.c
Ville Juven fed3da9f03 mm_grantable.c: Fix infinite loop due to memory fragmentation
The search algorithm does not work with the ctz approach at all, if there
is a free range of granules that does not fit a specific allocation (i.e.
the granule allocation is fragmented) it will cause an infinite loop as
the algorithm will try to find free space from the same (free) starting
granule, causing an infinite loop.

The clz approach works for all cases, it will find the last used granule
and the search will continue from the next free granule.

Also, offsetting a full GAT must be sizeof(gat[0] - 1), which is 31 in
this case. The reason is that the upper level search function increments
the value by +1.
2024-12-05 20:36:04 +08:00

312 lines
6.8 KiB
C

/****************************************************************************
* mm/mm_gran/mm_grantable.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <strings.h>
#include <debug.h>
#include <nuttx/bits.h>
#include <nuttx/mm/gran.h>
#include "mm_gran/mm_gran.h"
#include "mm_gran/mm_grantable.h"
#ifdef CONFIG_GRAN
/****************************************************************************
* Preprocessors
****************************************************************************/
#define GATCFULL 0xffffffffu /* a full GAT cell */
#define DEBRUJIN_NUM 0x077CB531UL /* the de Bruijn Sequence */
/****************************************************************************
* Private data
****************************************************************************/
#if !defined(CONFIG_HAVE_BUILTIN_CLZ) || !defined(CONFIG_HAVE_BUILTIN_CTZ)
/* The de Bruijn lookup table to get n from BIT(n). */
static const uint8_t DEBRUJIN_LUT[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/* return BIT(MSB(n)) */
uint32_t msb_mask(uint32_t n)
{
/* see https://www.geeksforgeeks.org/find-significant-set-bit-number */
DEBUGASSERT(n);
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n = ((n + 1) >> 1) | (n & (1 << ((sizeof(n) << 3)-1)));
return n;
}
/* return BIT(LSB(n)) */
uint32_t lsb_mask(uint32_t n)
{
DEBUGASSERT(n);
return (-n & n) & GATCFULL;
}
/* set or clear a GAT cell with given bit mask */
static void cell_set(gran_t *gran, uint32_t cell, uint32_t mask, bool val)
{
if (val)
{
gran->gat[cell] |= mask;
}
else
{
gran->gat[cell] &= ~mask;
}
}
/* set or clear a range of GAT bits */
static void gran_set_(gran_t *gran, gatr_t *rang, bool val)
{
uint32_t c;
cell_set(gran, rang->sidx, rang->smask, val);
if (rang->sidx != rang->eidx)
{
cell_set(gran, rang->eidx, rang->emask, val);
c = rang->sidx + 1;
for (; c < rang->eidx; c++)
{
cell_set(gran, c, GATCFULL, val);
}
}
return;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/* prepare given GAT range instance for later use. */
int gran_range(const gran_t *gran, size_t posi, size_t size,
gatr_t *rang)
{
if (!gran || gran->ngranules < posi + size)
{
return -EINVAL;
}
if (rang == NULL)
{
return -ENOMEM;
}
rang->width = GATC_BITS(gran);
rang->sidx = posi / rang->width;
rang->soff = posi % rang->width;
posi += size - 1;
rang->eidx = posi / GATC_BITS(gran);
rang->eoff = posi % GATC_BITS(gran);
rang->smask = ~(BIT(rang->soff) - 1);
rang->emask = (BIT(rang->eoff) - 1) | BIT(rang->eoff);
if (rang->sidx == rang->eidx)
{
rang->smask &= rang->emask; /* combine the masks */
rang->emask = rang->smask;
}
return OK;
}
/* checks if a range of granule matches the expected status */
bool gran_match(const gran_t *gran, size_t posi, size_t size, bool used,
size_t *mpos)
{
uint32_t c; /* cell index */
uint32_t v; /* masked cell value */
uint32_t e; /* expected cell value */
gatr_t r; /* range helper */
if (gran_range(gran, posi, size, &r) < 0)
{
memset(&r, 0, sizeof(r));
}
/* check the ending cell */
c = r.eidx;
e = used ? r.emask : 0 ;
v = gran->gat[c] & r.emask;
if (v != e)
{
goto failure;
}
if (r.sidx == r.eidx)
{
return true;
}
/* check cells in between */
c = r.eidx - 1;
e = used ? GATCFULL : 0;
for (; c > r.sidx; c--)
{
v = gran->gat[c];
if (v != e)
{
goto failure;
}
}
/* check the starting cell */
c = r.sidx;
e = used ? r.smask : 0 ;
v = gran->gat[c] & r.smask;
if (v != e)
{
goto failure;
}
return true;
failure:
if (mpos && !used)
{
size_t tmp;
v = gran->gat[c];
DEBUGASSERT(v);
if (v == GATCFULL)
{
/* Handle full GAT quickly */
tmp = 31;
}
else
{
/* offset of last used when matching for free */
#ifdef CONFIG_HAVE_BUILTIN_CLZ
tmp = 31 - __builtin_clz(v);
#else
tmp = (uint32_t)((msb_mask(v)) * DEBRUJIN_NUM) >> 27;
DEBUGASSERT(tmp < sizeof(DEBRUJIN_LUT));
tmp = DEBRUJIN_LUT[tmp];
#endif
}
/* return the last used position to caller */
*mpos = tmp + c * GATC_BITS(gran);
}
return false;
}
/* returns granule number of free range or negative error */
int gran_search(const gran_t *gran, size_t size)
{
int ret = -EINVAL;
if (gran == NULL || gran->ngranules < size)
{
return ret;
}
ret = -ENOMEM;
for (size_t i = 0; i <= gran->ngranules - size; i++)
{
if (gran_match(gran, i, size, 0, &i))
{
ret = i;
break;
}
}
return ret;
}
/* set a range of granules */
int gran_set(gran_t *gran, size_t posi, size_t size)
{
gatr_t rang;
int ret = gran_range(gran, posi, size, &rang);
if (ret == OK)
{
gran_set_(gran, &rang, true);
}
return ret;
}
/* clear a range of granules */
int gran_clear(gran_t *gran, size_t posi, size_t size)
{
gatr_t rang;
int ret = gran_range(gran, posi, size, &rang);
if (ret == OK)
{
gran_set_(gran, &rang, false);
}
return ret;
}
#endif /* CONFIG_GRAN */