Skip to content

Commit c627ff5

Browse files
davidhildenbrandmstsirkin
authored andcommitted
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: Pankaj Gupta <[email protected]> Cc: "Michael S. Tsirkin" <[email protected]> Cc: Jason Wang <[email protected]> Cc: Oscar Salvador <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Igor Mammedov <[email protected]> Cc: Dave Young <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Dan Williams <[email protected]> Cc: Pavel Tatashin <[email protected]> Cc: Stefan Hajnoczi <[email protected]> Cc: Vlastimil Babka <[email protected]> Signed-off-by: David Hildenbrand <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Michael S. Tsirkin <[email protected]>
1 parent f2af6d3 commit c627ff5

File tree

1 file changed

+114
-2
lines changed

1 file changed

+114
-2
lines changed

drivers/virtio/virtio_mem.c

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ struct virtio_mem {
123123
*
124124
* When this lock is held the pointers can't change, ONLINE and
125125
* OFFLINE blocks can't change the state and no subblocks will get
126-
* plugged.
126+
* plugged/unplugged.
127127
*/
128128
struct mutex hotplug_mutex;
129129
bool hotplug_active;
@@ -280,6 +280,12 @@ static int virtio_mem_mb_state_prepare_next_mb(struct virtio_mem *vm)
280280
_mb_id++) \
281281
if (virtio_mem_mb_get_state(_vm, _mb_id) == _state)
282282

283+
#define virtio_mem_for_each_mb_state_rev(_vm, _mb_id, _state) \
284+
for (_mb_id = _vm->next_mb_id - 1; \
285+
_mb_id >= _vm->first_mb_id && _vm->nb_mb_state[_state]; \
286+
_mb_id--) \
287+
if (virtio_mem_mb_get_state(_vm, _mb_id) == _state)
288+
283289
/*
284290
* Mark all selected subblocks plugged.
285291
*
@@ -325,6 +331,19 @@ static bool virtio_mem_mb_test_sb_plugged(struct virtio_mem *vm,
325331
bit + count;
326332
}
327333

334+
/*
335+
* Test if all selected subblocks are unplugged.
336+
*/
337+
static bool virtio_mem_mb_test_sb_unplugged(struct virtio_mem *vm,
338+
unsigned long mb_id, int sb_id,
339+
int count)
340+
{
341+
const int bit = (mb_id - vm->first_mb_id) * vm->nb_sb_per_mb + sb_id;
342+
343+
/* TODO: Helper similar to bitmap_set() */
344+
return find_next_bit(vm->sb_bitmap, bit + count, bit) >= bit + count;
345+
}
346+
328347
/*
329348
* Find the first plugged subblock. Returns vm->nb_sb_per_mb in case there is
330349
* none.
@@ -513,6 +532,9 @@ static void virtio_mem_notify_offline(struct virtio_mem *vm,
513532
BUG();
514533
break;
515534
}
535+
536+
/* trigger the workqueue, maybe we can now unplug memory. */
537+
virtio_mem_retry(vm);
516538
}
517539

518540
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)
11221144
return rc;
11231145
}
11241146

1147+
/*
1148+
* Unplug the desired number of plugged subblocks of an offline memory block.
1149+
* Will fail if any subblock cannot get unplugged (instead of skipping it).
1150+
*
1151+
* Will modify the state of the memory block. Might temporarily drop the
1152+
* hotplug_mutex.
1153+
*
1154+
* Note: Can fail after some subblocks were successfully unplugged.
1155+
*/
1156+
static int virtio_mem_mb_unplug_any_sb_offline(struct virtio_mem *vm,
1157+
unsigned long mb_id,
1158+
uint64_t *nb_sb)
1159+
{
1160+
int rc;
1161+
1162+
rc = virtio_mem_mb_unplug_any_sb(vm, mb_id, nb_sb);
1163+
1164+
/* some subblocks might have been unplugged even on failure */
1165+
if (!virtio_mem_mb_test_sb_plugged(vm, mb_id, 0, vm->nb_sb_per_mb))
1166+
virtio_mem_mb_set_state(vm, mb_id,
1167+
VIRTIO_MEM_MB_STATE_OFFLINE_PARTIAL);
1168+
if (rc)
1169+
return rc;
1170+
1171+
if (virtio_mem_mb_test_sb_unplugged(vm, mb_id, 0, vm->nb_sb_per_mb)) {
1172+
/*
1173+
* Remove the block from Linux - this should never fail.
1174+
* Hinder the block from getting onlined by marking it
1175+
* unplugged. Temporarily drop the mutex, so
1176+
* any pending GOING_ONLINE requests can be serviced/rejected.
1177+
*/
1178+
virtio_mem_mb_set_state(vm, mb_id,
1179+
VIRTIO_MEM_MB_STATE_UNUSED);
1180+
1181+
mutex_unlock(&vm->hotplug_mutex);
1182+
rc = virtio_mem_mb_remove(vm, mb_id);
1183+
BUG_ON(rc);
1184+
mutex_lock(&vm->hotplug_mutex);
1185+
}
1186+
return 0;
1187+
}
1188+
1189+
/*
1190+
* Try to unplug the requested amount of memory.
1191+
*/
1192+
static int virtio_mem_unplug_request(struct virtio_mem *vm, uint64_t diff)
1193+
{
1194+
uint64_t nb_sb = diff / vm->subblock_size;
1195+
unsigned long mb_id;
1196+
int rc;
1197+
1198+
if (!nb_sb)
1199+
return 0;
1200+
1201+
/*
1202+
* We'll drop the mutex a couple of times when it is safe to do so.
1203+
* This might result in some blocks switching the state (online/offline)
1204+
* and we could miss them in this run - we will retry again later.
1205+
*/
1206+
mutex_lock(&vm->hotplug_mutex);
1207+
1208+
/* Try to unplug subblocks of partially plugged offline blocks. */
1209+
virtio_mem_for_each_mb_state_rev(vm, mb_id,
1210+
VIRTIO_MEM_MB_STATE_OFFLINE_PARTIAL) {
1211+
rc = virtio_mem_mb_unplug_any_sb_offline(vm, mb_id,
1212+
&nb_sb);
1213+
if (rc || !nb_sb)
1214+
goto out_unlock;
1215+
cond_resched();
1216+
}
1217+
1218+
/* Try to unplug subblocks of plugged offline blocks. */
1219+
virtio_mem_for_each_mb_state_rev(vm, mb_id,
1220+
VIRTIO_MEM_MB_STATE_OFFLINE) {
1221+
rc = virtio_mem_mb_unplug_any_sb_offline(vm, mb_id,
1222+
&nb_sb);
1223+
if (rc || !nb_sb)
1224+
goto out_unlock;
1225+
cond_resched();
1226+
}
1227+
1228+
mutex_unlock(&vm->hotplug_mutex);
1229+
return 0;
1230+
out_unlock:
1231+
mutex_unlock(&vm->hotplug_mutex);
1232+
return rc;
1233+
}
1234+
11251235
/*
11261236
* Try to unplug all blocks that couldn't be unplugged before, for example,
11271237
* because the hypervisor was busy.
@@ -1204,8 +1314,10 @@ static void virtio_mem_run_wq(struct work_struct *work)
12041314
if (vm->requested_size > vm->plugged_size) {
12051315
diff = vm->requested_size - vm->plugged_size;
12061316
rc = virtio_mem_plug_request(vm, diff);
1317+
} else {
1318+
diff = vm->plugged_size - vm->requested_size;
1319+
rc = virtio_mem_unplug_request(vm, diff);
12071320
}
1208-
/* TODO: try to unplug memory */
12091321
}
12101322

12111323
switch (rc) {

0 commit comments

Comments
 (0)