virtio-mem: simplify high-level unplug handling in Sub Block Mode
authorDavid Hildenbrand <david@redhat.com>
Wed, 2 Jun 2021 18:57:17 +0000 (20:57 +0200)
committerMichael S. Tsirkin <mst@redhat.com>
Thu, 8 Jul 2021 11:49:02 +0000 (07:49 -0400)
Let's simplify by introducing a new virtio_mem_sbm_unplug_any_sb(),
similar to virtio_mem_sbm_plug_any_sb(), to simplify high-level memory
block selection when unplugging in Sub Block Mode.

Rename existing virtio_mem_sbm_unplug_any_sb() to
virtio_mem_sbm_unplug_any_sb_raw().

The only change is that we now temporarily unlock the hotplug mutex around
cond_resched() when processing offline memory blocks, which doesn't
make a real difference as we already have to temporarily unlock in
virtio_mem_sbm_unplug_any_sb_offline() when removing a memory block.

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

index c0e6ea6..d54bb34 100644 (file)
@@ -1453,8 +1453,8 @@ static int virtio_mem_bbm_plug_bb(struct virtio_mem *vm, unsigned long bb_id)
  *
  * Note: can fail after some subblocks were unplugged.
  */
-static int virtio_mem_sbm_unplug_any_sb(struct virtio_mem *vm,
-                                       unsigned long mb_id, uint64_t *nb_sb)
+static int virtio_mem_sbm_unplug_any_sb_raw(struct virtio_mem *vm,
+                                           unsigned long mb_id, uint64_t *nb_sb)
 {
        int sb_id, count;
        int rc;
@@ -1496,7 +1496,7 @@ static int virtio_mem_sbm_unplug_mb(struct virtio_mem *vm, unsigned long mb_id)
 {
        uint64_t nb_sb = vm->sbm.sbs_per_mb;
 
-       return virtio_mem_sbm_unplug_any_sb(vm, mb_id, &nb_sb);
+       return virtio_mem_sbm_unplug_any_sb_raw(vm, mb_id, &nb_sb);
 }
 
 /*
@@ -1806,7 +1806,7 @@ static int virtio_mem_sbm_unplug_any_sb_offline(struct virtio_mem *vm,
 {
        int rc;
 
-       rc = virtio_mem_sbm_unplug_any_sb(vm, mb_id, nb_sb);
+       rc = virtio_mem_sbm_unplug_any_sb_raw(vm, mb_id, nb_sb);
 
        /* some subblocks might have been unplugged even on failure */
        if (!virtio_mem_sbm_test_sb_plugged(vm, mb_id, 0, vm->sbm.sbs_per_mb))
@@ -1929,11 +1929,46 @@ unplugged:
        return 0;
 }
 
+/*
+ * Unplug the desired number of plugged subblocks of a memory block that is
+ * already added to Linux. Will skip subblock of online memory blocks that are
+ * busy (by the OS). Will fail if any subblock that's not busy cannot get
+ * unplugged.
+ *
+ * Will modify the state of the memory block. Might temporarily drop the
+ * hotplug_mutex.
+ *
+ * Note: Can fail after some subblocks were successfully unplugged. Can
+ *       return 0 even if subblocks were busy and could not get unplugged.
+ */
+static int virtio_mem_sbm_unplug_any_sb(struct virtio_mem *vm,
+                                       unsigned long mb_id,
+                                       uint64_t *nb_sb)
+{
+       const int old_state = virtio_mem_sbm_get_mb_state(vm, mb_id);
+
+       switch (old_state) {
+       case VIRTIO_MEM_SBM_MB_ONLINE_PARTIAL:
+       case VIRTIO_MEM_SBM_MB_ONLINE:
+               return virtio_mem_sbm_unplug_any_sb_online(vm, mb_id, nb_sb);
+       case VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL:
+       case VIRTIO_MEM_SBM_MB_OFFLINE:
+               return virtio_mem_sbm_unplug_any_sb_offline(vm, mb_id, nb_sb);
+       }
+       return -EINVAL;
+}
+
 static int virtio_mem_sbm_unplug_request(struct virtio_mem *vm, uint64_t diff)
 {
+       const int mb_states[] = {
+               VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL,
+               VIRTIO_MEM_SBM_MB_OFFLINE,
+               VIRTIO_MEM_SBM_MB_ONLINE_PARTIAL,
+               VIRTIO_MEM_SBM_MB_ONLINE,
+       };
        uint64_t nb_sb = diff / vm->sbm.sb_size;
        unsigned long mb_id;
-       int rc;
+       int rc, i;
 
        if (!nb_sb)
                return 0;
@@ -1945,47 +1980,23 @@ static int virtio_mem_sbm_unplug_request(struct virtio_mem *vm, uint64_t diff)
         */
        mutex_lock(&vm->hotplug_mutex);
 
-       /* Try to unplug subblocks of partially plugged offline blocks. */
-       virtio_mem_sbm_for_each_mb_rev(vm, mb_id,
-                                      VIRTIO_MEM_SBM_MB_OFFLINE_PARTIAL) {
-               rc = virtio_mem_sbm_unplug_any_sb_offline(vm, mb_id, &nb_sb);
-               if (rc || !nb_sb)
-                       goto out_unlock;
-               cond_resched();
-       }
-
-       /* Try to unplug subblocks of plugged offline blocks. */
-       virtio_mem_sbm_for_each_mb_rev(vm, mb_id, VIRTIO_MEM_SBM_MB_OFFLINE) {
-               rc = virtio_mem_sbm_unplug_any_sb_offline(vm, mb_id, &nb_sb);
-               if (rc || !nb_sb)
-                       goto out_unlock;
-               cond_resched();
-       }
-
-       if (!unplug_online) {
-               mutex_unlock(&vm->hotplug_mutex);
-               return 0;
-       }
-
-       /* Try to unplug subblocks of partially plugged online blocks. */
-       virtio_mem_sbm_for_each_mb_rev(vm, mb_id,
-                                      VIRTIO_MEM_SBM_MB_ONLINE_PARTIAL) {
-               rc = virtio_mem_sbm_unplug_any_sb_online(vm, mb_id, &nb_sb);
-               if (rc || !nb_sb)
-                       goto out_unlock;
-               mutex_unlock(&vm->hotplug_mutex);
-               cond_resched();
-               mutex_lock(&vm->hotplug_mutex);
-       }
-
-       /* Try to unplug subblocks of plugged online blocks. */
-       virtio_mem_sbm_for_each_mb_rev(vm, mb_id, VIRTIO_MEM_SBM_MB_ONLINE) {
-               rc = virtio_mem_sbm_unplug_any_sb_online(vm, mb_id, &nb_sb);
-               if (rc || !nb_sb)
-                       goto out_unlock;
-               mutex_unlock(&vm->hotplug_mutex);
-               cond_resched();
-               mutex_lock(&vm->hotplug_mutex);
+       /*
+        * We try unplug from partially plugged blocks first, to try removing
+        * whole memory blocks along with metadata.
+        */
+       for (i = 0; i < ARRAY_SIZE(mb_states); i++) {
+               virtio_mem_sbm_for_each_mb_rev(vm, mb_id, mb_states[i]) {
+                       rc = virtio_mem_sbm_unplug_any_sb(vm, mb_id, &nb_sb);
+                       if (rc || !nb_sb)
+                               goto out_unlock;
+                       mutex_unlock(&vm->hotplug_mutex);
+                       cond_resched();
+                       mutex_lock(&vm->hotplug_mutex);
+               }
+               if (!unplug_online && i == 1) {
+                       mutex_unlock(&vm->hotplug_mutex);
+                       return 0;
+               }
        }
 
        mutex_unlock(&vm->hotplug_mutex);