{
chunk *target = NULL;
+ /**
+ * For chunk compaction, we use two-stage compactions, depending on the status of free chunks.
+ * At first, we can move used chunks to free chunks if there're enough large free chunks.
+ * In most cases, it would be enough. Otherwise, we need to re-arrange free chunks
+ * by shifting them to the bottom. It's heavy-weight and requires more memory copies.
+ */
+
+ /* we first try to perform 1st stage (light-weight) */
+
/* find a target chunk starting at top */
list_for_each_entry_reverse (target, mpriv.used_chunks, list) {
/* should be not busy (i.e., no processing here) and other conditions (TBD)? */
if (chunk_set_state (target, CHUNK_STATE_MODIFIED, false) == 0) {
- chunk *next_chunk, *free_chunk = NULL;
+ chunk *free_chunk = NULL;
/* find a free chunk starting at bottom */
list_for_each_entry (free_chunk, mpriv.free_chunks, list) {
if (free_chunk->offset > target->offset)
break;
- /* case 1) there is a free slot where this chunk moves */
+ /* there is a free slot where this chunk moves */
if (free_chunk->size >= target->size) {
*offset_dst = free_chunk->offset;
list_del (&mpriv.used_chunks, &target->list);
return target;
- } else
- /* case 2) this chunk is directly attached to the free chunk */
+ }
+ }
+
+ /* failure */
+ assert (target->state == CHUNK_STATE_MODIFIED);
+ target->state = CHUNK_STATE_FREE;
+ }
+ }
+
+ /* If there is no avaialble free chunk, let's go to 2nd stage (heavy-weight) */
+
+ /* we shift a used chunk to its left free chunk, starting at bottom */
+ list_for_each_entry (target, mpriv.used_chunks, list) {
+ if (chunk_set_state (target, CHUNK_STATE_MODIFIED, false) == 0) {
+ chunk *free_chunk = NULL;
+
+ /* find a free chunk starting at bottom */
+ list_for_each_entry (free_chunk, mpriv.free_chunks, list) {
+ /* this chunk is directly attached to the free chunk */
if (free_chunk->offset + free_chunk->size == target->offset) {
- *offset_dst = target->offset - free_chunk->size;
+ chunk *next_chunk;
+ *offset_dst = target->offset - free_chunk->size;
free_chunk->offset += target->size;
/* check merge */
case BUFFER_STATE_EMPTY:
/* when the output buffer was returned */
if (priv->state == BUFFER_STATE_OUTPUT_RETURN ||
- /* when the previous input need to be discarded (e.g., NPU processing is too slow) */
+ /* when the previous input need to be discarded (e.g., NPU processing is too slow) */
priv->state == BUFFER_STATE_INPUT_READY)
state_changed = true;
break;
uint32_t handle;
int fd;
- if (size_in % MEM_BASE_SIZE != 0 || size_in > UINT32_MAX) {
+ if (size_in == 0 || size_in % MEM_BASE_SIZE != 0 || size_in > UINT32_MAX) {
logerr(MEM_TAG, "Invalid memory size: 0x%lx\n", size_in);
return -EINVAL;
}