[initrd] Swap initrds entirely in-place via triple reversal

Eliminate the requirement for free space when reshuffling initrds by
swapping adjacent initrds using an in-place triple reversal.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-05-22 13:27:03 +01:00
parent 11e01f0652
commit 9231d8c952
2 changed files with 42 additions and 55 deletions

View File

@@ -45,9 +45,8 @@ static physaddr_t initrd_top;
* Squash initrds as high as possible in memory
*
* @v top Highest possible physical address
* @ret used Lowest physical address used by initrds
*/
static physaddr_t initrd_squash_high ( physaddr_t top ) {
static void initrd_squash_high ( physaddr_t top ) {
physaddr_t current = top;
struct image *initrd;
struct image *highest;
@@ -100,8 +99,25 @@ static physaddr_t initrd_squash_high ( physaddr_t top ) {
initrd->data = data;
}
}
}
return current;
/**
* Reverse aligned memory region
*
* @v data Memory region
* @v len Length of region
*/
static void initrd_reverse ( void *data, size_t len ) {
unsigned long *low = data;
unsigned long *high = ( data + len );
unsigned long tmp;
/* Reverse region */
for ( high-- ; low < high ; low++, high-- ) {
tmp = *low;
*low = *high;
*high = tmp;
}
}
/**
@@ -109,14 +125,12 @@ static physaddr_t initrd_squash_high ( physaddr_t top ) {
*
* @v low Lower initrd
* @v high Higher initrd
* @v free Free space
* @v free_len Length of free space
*/
static void initrd_swap ( struct image *low, struct image *high,
void *free, size_t free_len ) {
size_t len = 0;
size_t frag_len;
size_t new_len;
static void initrd_swap ( struct image *low, struct image *high ) {
size_t low_len;
size_t high_len;
size_t len;
void *data;
DBGC ( &images, "INITRD swapping %s [%#08lx,%#08lx)<->[%#08lx,%#08lx) "
"%s\n", low->name, virt_to_phys ( low->data ),
@@ -124,41 +138,30 @@ static void initrd_swap ( struct image *low, struct image *high,
virt_to_phys ( high->data ),
( virt_to_phys ( high->data ) + high->len ), high->name );
/* Round down length of free space */
free_len &= ~( INITRD_ALIGN - 1 );
assert ( free_len > 0 );
/* Swap image data */
while ( len < high->len ) {
/* Calculate maximum fragment length */
frag_len = ( high->len - len );
if ( frag_len > free_len )
frag_len = free_len;
new_len = ( ( len + frag_len + INITRD_ALIGN - 1 ) &
~( INITRD_ALIGN - 1 ) );
/* Swap fragments */
memcpy ( free, ( high->data + len ), frag_len );
memmove ( ( low->rwdata + new_len ), ( low->data + len ),
low->len );
memcpy ( ( low->rwdata + len ), free, frag_len );
len = new_len;
}
/* Calculate padded lengths */
low_len = ( ( low->len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ) );
high_len = ( ( high->len + INITRD_ALIGN - 1 ) & ~( INITRD_ALIGN - 1 ));
len = ( low_len + high_len );
data = low->rwdata;
assert ( high->data == ( data + low_len ) );
/* Adjust data pointers */
high->data = low->data;
low->data += len;
high->data -= low_len;
low->data += high_len;
assert ( high->data == data );
/* Swap content via triple reversal */
initrd_reverse ( data, len );
initrd_reverse ( low->rwdata, low_len );
initrd_reverse ( high->rwdata, high_len );
}
/**
* Swap position of any two adjacent initrds not currently in the correct order
*
* @v free Free space
* @v free_len Length of free space
* @ret swapped A pair of initrds was swapped
*/
static int initrd_swap_any ( void *free, size_t free_len ) {
static int initrd_swap_any ( void ) {
struct image *low;
struct image *high;
const void *adjacent;
@@ -184,7 +187,7 @@ static int initrd_swap_any ( void *free, size_t free_len ) {
/* If we have found the adjacent image, swap and exit */
if ( high->data == adjacent ) {
initrd_swap ( low, high, free, free_len );
initrd_swap ( low, high );
return 1;
}
}
@@ -228,9 +231,6 @@ static void initrd_dump ( void ) {
*/
void initrd_reshuffle ( physaddr_t bottom ) {
physaddr_t top;
physaddr_t used;
void *free;
size_t free_len;
/* Calculate limits of available space for initrds */
top = ( initrd_top ? initrd_top : uheap_end );
@@ -241,14 +241,10 @@ void initrd_reshuffle ( physaddr_t bottom ) {
initrd_dump();
/* Squash initrds as high as possible in memory */
used = initrd_squash_high ( top );
/* Calculate available free space */
free = phys_to_virt ( bottom );
free_len = ( used - bottom );
initrd_squash_high ( top );
/* Bubble-sort initrds into desired order */
while ( initrd_swap_any ( free, free_len ) ) {}
while ( initrd_swap_any() ) {}
/* Debug */
initrd_dump();
@@ -270,9 +266,6 @@ int initrd_reshuffle_check ( size_t len, physaddr_t bottom ) {
assert ( bottom >= uheap_limit );
available = ( top - bottom );
/* Allow for a sensible minimum amount of free space */
len += INITRD_MIN_FREE_LEN;
/* Check for available space */
return ( ( len < available ) ? 0 : -ENOBUFS );
}

View File

@@ -11,12 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
/** Minimum free space required to reshuffle initrds
*
* Chosen to avoid absurdly long reshuffling times
*/
#define INITRD_MIN_FREE_LEN ( 512 * 1024 )
extern void initrd_reshuffle ( physaddr_t bottom );
extern int initrd_reshuffle_check ( size_t len, physaddr_t bottom );