in virtio_mem.c [2042:2093]
static int virtio_mem_sbm_unplug_request(struct virtio_mem *vm, uint64_t diff)
{
const int mb_states[] = {
VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL,
VIRTIO_MEM_SBM_MB_OFFLINE,
VIRTIO_MEM_SBM_MB_MOVABLE_PARTIAL,
VIRTIO_MEM_SBM_MB_KERNEL_PARTIAL,
VIRTIO_MEM_SBM_MB_MOVABLE,
VIRTIO_MEM_SBM_MB_KERNEL,
};
uint64_t nb_sb = diff / vm->sbm.sb_size;
unsigned long mb_id;
int rc, i;
if (!nb_sb)
return 0;
/*
* We'll drop the mutex a couple of times when it is safe to do so.
* This might result in some blocks switching the state (online/offline)
* and we could miss them in this run - we will retry again later.
*/
mutex_lock(&vm->hotplug_mutex);
/*
* We try unplug from partially plugged blocks first, to try removing
* whole memory blocks along with metadata. We prioritize ZONE_MOVABLE
* as it's more reliable to unplug memory and remove whole memory
* blocks, and we don't want to trigger a zone imbalances by
* accidentially removing too much kernel memory.
*/
for (i = 0; i < ARRAY_SIZE(mb_states); i++) {
virtio_mem_sbm_for_each_mb_rev(vm, mb_id, mb_states[i]) {
rc = virtio_mem_sbm_unplug_any_sb(vm, mb_id, &nb_sb);
if (rc || !nb_sb)
goto out_unlock;
mutex_unlock(&vm->hotplug_mutex);
cond_resched();
mutex_lock(&vm->hotplug_mutex);
}
if (!unplug_online && i == 1) {
mutex_unlock(&vm->hotplug_mutex);
return 0;
}
}
mutex_unlock(&vm->hotplug_mutex);
return nb_sb ? -EBUSY : 0;
out_unlock:
mutex_unlock(&vm->hotplug_mutex);
return rc;
}