|
@@ -22,8 +22,7 @@
|
|
|
#include <linux/virtio.h>
|
|
|
#include <linux/virtio_balloon.h>
|
|
|
#include <linux/swap.h>
|
|
|
-#include <linux/kthread.h>
|
|
|
-#include <linux/freezer.h>
|
|
|
+#include <linux/workqueue.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/module.h>
|
|
@@ -49,11 +48,12 @@ struct virtio_balloon {
|
|
|
struct virtio_device *vdev;
|
|
|
struct virtqueue *inflate_vq, *deflate_vq, *stats_vq;
|
|
|
|
|
|
- /* Where the ballooning thread waits for config to change. */
|
|
|
- wait_queue_head_t config_change;
|
|
|
+ /* The balloon servicing is delegated to a freezable workqueue. */
|
|
|
+ struct work_struct work;
|
|
|
|
|
|
- /* The thread servicing the balloon. */
|
|
|
- struct task_struct *thread;
|
|
|
+ /* Prevent updating balloon when it is being canceled. */
|
|
|
+ spinlock_t stop_update_lock;
|
|
|
+ bool stop_update;
|
|
|
|
|
|
/* Waiting for host to ack the pages we released. */
|
|
|
wait_queue_head_t acked;
|
|
@@ -135,9 +135,10 @@ static void set_page_pfns(u32 pfns[], struct page *page)
|
|
|
pfns[i] = page_to_balloon_pfn(page) + i;
|
|
|
}
|
|
|
|
|
|
-static void fill_balloon(struct virtio_balloon *vb, size_t num)
|
|
|
+static unsigned fill_balloon(struct virtio_balloon *vb, size_t num)
|
|
|
{
|
|
|
struct balloon_dev_info *vb_dev_info = &vb->vb_dev_info;
|
|
|
+ unsigned num_allocated_pages;
|
|
|
|
|
|
/* We can only do one array worth at a time. */
|
|
|
num = min(num, ARRAY_SIZE(vb->pfns));
|
|
@@ -162,10 +163,13 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
|
|
|
adjust_managed_page_count(page, -1);
|
|
|
}
|
|
|
|
|
|
+ num_allocated_pages = vb->num_pfns;
|
|
|
/* Did we get any? */
|
|
|
if (vb->num_pfns != 0)
|
|
|
tell_host(vb, vb->inflate_vq);
|
|
|
mutex_unlock(&vb->balloon_lock);
|
|
|
+
|
|
|
+ return num_allocated_pages;
|
|
|
}
|
|
|
|
|
|
static void release_pages_balloon(struct virtio_balloon *vb)
|
|
@@ -251,14 +255,19 @@ static void update_balloon_stats(struct virtio_balloon *vb)
|
|
|
* with a single buffer. From that point forward, all conversations consist of
|
|
|
* a hypervisor request (a call to this function) which directs us to refill
|
|
|
* the virtqueue with a fresh stats buffer. Since stats collection can sleep,
|
|
|
- * we notify our kthread which does the actual work via stats_handle_request().
|
|
|
+ * we delegate the job to a freezable workqueue that will do the actual work via
|
|
|
+ * stats_handle_request().
|
|
|
*/
|
|
|
static void stats_request(struct virtqueue *vq)
|
|
|
{
|
|
|
struct virtio_balloon *vb = vq->vdev->priv;
|
|
|
|
|
|
vb->need_stats_update = 1;
|
|
|
- wake_up(&vb->config_change);
|
|
|
+
|
|
|
+ spin_lock(&vb->stop_update_lock);
|
|
|
+ if (!vb->stop_update)
|
|
|
+ queue_work(system_freezable_wq, &vb->work);
|
|
|
+ spin_unlock(&vb->stop_update_lock);
|
|
|
}
|
|
|
|
|
|
static void stats_handle_request(struct virtio_balloon *vb)
|
|
@@ -281,8 +290,12 @@ static void stats_handle_request(struct virtio_balloon *vb)
|
|
|
static void virtballoon_changed(struct virtio_device *vdev)
|
|
|
{
|
|
|
struct virtio_balloon *vb = vdev->priv;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
- wake_up(&vb->config_change);
|
|
|
+ spin_lock_irqsave(&vb->stop_update_lock, flags);
|
|
|
+ if (!vb->stop_update)
|
|
|
+ queue_work(system_freezable_wq, &vb->work);
|
|
|
+ spin_unlock_irqrestore(&vb->stop_update_lock, flags);
|
|
|
}
|
|
|
|
|
|
static inline s64 towards_target(struct virtio_balloon *vb)
|
|
@@ -345,43 +358,25 @@ static int virtballoon_oom_notify(struct notifier_block *self,
|
|
|
return NOTIFY_OK;
|
|
|
}
|
|
|
|
|
|
-static int balloon(void *_vballoon)
|
|
|
+static void balloon(struct work_struct *work)
|
|
|
{
|
|
|
- struct virtio_balloon *vb = _vballoon;
|
|
|
- DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
|
|
-
|
|
|
- set_freezable();
|
|
|
- while (!kthread_should_stop()) {
|
|
|
- s64 diff;
|
|
|
-
|
|
|
- try_to_freeze();
|
|
|
-
|
|
|
- add_wait_queue(&vb->config_change, &wait);
|
|
|
- for (;;) {
|
|
|
- if ((diff = towards_target(vb)) != 0 ||
|
|
|
- vb->need_stats_update ||
|
|
|
- kthread_should_stop() ||
|
|
|
- freezing(current))
|
|
|
- break;
|
|
|
- wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
|
|
|
- }
|
|
|
- remove_wait_queue(&vb->config_change, &wait);
|
|
|
+ struct virtio_balloon *vb;
|
|
|
+ s64 diff;
|
|
|
|
|
|
- if (vb->need_stats_update)
|
|
|
- stats_handle_request(vb);
|
|
|
- if (diff > 0)
|
|
|
- fill_balloon(vb, diff);
|
|
|
- else if (diff < 0)
|
|
|
- leak_balloon(vb, -diff);
|
|
|
- update_balloon_size(vb);
|
|
|
+ vb = container_of(work, struct virtio_balloon, work);
|
|
|
+ diff = towards_target(vb);
|
|
|
|
|
|
- /*
|
|
|
- * For large balloon changes, we could spend a lot of time
|
|
|
- * and always have work to do. Be nice if preempt disabled.
|
|
|
- */
|
|
|
- cond_resched();
|
|
|
- }
|
|
|
- return 0;
|
|
|
+ if (vb->need_stats_update)
|
|
|
+ stats_handle_request(vb);
|
|
|
+
|
|
|
+ if (diff > 0)
|
|
|
+ diff -= fill_balloon(vb, diff);
|
|
|
+ else if (diff < 0)
|
|
|
+ diff += leak_balloon(vb, -diff);
|
|
|
+ update_balloon_size(vb);
|
|
|
+
|
|
|
+ if (diff)
|
|
|
+ queue_work(system_freezable_wq, work);
|
|
|
}
|
|
|
|
|
|
static int init_vqs(struct virtio_balloon *vb)
|
|
@@ -499,9 +494,11 @@ static int virtballoon_probe(struct virtio_device *vdev)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ INIT_WORK(&vb->work, balloon);
|
|
|
+ spin_lock_init(&vb->stop_update_lock);
|
|
|
+ vb->stop_update = false;
|
|
|
vb->num_pages = 0;
|
|
|
mutex_init(&vb->balloon_lock);
|
|
|
- init_waitqueue_head(&vb->config_change);
|
|
|
init_waitqueue_head(&vb->acked);
|
|
|
vb->vdev = vdev;
|
|
|
vb->need_stats_update = 0;
|
|
@@ -523,16 +520,8 @@ static int virtballoon_probe(struct virtio_device *vdev)
|
|
|
|
|
|
virtio_device_ready(vdev);
|
|
|
|
|
|
- vb->thread = kthread_run(balloon, vb, "vballoon");
|
|
|
- if (IS_ERR(vb->thread)) {
|
|
|
- err = PTR_ERR(vb->thread);
|
|
|
- goto out_del_vqs;
|
|
|
- }
|
|
|
-
|
|
|
return 0;
|
|
|
|
|
|
-out_del_vqs:
|
|
|
- unregister_oom_notifier(&vb->nb);
|
|
|
out_oom_notify:
|
|
|
vdev->config->del_vqs(vdev);
|
|
|
out_free_vb:
|
|
@@ -559,7 +548,12 @@ static void virtballoon_remove(struct virtio_device *vdev)
|
|
|
struct virtio_balloon *vb = vdev->priv;
|
|
|
|
|
|
unregister_oom_notifier(&vb->nb);
|
|
|
- kthread_stop(vb->thread);
|
|
|
+
|
|
|
+ spin_lock_irq(&vb->stop_update_lock);
|
|
|
+ vb->stop_update = true;
|
|
|
+ spin_unlock_irq(&vb->stop_update_lock);
|
|
|
+ cancel_work_sync(&vb->work);
|
|
|
+
|
|
|
remove_common(vb);
|
|
|
kfree(vb);
|
|
|
}
|
|
@@ -570,10 +564,9 @@ static int virtballoon_freeze(struct virtio_device *vdev)
|
|
|
struct virtio_balloon *vb = vdev->priv;
|
|
|
|
|
|
/*
|
|
|
- * The kthread is already frozen by the PM core before this
|
|
|
+ * The workqueue is already frozen by the PM core before this
|
|
|
* function is called.
|
|
|
*/
|
|
|
-
|
|
|
remove_common(vb);
|
|
|
return 0;
|
|
|
}
|
|
@@ -589,7 +582,8 @@ static int virtballoon_restore(struct virtio_device *vdev)
|
|
|
|
|
|
virtio_device_ready(vdev);
|
|
|
|
|
|
- fill_balloon(vb, towards_target(vb));
|
|
|
+ if (towards_target(vb))
|
|
|
+ virtballoon_changed(vdev);
|
|
|
update_balloon_size(vb);
|
|
|
return 0;
|
|
|
}
|