virtio-mem: simplify high-level unplug handling in Big Block Mode
authorDavid Hildenbrand <david@redhat.com>
Wed, 2 Jun 2021 18:57:19 +0000 (20:57 +0200)
committerMichael S. Tsirkin <mst@redhat.com>
Thu, 8 Jul 2021 11:49:02 +0000 (07:49 -0400)
Let's simplify high-level big block selection when unplugging in
Big Block Mode.

Combine handling of offline and online blocks. We can get rid of
virtio_mem_bbm_bb_is_offline() and simply use
virtio_mem_bbm_offline_remove_and_unplug_bb(), as that already tolerates
offline parts.

We can race with concurrent onlining/offlining either way, so we don;t
have to be super correct by failing if an offline big block we'd like to
unplug just got (partially) onlined.

Signed-off-by: David Hildenbrand <david@redhat.com>
Link: https://lore.kernel.org/r/20210602185720.31821-7-david@redhat.com
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
drivers/virtio/virtio_mem.c

index 156a79c..4319938 100644 (file)
@@ -703,18 +703,6 @@ static int virtio_mem_sbm_remove_mb(struct virtio_mem *vm, unsigned long mb_id)
 }
 
 /*
- * See virtio_mem_remove_memory(): Try to remove all Linux memory blocks covered
- * by the big block.
- */
-static int virtio_mem_bbm_remove_bb(struct virtio_mem *vm, unsigned long bb_id)
-{
-       const uint64_t addr = virtio_mem_bb_id_to_phys(vm, bb_id);
-       const uint64_t size = vm->bbm.bb_size;
-
-       return virtio_mem_remove_memory(vm, addr, size);
-}
-
-/*
  * Try offlining and removing memory from Linux.
  *
  * Must not be called with the vm->hotplug_mutex held (possible deadlock with
@@ -2115,35 +2103,6 @@ rollback_safe_unplug:
 }
 
 /*
- * Try to remove a big block from Linux and unplug it. Will fail with
- * -EBUSY if some memory is online.
- *
- * Will modify the state of the memory block.
- */
-static int virtio_mem_bbm_remove_and_unplug_bb(struct virtio_mem *vm,
-                                              unsigned long bb_id)
-{
-       int rc;
-
-       if (WARN_ON_ONCE(virtio_mem_bbm_get_bb_state(vm, bb_id) !=
-                        VIRTIO_MEM_BBM_BB_ADDED))
-               return -EINVAL;
-
-       rc = virtio_mem_bbm_remove_bb(vm, bb_id);
-       if (rc)
-               return -EBUSY;
-
-       rc = virtio_mem_bbm_unplug_bb(vm, bb_id);
-       if (rc)
-               virtio_mem_bbm_set_bb_state(vm, bb_id,
-                                           VIRTIO_MEM_BBM_BB_PLUGGED);
-       else
-               virtio_mem_bbm_set_bb_state(vm, bb_id,
-                                           VIRTIO_MEM_BBM_BB_UNUSED);
-       return rc;
-}
-
-/*
  * Test if a big block is completely offline.
  */
 static bool virtio_mem_bbm_bb_is_offline(struct virtio_mem *vm,
@@ -2166,42 +2125,35 @@ static int virtio_mem_bbm_unplug_request(struct virtio_mem *vm, uint64_t diff)
 {
        uint64_t nb_bb = diff / vm->bbm.bb_size;
        uint64_t bb_id;
-       int rc;
+       int rc, i;
 
        if (!nb_bb)
                return 0;
 
-       /* Try to unplug completely offline big blocks first. */
-       virtio_mem_bbm_for_each_bb_rev(vm, bb_id, VIRTIO_MEM_BBM_BB_ADDED) {
-               cond_resched();
-               /*
-                * As we're holding no locks, this check is racy as memory
-                * can get onlined in the meantime - but we'll fail gracefully.
-                */
-               if (!virtio_mem_bbm_bb_is_offline(vm, bb_id))
-                       continue;
-               rc = virtio_mem_bbm_remove_and_unplug_bb(vm, bb_id);
-               if (rc == -EBUSY)
-                       continue;
-               if (!rc)
-                       nb_bb--;
-               if (rc || !nb_bb)
-                       return rc;
-       }
-
-       if (!unplug_online)
-               return 0;
+       /*
+        * Try to unplug big blocks. Similar to SBM, start with offline
+        * big blocks.
+        */
+       for (i = 0; i < 2; i++) {
+               virtio_mem_bbm_for_each_bb_rev(vm, bb_id, VIRTIO_MEM_BBM_BB_ADDED) {
+                       cond_resched();
 
-       /* Try to unplug any big blocks. */
-       virtio_mem_bbm_for_each_bb_rev(vm, bb_id, VIRTIO_MEM_BBM_BB_ADDED) {
-               cond_resched();
-               rc = virtio_mem_bbm_offline_remove_and_unplug_bb(vm, bb_id);
-               if (rc == -EBUSY)
-                       continue;
-               if (!rc)
-                       nb_bb--;
-               if (rc || !nb_bb)
-                       return rc;
+                       /*
+                        * As we're holding no locks, these checks are racy,
+                        * but we don't care.
+                        */
+                       if (i == 0 && !virtio_mem_bbm_bb_is_offline(vm, bb_id))
+                               continue;
+                       rc = virtio_mem_bbm_offline_remove_and_unplug_bb(vm, bb_id);
+                       if (rc == -EBUSY)
+                               continue;
+                       if (!rc)
+                               nb_bb--;
+                       if (rc || !nb_bb)
+                               return rc;
+               }
+               if (i == 0 && !unplug_online)
+                       return 0;
        }
 
        return nb_bb ? -EBUSY : 0;