@@ -1453,8 +1453,8 @@ static int virtio_mem_bbm_plug_bb(struct virtio_mem *vm, unsigned long bb_id)
14531453 *
14541454 * Note: can fail after some subblocks were unplugged.
14551455 */
1456- static int virtio_mem_sbm_unplug_any_sb (struct virtio_mem * vm ,
1457- unsigned long mb_id , uint64_t * nb_sb )
1456+ static int virtio_mem_sbm_unplug_any_sb_raw (struct virtio_mem * vm ,
1457+ unsigned long mb_id , uint64_t * nb_sb )
14581458{
14591459 int sb_id , count ;
14601460 int rc ;
@@ -1496,7 +1496,7 @@ static int virtio_mem_sbm_unplug_mb(struct virtio_mem *vm, unsigned long mb_id)
14961496{
14971497 uint64_t nb_sb = vm -> sbm .sbs_per_mb ;
14981498
1499- return virtio_mem_sbm_unplug_any_sb (vm , mb_id , & nb_sb );
1499+ return virtio_mem_sbm_unplug_any_sb_raw (vm , mb_id , & nb_sb );
15001500}
15011501
15021502/*
@@ -1806,7 +1806,7 @@ static int virtio_mem_sbm_unplug_any_sb_offline(struct virtio_mem *vm,
18061806{
18071807 int rc ;
18081808
1809- rc = virtio_mem_sbm_unplug_any_sb (vm , mb_id , nb_sb );
1809+ rc = virtio_mem_sbm_unplug_any_sb_raw (vm , mb_id , nb_sb );
18101810
18111811 /* some subblocks might have been unplugged even on failure */
18121812 if (!virtio_mem_sbm_test_sb_plugged (vm , mb_id , 0 , vm -> sbm .sbs_per_mb ))
@@ -1929,11 +1929,46 @@ static int virtio_mem_sbm_unplug_any_sb_online(struct virtio_mem *vm,
19291929 return 0 ;
19301930}
19311931
1932+ /*
1933+ * Unplug the desired number of plugged subblocks of a memory block that is
1934+ * already added to Linux. Will skip subblock of online memory blocks that are
1935+ * busy (by the OS). Will fail if any subblock that's not busy cannot get
1936+ * unplugged.
1937+ *
1938+ * Will modify the state of the memory block. Might temporarily drop the
1939+ * hotplug_mutex.
1940+ *
1941+ * Note: Can fail after some subblocks were successfully unplugged. Can
1942+ * return 0 even if subblocks were busy and could not get unplugged.
1943+ */
1944+ static int virtio_mem_sbm_unplug_any_sb (struct virtio_mem * vm ,
1945+ unsigned long mb_id ,
1946+ uint64_t * nb_sb )
1947+ {
1948+ const int old_state = virtio_mem_sbm_get_mb_state (vm , mb_id );
1949+
1950+ switch (old_state ) {
1951+ case VIRTIO_MEM_SBM_MB_ONLINE_PARTIAL :
1952+ case VIRTIO_MEM_SBM_MB_ONLINE :
1953+ return virtio_mem_sbm_unplug_any_sb_online (vm , mb_id , nb_sb );
1954+ case VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL :
1955+ case VIRTIO_MEM_SBM_MB_OFFLINE :
1956+ return virtio_mem_sbm_unplug_any_sb_offline (vm , mb_id , nb_sb );
1957+ }
1958+ return - EINVAL ;
1959+ }
1960+
19321961static int virtio_mem_sbm_unplug_request (struct virtio_mem * vm , uint64_t diff )
19331962{
1963+ const int mb_states [] = {
1964+ VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL ,
1965+ VIRTIO_MEM_SBM_MB_OFFLINE ,
1966+ VIRTIO_MEM_SBM_MB_ONLINE_PARTIAL ,
1967+ VIRTIO_MEM_SBM_MB_ONLINE ,
1968+ };
19341969 uint64_t nb_sb = diff / vm -> sbm .sb_size ;
19351970 unsigned long mb_id ;
1936- int rc ;
1971+ int rc , i ;
19371972
19381973 if (!nb_sb )
19391974 return 0 ;
@@ -1945,47 +1980,23 @@ static int virtio_mem_sbm_unplug_request(struct virtio_mem *vm, uint64_t diff)
19451980 */
19461981 mutex_lock (& vm -> hotplug_mutex );
19471982
1948- /* Try to unplug subblocks of partially plugged offline blocks. */
1949- virtio_mem_sbm_for_each_mb_rev (vm , mb_id ,
1950- VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL ) {
1951- rc = virtio_mem_sbm_unplug_any_sb_offline (vm , mb_id , & nb_sb );
1952- if (rc || !nb_sb )
1953- goto out_unlock ;
1954- cond_resched ();
1955- }
1956-
1957- /* Try to unplug subblocks of plugged offline blocks. */
1958- virtio_mem_sbm_for_each_mb_rev (vm , mb_id , VIRTIO_MEM_SBM_MB_OFFLINE ) {
1959- rc = virtio_mem_sbm_unplug_any_sb_offline (vm , mb_id , & nb_sb );
1960- if (rc || !nb_sb )
1961- goto out_unlock ;
1962- cond_resched ();
1963- }
1964-
1965- if (!unplug_online ) {
1966- mutex_unlock (& vm -> hotplug_mutex );
1967- return 0 ;
1968- }
1969-
1970- /* Try to unplug subblocks of partially plugged online blocks. */
1971- virtio_mem_sbm_for_each_mb_rev (vm , mb_id ,
1972- VIRTIO_MEM_SBM_MB_ONLINE_PARTIAL ) {
1973- rc = virtio_mem_sbm_unplug_any_sb_online (vm , mb_id , & nb_sb );
1974- if (rc || !nb_sb )
1975- goto out_unlock ;
1976- mutex_unlock (& vm -> hotplug_mutex );
1977- cond_resched ();
1978- mutex_lock (& vm -> hotplug_mutex );
1979- }
1980-
1981- /* Try to unplug subblocks of plugged online blocks. */
1982- virtio_mem_sbm_for_each_mb_rev (vm , mb_id , VIRTIO_MEM_SBM_MB_ONLINE ) {
1983- rc = virtio_mem_sbm_unplug_any_sb_online (vm , mb_id , & nb_sb );
1984- if (rc || !nb_sb )
1985- goto out_unlock ;
1986- mutex_unlock (& vm -> hotplug_mutex );
1987- cond_resched ();
1988- mutex_lock (& vm -> hotplug_mutex );
1983+ /*
1984+ * We try unplug from partially plugged blocks first, to try removing
1985+ * whole memory blocks along with metadata.
1986+ */
1987+ for (i = 0 ; i < ARRAY_SIZE (mb_states ); i ++ ) {
1988+ virtio_mem_sbm_for_each_mb_rev (vm , mb_id , mb_states [i ]) {
1989+ rc = virtio_mem_sbm_unplug_any_sb (vm , mb_id , & nb_sb );
1990+ if (rc || !nb_sb )
1991+ goto out_unlock ;
1992+ mutex_unlock (& vm -> hotplug_mutex );
1993+ cond_resched ();
1994+ mutex_lock (& vm -> hotplug_mutex );
1995+ }
1996+ if (!unplug_online && i == 1 ) {
1997+ mutex_unlock (& vm -> hotplug_mutex );
1998+ return 0 ;
1999+ }
19892000 }
19902001
19912002 mutex_unlock (& vm -> hotplug_mutex );
0 commit comments