Skip to content

Commit 8e5c921

Browse files
davidhildenbrandmstsirkin
authored andcommitted
virtio-mem: Allow to offline partially unplugged memory blocks
Dropping the reference count of PageOffline() pages during MEM_GOING_ONLINE allows offlining code to skip them. However, we also have to clear PG_reserved, because PG_reserved pages get detected as unmovable right away. Take care of restoring the reference count when offlining is canceled. Clarify why we don't have to perform any action when unloading the driver. Also, let's add a warning if anybody is still holding a reference to unplugged pages when offlining. 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 aa21879 commit 8e5c921

File tree

1 file changed

+67
-1
lines changed

1 file changed

+67
-1
lines changed

drivers/virtio/virtio_mem.c

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,57 @@ static void virtio_mem_notify_online(struct virtio_mem *vm, unsigned long mb_id,
572572
virtio_mem_retry(vm);
573573
}
574574

575+
static void virtio_mem_notify_going_offline(struct virtio_mem *vm,
576+
unsigned long mb_id)
577+
{
578+
const unsigned long nr_pages = PFN_DOWN(vm->subblock_size);
579+
struct page *page;
580+
unsigned long pfn;
581+
int sb_id, i;
582+
583+
for (sb_id = 0; sb_id < vm->nb_sb_per_mb; sb_id++) {
584+
if (virtio_mem_mb_test_sb_plugged(vm, mb_id, sb_id, 1))
585+
continue;
586+
/*
587+
* Drop our reference to the pages so the memory can get
588+
* offlined and add the unplugged pages to the managed
589+
* page counters (so offlining code can correctly subtract
590+
* them again).
591+
*/
592+
pfn = PFN_DOWN(virtio_mem_mb_id_to_phys(mb_id) +
593+
sb_id * vm->subblock_size);
594+
adjust_managed_page_count(pfn_to_page(pfn), nr_pages);
595+
for (i = 0; i < nr_pages; i++) {
596+
page = pfn_to_page(pfn + i);
597+
if (WARN_ON(!page_ref_dec_and_test(page)))
598+
dump_page(page, "unplugged page referenced");
599+
}
600+
}
601+
}
602+
603+
static void virtio_mem_notify_cancel_offline(struct virtio_mem *vm,
604+
unsigned long mb_id)
605+
{
606+
const unsigned long nr_pages = PFN_DOWN(vm->subblock_size);
607+
unsigned long pfn;
608+
int sb_id, i;
609+
610+
for (sb_id = 0; sb_id < vm->nb_sb_per_mb; sb_id++) {
611+
if (virtio_mem_mb_test_sb_plugged(vm, mb_id, sb_id, 1))
612+
continue;
613+
/*
614+
* Get the reference we dropped when going offline and
615+
* subtract the unplugged pages from the managed page
616+
* counters.
617+
*/
618+
pfn = PFN_DOWN(virtio_mem_mb_id_to_phys(mb_id) +
619+
sb_id * vm->subblock_size);
620+
adjust_managed_page_count(pfn_to_page(pfn), -nr_pages);
621+
for (i = 0; i < nr_pages; i++)
622+
page_ref_inc(pfn_to_page(pfn + i));
623+
}
624+
}
625+
575626
/*
576627
* This callback will either be called synchronously from add_memory() or
577628
* asynchronously (e.g., triggered via user space). We have to be careful
@@ -618,6 +669,7 @@ static int virtio_mem_memory_notifier_cb(struct notifier_block *nb,
618669
break;
619670
}
620671
vm->hotplug_active = true;
672+
virtio_mem_notify_going_offline(vm, mb_id);
621673
break;
622674
case MEM_GOING_ONLINE:
623675
mutex_lock(&vm->hotplug_mutex);
@@ -642,6 +694,12 @@ static int virtio_mem_memory_notifier_cb(struct notifier_block *nb,
642694
mutex_unlock(&vm->hotplug_mutex);
643695
break;
644696
case MEM_CANCEL_OFFLINE:
697+
if (!vm->hotplug_active)
698+
break;
699+
virtio_mem_notify_cancel_offline(vm, mb_id);
700+
vm->hotplug_active = false;
701+
mutex_unlock(&vm->hotplug_mutex);
702+
break;
645703
case MEM_CANCEL_ONLINE:
646704
if (!vm->hotplug_active)
647705
break;
@@ -668,8 +726,11 @@ static void virtio_mem_set_fake_offline(unsigned long pfn,
668726
struct page *page = pfn_to_page(pfn);
669727

670728
__SetPageOffline(page);
671-
if (!onlined)
729+
if (!onlined) {
672730
SetPageDirty(page);
731+
/* FIXME: remove after cleanups */
732+
ClearPageReserved(page);
733+
}
673734
}
674735
}
675736

@@ -1722,6 +1783,11 @@ static void virtio_mem_remove(struct virtio_device *vdev)
17221783
BUG_ON(rc);
17231784
virtio_mem_mb_set_state(vm, mb_id, VIRTIO_MEM_MB_STATE_UNUSED);
17241785
}
1786+
/*
1787+
* After we unregistered our callbacks, user space can no longer
1788+
* offline partially plugged online memory blocks. No need to worry
1789+
* about them.
1790+
*/
17251791

17261792
/* unregister callbacks */
17271793
unregister_virtio_mem_device(vm);

0 commit comments

Comments
 (0)