Commit c627ff5d authored by David Hildenbrand's avatar David Hildenbrand Committed by Michael S. Tsirkin

virtio-mem: Paravirtualized memory hotunplug part 1

Unplugging subblocks of memory blocks that are offline is easy. All we
have to do is watch out for concurrent onlining activity.
Tested-by: default avatarPankaj Gupta <pankaj.gupta.linux@gmail.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Pavel Tatashin <pasha.tatashin@soleen.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: default avatarDavid Hildenbrand <david@redhat.com>
Link: https://lore.kernel.org/r/20200507140139.17083-5-david@redhat.comSigned-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent f2af6d39
...@@ -123,7 +123,7 @@ struct virtio_mem { ...@@ -123,7 +123,7 @@ struct virtio_mem {
* *
* When this lock is held the pointers can't change, ONLINE and * When this lock is held the pointers can't change, ONLINE and
* OFFLINE blocks can't change the state and no subblocks will get * OFFLINE blocks can't change the state and no subblocks will get
* plugged. * plugged/unplugged.
*/ */
struct mutex hotplug_mutex; struct mutex hotplug_mutex;
bool hotplug_active; bool hotplug_active;
...@@ -280,6 +280,12 @@ static int virtio_mem_mb_state_prepare_next_mb(struct virtio_mem *vm) ...@@ -280,6 +280,12 @@ static int virtio_mem_mb_state_prepare_next_mb(struct virtio_mem *vm)
_mb_id++) \ _mb_id++) \
if (virtio_mem_mb_get_state(_vm, _mb_id) == _state) if (virtio_mem_mb_get_state(_vm, _mb_id) == _state)
#define virtio_mem_for_each_mb_state_rev(_vm, _mb_id, _state) \
for (_mb_id = _vm->next_mb_id - 1; \
_mb_id >= _vm->first_mb_id && _vm->nb_mb_state[_state]; \
_mb_id--) \
if (virtio_mem_mb_get_state(_vm, _mb_id) == _state)
/* /*
* Mark all selected subblocks plugged. * Mark all selected subblocks plugged.
* *
...@@ -325,6 +331,19 @@ static bool virtio_mem_mb_test_sb_plugged(struct virtio_mem *vm, ...@@ -325,6 +331,19 @@ static bool virtio_mem_mb_test_sb_plugged(struct virtio_mem *vm,
bit + count; bit + count;
} }
/*
* Test if all selected subblocks are unplugged.
*/
static bool virtio_mem_mb_test_sb_unplugged(struct virtio_mem *vm,
unsigned long mb_id, int sb_id,
int count)
{
const int bit = (mb_id - vm->first_mb_id) * vm->nb_sb_per_mb + sb_id;
/* TODO: Helper similar to bitmap_set() */
return find_next_bit(vm->sb_bitmap, bit + count, bit) >= bit + count;
}
/* /*
* Find the first plugged subblock. Returns vm->nb_sb_per_mb in case there is * Find the first plugged subblock. Returns vm->nb_sb_per_mb in case there is
* none. * none.
...@@ -513,6 +532,9 @@ static void virtio_mem_notify_offline(struct virtio_mem *vm, ...@@ -513,6 +532,9 @@ static void virtio_mem_notify_offline(struct virtio_mem *vm,
BUG(); BUG();
break; break;
} }
/* trigger the workqueue, maybe we can now unplug memory. */
virtio_mem_retry(vm);
} }
static void virtio_mem_notify_online(struct virtio_mem *vm, unsigned long mb_id, static void virtio_mem_notify_online(struct virtio_mem *vm, unsigned long mb_id,
...@@ -1122,6 +1144,94 @@ static int virtio_mem_plug_request(struct virtio_mem *vm, uint64_t diff) ...@@ -1122,6 +1144,94 @@ static int virtio_mem_plug_request(struct virtio_mem *vm, uint64_t diff)
return rc; return rc;
} }
/*
* Unplug the desired number of plugged subblocks of an offline memory block.
* Will fail if any subblock cannot get unplugged (instead of skipping it).
*
* Will modify the state of the memory block. Might temporarily drop the
* hotplug_mutex.
*
* Note: Can fail after some subblocks were successfully unplugged.
*/
static int virtio_mem_mb_unplug_any_sb_offline(struct virtio_mem *vm,
unsigned long mb_id,
uint64_t *nb_sb)
{
int rc;
rc = virtio_mem_mb_unplug_any_sb(vm, mb_id, nb_sb);
/* some subblocks might have been unplugged even on failure */
if (!virtio_mem_mb_test_sb_plugged(vm, mb_id, 0, vm->nb_sb_per_mb))
virtio_mem_mb_set_state(vm, mb_id,
VIRTIO_MEM_MB_STATE_OFFLINE_PARTIAL);
if (rc)
return rc;
if (virtio_mem_mb_test_sb_unplugged(vm, mb_id, 0, vm->nb_sb_per_mb)) {
/*
* Remove the block from Linux - this should never fail.
* Hinder the block from getting onlined by marking it
* unplugged. Temporarily drop the mutex, so
* any pending GOING_ONLINE requests can be serviced/rejected.
*/
virtio_mem_mb_set_state(vm, mb_id,
VIRTIO_MEM_MB_STATE_UNUSED);
mutex_unlock(&vm->hotplug_mutex);
rc = virtio_mem_mb_remove(vm, mb_id);
BUG_ON(rc);
mutex_lock(&vm->hotplug_mutex);
}
return 0;
}
/*
* Try to unplug the requested amount of memory.
*/
static int virtio_mem_unplug_request(struct virtio_mem *vm, uint64_t diff)
{
uint64_t nb_sb = diff / vm->subblock_size;
unsigned long mb_id;
int rc;
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);
/* Try to unplug subblocks of partially plugged offline blocks. */
virtio_mem_for_each_mb_state_rev(vm, mb_id,
VIRTIO_MEM_MB_STATE_OFFLINE_PARTIAL) {
rc = virtio_mem_mb_unplug_any_sb_offline(vm, mb_id,
&nb_sb);
if (rc || !nb_sb)
goto out_unlock;
cond_resched();
}
/* Try to unplug subblocks of plugged offline blocks. */
virtio_mem_for_each_mb_state_rev(vm, mb_id,
VIRTIO_MEM_MB_STATE_OFFLINE) {
rc = virtio_mem_mb_unplug_any_sb_offline(vm, mb_id,
&nb_sb);
if (rc || !nb_sb)
goto out_unlock;
cond_resched();
}
mutex_unlock(&vm->hotplug_mutex);
return 0;
out_unlock:
mutex_unlock(&vm->hotplug_mutex);
return rc;
}
/* /*
* Try to unplug all blocks that couldn't be unplugged before, for example, * Try to unplug all blocks that couldn't be unplugged before, for example,
* because the hypervisor was busy. * because the hypervisor was busy.
...@@ -1204,8 +1314,10 @@ static void virtio_mem_run_wq(struct work_struct *work) ...@@ -1204,8 +1314,10 @@ static void virtio_mem_run_wq(struct work_struct *work)
if (vm->requested_size > vm->plugged_size) { if (vm->requested_size > vm->plugged_size) {
diff = vm->requested_size - vm->plugged_size; diff = vm->requested_size - vm->plugged_size;
rc = virtio_mem_plug_request(vm, diff); rc = virtio_mem_plug_request(vm, diff);
} else {
diff = vm->plugged_size - vm->requested_size;
rc = virtio_mem_unplug_request(vm, diff);
} }
/* TODO: try to unplug memory */
} }
switch (rc) { switch (rc) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment