diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index cebfb178..ade6f43a 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -2136,35 +2136,67 @@ static int hermon_map_vpm ( struct hermon *hermon, const struct hermonprm_virtual_physical_mapping* ), uint64_t va, physaddr_t pa, size_t len ) { struct hermonprm_virtual_physical_mapping mapping; + physaddr_t start; + physaddr_t low; + physaddr_t high; + physaddr_t end; + size_t size; int rc; + /* Sanity checks */ assert ( ( va & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); + /* Calculate starting points */ + start = pa; + end = ( start + len ); + size = ( 1UL << ( fls ( start ^ end ) - 1 ) ); + low = high = ( end & ~( size - 1 ) ); + assert ( start < low ); + assert ( high <= end ); + /* These mappings tend to generate huge volumes of * uninteresting debug data, which basically makes it * impossible to use debugging otherwise. */ DBG_DISABLE ( DBGLVL_LOG | DBGLVL_EXTRA ); - while ( len ) { + /* Map blocks in descending order of size */ + while ( size >= HERMON_PAGE_SIZE ) { + + /* Find the next candidate block */ + if ( ( low - size ) >= start ) { + low -= size; + pa = low; + } else if ( ( high + size ) <= end ) { + pa = high; + high += size; + } else { + size >>= 1; + continue; + } + assert ( ( va & ( size - 1 ) ) == 0 ); + assert ( ( pa & ( size - 1 ) ) == 0 ); + + /* Map this block */ memset ( &mapping, 0, sizeof ( mapping ) ); MLX_FILL_1 ( &mapping, 0, va_h, ( va >> 32 ) ); MLX_FILL_1 ( &mapping, 1, va_l, ( va >> 12 ) ); MLX_FILL_2 ( &mapping, 3, - log2size, 0, + log2size, ( ( fls ( size ) - 1 ) - 12 ), pa_l, ( pa >> 12 ) ); if ( ( rc = map ( hermon, &mapping ) ) != 0 ) { DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA ); - DBGC ( hermon, "Hermon %p could not map %llx => %lx: " - "%s\n", hermon, va, pa, strerror ( rc ) ); + DBGC ( hermon, "Hermon %p could not map %08llx+%zx to " + "%08lx: %s\n", + hermon, va, size, pa, strerror ( rc ) ); return rc; } - pa += HERMON_PAGE_SIZE; - va += HERMON_PAGE_SIZE; - len -= HERMON_PAGE_SIZE; + va += size; } + assert ( low == start ); + assert ( high == end ); DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA ); return 0;