React glacefully to in-middle out-of-memory. Fix few bugs.
This commit is contained in:
parent
298ffd54b0
commit
e16c2c65b9
1 changed files with 121 additions and 82 deletions
203
lib/relocator.c
203
lib/relocator.c
|
@ -22,8 +22,6 @@
|
||||||
#include <grub/misc.h>
|
#include <grub/misc.h>
|
||||||
#include <grub/cache.h>
|
#include <grub/cache.h>
|
||||||
|
|
||||||
/* FIXME: check memory map. */
|
|
||||||
|
|
||||||
struct grub_relocator
|
struct grub_relocator
|
||||||
{
|
{
|
||||||
struct grub_relocator_chunk *chunks;
|
struct grub_relocator_chunk *chunks;
|
||||||
|
@ -185,21 +183,81 @@ allocate_inreg (grub_addr_t addr, grub_size_t size,
|
||||||
foll->next = hb;
|
foll->next = hb;
|
||||||
hbp->next = foll;
|
hbp->next = foll;
|
||||||
if (rb->first == hb)
|
if (rb->first == hb)
|
||||||
rb->first = foll;
|
{
|
||||||
|
rb->first = foll;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (foll)
|
if (foll)
|
||||||
foll->next = hb->next;
|
{
|
||||||
|
foll->next = hb->next;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
foll = hb->next;
|
foll = hb->next;
|
||||||
|
|
||||||
hbp->next = foll;
|
hbp->next = foll;
|
||||||
if (rb->first == hb)
|
if (rb->first == hb)
|
||||||
rb->first = foll;
|
{
|
||||||
|
rb->first = foll;
|
||||||
|
}
|
||||||
if (rb->first == hb)
|
if (rb->first == hb)
|
||||||
rb->first = (void *) (rb + 1);
|
{
|
||||||
|
rb->first = (void *) (rb + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: remove extra blocks. */
|
||||||
|
static void
|
||||||
|
free_subchunk (const struct grub_relocator_subchunk *subchu)
|
||||||
|
{
|
||||||
|
switch (subchu->type)
|
||||||
|
{
|
||||||
|
case CHUNK_TYPE_REGION_START:
|
||||||
|
{
|
||||||
|
grub_mm_region_t r1, r2, *rp;
|
||||||
|
grub_mm_header_t h;
|
||||||
|
grub_size_t pre_size;
|
||||||
|
r1 = (grub_mm_region_t) ALIGN_UP (subchu->start + subchu->size,
|
||||||
|
GRUB_MM_ALIGN);
|
||||||
|
r2 = (grub_mm_region_t) ALIGN_UP (subchu->host_start, GRUB_MM_ALIGN);
|
||||||
|
for (rp = &grub_mm_base; *rp && *rp != r2; rp = &((*rp)->next));
|
||||||
|
/* FIXME */
|
||||||
|
if (!*rp)
|
||||||
|
grub_fatal ("Anomaly in region alocations detected. "
|
||||||
|
"Simultaneous relocators?");
|
||||||
|
pre_size = ALIGN_UP (subchu->host_start, GRUB_MM_ALIGN)
|
||||||
|
- subchu->host_start;
|
||||||
|
r2->first = r1->first;
|
||||||
|
r2->next = r1->next;
|
||||||
|
r2->pre_size = pre_size;
|
||||||
|
r2->size = r1->size + (r2 - r1) * sizeof (*r2);
|
||||||
|
*rp = r1;
|
||||||
|
h = (grub_mm_header_t) (r1 + 1);
|
||||||
|
h->next = h;
|
||||||
|
h->magic = GRUB_MM_ALLOC_MAGIC;
|
||||||
|
h->size = (r2 - r1);
|
||||||
|
grub_free (h + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CHUNK_TYPE_IN_REGION:
|
||||||
|
{
|
||||||
|
grub_mm_header_t h = (grub_mm_header_t) ALIGN_DOWN (subchu->start,
|
||||||
|
GRUB_MM_ALIGN);
|
||||||
|
h->size = (subchu->start / GRUB_MM_ALIGN)
|
||||||
|
- ((subchu->start + subchu->size + GRUB_MM_ALIGN - 1) / GRUB_MM_ALIGN);
|
||||||
|
h->next = h;
|
||||||
|
h->magic = GRUB_MM_ALLOC_MAGIC;
|
||||||
|
grub_free (h + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
||||||
|
case CHUNK_TYPE_FIRMWARE:
|
||||||
|
grub_relocator_firmware_free_region (subchu->start, subchu->size);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,12 +574,24 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
events[last_start].reg,
|
events[last_start].reg,
|
||||||
events[last_start].regancestor,
|
events[last_start].regancestor,
|
||||||
events[last_start].hancestor);
|
events[last_start].hancestor);
|
||||||
|
{
|
||||||
|
unsigned k;
|
||||||
|
for (k = 0; k < N; k++)
|
||||||
|
if (events[k].hancestor == events[last_start].head)
|
||||||
|
events[k].hancestor = events[last_start].hancestor;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CHUNK_TYPE_IN_REGION:
|
case CHUNK_TYPE_IN_REGION:
|
||||||
allocate_inreg (alloc_start, alloc_end - alloc_start,
|
allocate_inreg (alloc_start, alloc_end - alloc_start,
|
||||||
events[last_start].head,
|
events[last_start].head,
|
||||||
events[last_start].hancestor,
|
events[last_start].hancestor,
|
||||||
events[last_start].reg);
|
events[last_start].reg);
|
||||||
|
{
|
||||||
|
unsigned k;
|
||||||
|
for (k = 0; k < N; k++)
|
||||||
|
if (events[k].hancestor == events[last_start].head)
|
||||||
|
events[k].hancestor = events[last_start].hancestor;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
||||||
case CHUNK_TYPE_FIRMWARE:
|
case CHUNK_TYPE_FIRMWARE:
|
||||||
|
@ -529,9 +599,6 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
if (!grub_relocator_firmware_alloc_region (alloc_start,
|
if (!grub_relocator_firmware_alloc_region (alloc_start,
|
||||||
alloc_end - alloc_start))
|
alloc_end - alloc_start))
|
||||||
{
|
{
|
||||||
grub_dprintf ("relocator",
|
|
||||||
"firmware allocation 0x%x-0x%x failed.\n",
|
|
||||||
alloc_start, alloc_end);
|
|
||||||
if (from_low_priv)
|
if (from_low_priv)
|
||||||
start = alloc_end;
|
start = alloc_end;
|
||||||
else
|
else
|
||||||
|
@ -559,7 +626,10 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
|
|
||||||
case REG_BEG_END:
|
case REG_BEG_END:
|
||||||
case IN_REG_END:
|
case IN_REG_END:
|
||||||
inreg = regbeg = 0;
|
if (regbeg)
|
||||||
|
regbeg--;
|
||||||
|
else
|
||||||
|
inreg--;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
||||||
|
@ -598,7 +668,7 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (!p)
|
if (!p)
|
||||||
grub_fatal ("null in the ring %p %p\n", r, p);
|
grub_fatal ("null in the ring %p\n", r);
|
||||||
p = p->next;
|
p = p->next;
|
||||||
}
|
}
|
||||||
while (p != r->first);
|
while (p != r->first);
|
||||||
|
@ -607,9 +677,6 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
/* Malloc is available again. */
|
/* Malloc is available again. */
|
||||||
grub_mm_base = base_saved;
|
grub_mm_base = base_saved;
|
||||||
|
|
||||||
/* FIXME: react on out of memory. */
|
|
||||||
res->subchunks = grub_malloc (sizeof (res->subchunks[0]) * nallocs);
|
|
||||||
res->nsubchunks = nallocs;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
int last_start = 0;
|
int last_start = 0;
|
||||||
|
@ -617,7 +684,13 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
||||||
int fwin = 0, fwb = 0;
|
int fwin = 0, fwb = 0;
|
||||||
#endif
|
#endif
|
||||||
int cural = 0;
|
unsigned cural = 0;
|
||||||
|
int oom = 0;
|
||||||
|
res->subchunks = grub_malloc (sizeof (res->subchunks[0]) * nallocs);
|
||||||
|
if (!res->subchunks)
|
||||||
|
oom = 1;
|
||||||
|
res->nsubchunks = nallocs;
|
||||||
|
|
||||||
for (j = 0; j < N; j++)
|
for (j = 0; j < N; j++)
|
||||||
{
|
{
|
||||||
int typepre;
|
int typepre;
|
||||||
|
@ -637,6 +710,10 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
if (j != 0 && events[j - 1].pos != events[j].pos)
|
if (j != 0 && events[j - 1].pos != events[j].pos)
|
||||||
{
|
{
|
||||||
grub_addr_t alloc_start, alloc_end;
|
grub_addr_t alloc_start, alloc_end;
|
||||||
|
struct grub_relocator_subchunk tofree;
|
||||||
|
struct grub_relocator_subchunk *curschu = &tofree;
|
||||||
|
if (!oom)
|
||||||
|
curschu = &res->subchunks[cural];
|
||||||
alloc_start = max (events[j - 1].pos, target);
|
alloc_start = max (events[j - 1].pos, target);
|
||||||
alloc_end = min (events[j].pos, target + size);
|
alloc_end = min (events[j].pos, target + size);
|
||||||
if (alloc_end > alloc_start)
|
if (alloc_end > alloc_start)
|
||||||
|
@ -644,26 +721,33 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
grub_dprintf ("relocator", "subchunk 0x%lx-0x%lx, %d\n",
|
grub_dprintf ("relocator", "subchunk 0x%lx-0x%lx, %d\n",
|
||||||
(unsigned long) alloc_start,
|
(unsigned long) alloc_start,
|
||||||
(unsigned long) alloc_end, typepre);
|
(unsigned long) alloc_end, typepre);
|
||||||
res->subchunks[cural].type = typepre;
|
curschu->type = typepre;
|
||||||
if (typepre == CHUNK_TYPE_REGION_START)
|
if (typepre == CHUNK_TYPE_REGION_START)
|
||||||
{
|
curschu->host_start = (grub_addr_t) events[last_start].reg;
|
||||||
res->subchunks[cural].host_start
|
|
||||||
= (grub_addr_t) events[last_start].reg;
|
|
||||||
}
|
|
||||||
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
||||||
if (typepre == CHUNK_TYPE_REGION_START
|
if (!oom && (typepre == CHUNK_TYPE_REGION_START
|
||||||
|| typepre == CHUNK_TYPE_FIRMWARE)
|
|| typepre == CHUNK_TYPE_FIRMWARE))
|
||||||
{
|
{
|
||||||
/* FIXME: react on out of memory. */
|
|
||||||
struct grub_relocator_extra_block *ne;
|
struct grub_relocator_extra_block *ne;
|
||||||
ne = grub_malloc (sizeof (*ne));
|
ne = grub_malloc (sizeof (*ne));
|
||||||
ne->start = alloc_start;
|
if (!ne)
|
||||||
ne->end = alloc_end;
|
{
|
||||||
ne->next = extra_blocks;
|
oom = 1;
|
||||||
extra_blocks = ne;
|
grub_memcpy (&tofree, curschu, sizeof (tofree));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ne->start = alloc_start;
|
||||||
|
ne->end = alloc_end;
|
||||||
|
ne->next = extra_blocks;
|
||||||
|
extra_blocks = ne;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
cural++;
|
if (!oom)
|
||||||
|
cural++;
|
||||||
|
else
|
||||||
|
free_subchunk (&tofree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,7 +792,14 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
ncol--;
|
ncol--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (oom)
|
||||||
|
{
|
||||||
|
for (i = 0; i < cural; i++)
|
||||||
|
free_subchunk (&res->subchunks[i]);
|
||||||
|
grub_free (res->subchunks);
|
||||||
|
grub_dprintf ("relocator", "allocation failed with out-of-memory\n");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,7 +1040,6 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: remove extra blocks. */
|
|
||||||
void
|
void
|
||||||
grub_relocator_unload (struct grub_relocator *rel)
|
grub_relocator_unload (struct grub_relocator *rel)
|
||||||
{
|
{
|
||||||
|
@ -960,58 +1050,7 @@ grub_relocator_unload (struct grub_relocator *rel)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
for (i = 0; i < chunk->nsubchunks; i++)
|
for (i = 0; i < chunk->nsubchunks; i++)
|
||||||
switch (chunk->subchunks[i].type)
|
free_subchunk (&chunk->subchunks[i]);
|
||||||
{
|
|
||||||
case CHUNK_TYPE_REGION_START:
|
|
||||||
{
|
|
||||||
grub_mm_region_t r1, r2, *rp;
|
|
||||||
grub_mm_header_t h;
|
|
||||||
grub_size_t pre_size;
|
|
||||||
r1 = (grub_mm_region_t) ALIGN_UP (chunk->subchunks[i].start
|
|
||||||
+ chunk->subchunks[i].size,
|
|
||||||
GRUB_MM_ALIGN);
|
|
||||||
r2 = (grub_mm_region_t) ALIGN_UP (chunk->subchunks[i].host_start,
|
|
||||||
GRUB_MM_ALIGN);
|
|
||||||
for (rp = &grub_mm_base; *rp && *rp != r2; rp = &((*rp)->next));
|
|
||||||
/* FIXME */
|
|
||||||
if (!*rp)
|
|
||||||
grub_fatal ("Anomaly in region alocations detected. "
|
|
||||||
"Simultaneous relocators?");
|
|
||||||
pre_size = ALIGN_UP (chunk->subchunks[i].host_start,
|
|
||||||
GRUB_MM_ALIGN)
|
|
||||||
- chunk->subchunks[i].host_start;
|
|
||||||
r2->first = r1->first;
|
|
||||||
r2->next = r1->next;
|
|
||||||
r2->pre_size = pre_size;
|
|
||||||
r2->size = r1->size + (r2 - r1) * sizeof (*r2);
|
|
||||||
*rp = r1;
|
|
||||||
h = (grub_mm_header_t) (r1 + 1);
|
|
||||||
h->next = h;
|
|
||||||
h->magic = GRUB_MM_ALLOC_MAGIC;
|
|
||||||
h->size = (r2 - r1);
|
|
||||||
grub_free (h + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CHUNK_TYPE_IN_REGION:
|
|
||||||
{
|
|
||||||
grub_mm_header_t h
|
|
||||||
= (grub_mm_header_t) ALIGN_DOWN (chunk->subchunks[i].start,
|
|
||||||
GRUB_MM_ALIGN);
|
|
||||||
h->size = (chunk->subchunks[i].start / GRUB_MM_ALIGN)
|
|
||||||
- ((chunk->subchunks[i].start + chunk->subchunks[i].size
|
|
||||||
+ GRUB_MM_ALIGN - 1) / GRUB_MM_ALIGN);
|
|
||||||
h->next = h;
|
|
||||||
h->magic = GRUB_MM_ALLOC_MAGIC;
|
|
||||||
grub_free (h + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|
|
||||||
case CHUNK_TYPE_FIRMWARE:
|
|
||||||
grub_relocator_firmware_free_region (chunk->subchunks[i].start,
|
|
||||||
chunk->subchunks[i].size);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
next = chunk->next;
|
next = chunk->next;
|
||||||
grub_free (chunk->subchunks);
|
grub_free (chunk->subchunks);
|
||||||
grub_free (chunk);
|
grub_free (chunk);
|
||||||
|
|
Loading…
Reference in a new issue