mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-07 13:43:51 +00:00
virtio-mem: keep retrying on offline_and_remove_memory() errors in Sub Block Mode (SBM)
In case offline_and_remove_memory() fails in SBM, we leave a completely unplugged Linux memory block stick around until we try plugging memory again. We won't try removing that memory block again. offline_and_remove_memory() may, for example, fail if we're racing with another alloc_contig_range() user, if allocating temporary memory fails, or if some memory notifier rejected the offlining request. Let's handle that case better, by simple retrying to offline and remove such memory. Tested using CONFIG_MEMORY_NOTIFIER_ERROR_INJECT. Signed-off-by: David Hildenbrand <david@redhat.com> Message-Id: <20230713145551.2824980-4-david@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
ddf4098514
commit
a31648fd4f
@ -168,6 +168,13 @@ struct virtio_mem {
|
||||
/* The number of subblocks per Linux memory block. */
|
||||
uint32_t sbs_per_mb;
|
||||
|
||||
/*
|
||||
* Some of the Linux memory blocks tracked as "partially
|
||||
* plugged" are completely unplugged and can be offlined
|
||||
* and removed -- which previously failed.
|
||||
*/
|
||||
bool have_unplugged_mb;
|
||||
|
||||
/* Summary of all memory block states. */
|
||||
unsigned long mb_count[VIRTIO_MEM_SBM_MB_COUNT];
|
||||
|
||||
@ -765,6 +772,34 @@ static int virtio_mem_sbm_offline_and_remove_mb(struct virtio_mem *vm,
|
||||
return virtio_mem_offline_and_remove_memory(vm, addr, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try (offlining and) removing memory from Linux in case all subblocks are
|
||||
* unplugged. Can be called on online and offline memory blocks.
|
||||
*
|
||||
* May modify the state of memory blocks in virtio-mem.
|
||||
*/
|
||||
static int virtio_mem_sbm_try_remove_unplugged_mb(struct virtio_mem *vm,
|
||||
unsigned long mb_id)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Once all subblocks of a memory block were unplugged, offline and
|
||||
* remove it.
|
||||
*/
|
||||
if (!virtio_mem_sbm_test_sb_unplugged(vm, mb_id, 0, vm->sbm.sbs_per_mb))
|
||||
return 0;
|
||||
|
||||
/* offline_and_remove_memory() works for online and offline memory. */
|
||||
mutex_unlock(&vm->hotplug_mutex);
|
||||
rc = virtio_mem_sbm_offline_and_remove_mb(vm, mb_id);
|
||||
mutex_lock(&vm->hotplug_mutex);
|
||||
if (!rc)
|
||||
virtio_mem_sbm_set_mb_state(vm, mb_id,
|
||||
VIRTIO_MEM_SBM_MB_UNUSED);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* See virtio_mem_offline_and_remove_memory(): Try to offline and remove a
|
||||
* all Linux memory blocks covered by the big block.
|
||||
@ -1988,20 +2023,10 @@ static int virtio_mem_sbm_unplug_any_sb_online(struct virtio_mem *vm,
|
||||
}
|
||||
|
||||
unplugged:
|
||||
/*
|
||||
* Once all subblocks of a memory block were unplugged, offline and
|
||||
* remove it. This will usually not fail, as no memory is in use
|
||||
* anymore - however some other notifiers might NACK the request.
|
||||
*/
|
||||
if (virtio_mem_sbm_test_sb_unplugged(vm, mb_id, 0, vm->sbm.sbs_per_mb)) {
|
||||
mutex_unlock(&vm->hotplug_mutex);
|
||||
rc = virtio_mem_sbm_offline_and_remove_mb(vm, mb_id);
|
||||
mutex_lock(&vm->hotplug_mutex);
|
||||
if (!rc)
|
||||
virtio_mem_sbm_set_mb_state(vm, mb_id,
|
||||
VIRTIO_MEM_SBM_MB_UNUSED);
|
||||
}
|
||||
|
||||
rc = virtio_mem_sbm_try_remove_unplugged_mb(vm, mb_id);
|
||||
if (rc)
|
||||
vm->sbm.have_unplugged_mb = 1;
|
||||
/* Ignore errors, this is not critical. We'll retry later. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2253,12 +2278,13 @@ static int virtio_mem_unplug_request(struct virtio_mem *vm, uint64_t diff)
|
||||
|
||||
/*
|
||||
* Try to unplug all blocks that couldn't be unplugged before, for example,
|
||||
* because the hypervisor was busy.
|
||||
* because the hypervisor was busy. Further, offline and remove any memory
|
||||
* blocks where we previously failed.
|
||||
*/
|
||||
static int virtio_mem_unplug_pending_mb(struct virtio_mem *vm)
|
||||
static int virtio_mem_cleanup_pending_mb(struct virtio_mem *vm)
|
||||
{
|
||||
unsigned long id;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
if (!vm->in_sbm) {
|
||||
virtio_mem_bbm_for_each_bb(vm, id,
|
||||
@ -2280,6 +2306,27 @@ static int virtio_mem_unplug_pending_mb(struct virtio_mem *vm)
|
||||
VIRTIO_MEM_SBM_MB_UNUSED);
|
||||
}
|
||||
|
||||
if (!vm->sbm.have_unplugged_mb)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Let's retry (offlining and) removing completely unplugged Linux
|
||||
* memory blocks.
|
||||
*/
|
||||
vm->sbm.have_unplugged_mb = false;
|
||||
|
||||
mutex_lock(&vm->hotplug_mutex);
|
||||
virtio_mem_sbm_for_each_mb(vm, id, VIRTIO_MEM_SBM_MB_MOVABLE_PARTIAL)
|
||||
rc |= virtio_mem_sbm_try_remove_unplugged_mb(vm, id);
|
||||
virtio_mem_sbm_for_each_mb(vm, id, VIRTIO_MEM_SBM_MB_KERNEL_PARTIAL)
|
||||
rc |= virtio_mem_sbm_try_remove_unplugged_mb(vm, id);
|
||||
virtio_mem_sbm_for_each_mb(vm, id, VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL)
|
||||
rc |= virtio_mem_sbm_try_remove_unplugged_mb(vm, id);
|
||||
mutex_unlock(&vm->hotplug_mutex);
|
||||
|
||||
if (rc)
|
||||
vm->sbm.have_unplugged_mb = true;
|
||||
/* Ignore errors, this is not critical. We'll retry later. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2361,9 +2408,9 @@ static void virtio_mem_run_wq(struct work_struct *work)
|
||||
virtio_mem_refresh_config(vm);
|
||||
}
|
||||
|
||||
/* Unplug any leftovers from previous runs */
|
||||
/* Cleanup any leftovers from previous runs */
|
||||
if (!rc)
|
||||
rc = virtio_mem_unplug_pending_mb(vm);
|
||||
rc = virtio_mem_cleanup_pending_mb(vm);
|
||||
|
||||
if (!rc && vm->requested_size != vm->plugged_size) {
|
||||
if (vm->requested_size > vm->plugged_size) {
|
||||
@ -2375,6 +2422,13 @@ static void virtio_mem_run_wq(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep retrying to offline and remove completely unplugged Linux
|
||||
* memory blocks.
|
||||
*/
|
||||
if (!rc && vm->in_sbm && vm->sbm.have_unplugged_mb)
|
||||
rc = -EBUSY;
|
||||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
vm->retry_timer_ms = VIRTIO_MEM_RETRY_TIMER_MIN_MS;
|
||||
|
Loading…
Reference in New Issue
Block a user