@@ -572,6 +572,57 @@ static void virtio_mem_notify_online(struct virtio_mem *vm, unsigned long mb_id,
572
572
virtio_mem_retry (vm );
573
573
}
574
574
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
+
575
626
/*
576
627
* This callback will either be called synchronously from add_memory() or
577
628
* 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,
618
669
break ;
619
670
}
620
671
vm -> hotplug_active = true;
672
+ virtio_mem_notify_going_offline (vm , mb_id );
621
673
break ;
622
674
case MEM_GOING_ONLINE :
623
675
mutex_lock (& vm -> hotplug_mutex );
@@ -642,6 +694,12 @@ static int virtio_mem_memory_notifier_cb(struct notifier_block *nb,
642
694
mutex_unlock (& vm -> hotplug_mutex );
643
695
break ;
644
696
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 ;
645
703
case MEM_CANCEL_ONLINE :
646
704
if (!vm -> hotplug_active )
647
705
break ;
@@ -668,8 +726,11 @@ static void virtio_mem_set_fake_offline(unsigned long pfn,
668
726
struct page * page = pfn_to_page (pfn );
669
727
670
728
__SetPageOffline (page );
671
- if (!onlined )
729
+ if (!onlined ) {
672
730
SetPageDirty (page );
731
+ /* FIXME: remove after cleanups */
732
+ ClearPageReserved (page );
733
+ }
673
734
}
674
735
}
675
736
@@ -1722,6 +1783,11 @@ static void virtio_mem_remove(struct virtio_device *vdev)
1722
1783
BUG_ON (rc );
1723
1784
virtio_mem_mb_set_state (vm , mb_id , VIRTIO_MEM_MB_STATE_UNUSED );
1724
1785
}
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
+ */
1725
1791
1726
1792
/* unregister callbacks */
1727
1793
unregister_virtio_mem_device (vm );
0 commit comments