|
@@ -533,6 +533,9 @@ struct hv_dynmem_device {
|
|
|
*/
|
|
|
struct task_struct *thread;
|
|
|
|
|
|
+ struct mutex ha_region_mutex;
|
|
|
+ struct completion waiter_event;
|
|
|
+
|
|
|
/*
|
|
|
* A list of hot-add regions.
|
|
|
*/
|
|
@@ -549,7 +552,59 @@ struct hv_dynmem_device {
|
|
|
static struct hv_dynmem_device dm_device;
|
|
|
|
|
|
static void post_status(struct hv_dynmem_device *dm);
|
|
|
+
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
|
+static void acquire_region_mutex(bool trylock)
|
|
|
+{
|
|
|
+ if (trylock) {
|
|
|
+ reinit_completion(&dm_device.waiter_event);
|
|
|
+ while (!mutex_trylock(&dm_device.ha_region_mutex))
|
|
|
+ wait_for_completion(&dm_device.waiter_event);
|
|
|
+ } else {
|
|
|
+ mutex_lock(&dm_device.ha_region_mutex);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void release_region_mutex(bool trylock)
|
|
|
+{
|
|
|
+ if (trylock) {
|
|
|
+ mutex_unlock(&dm_device.ha_region_mutex);
|
|
|
+ } else {
|
|
|
+ mutex_unlock(&dm_device.ha_region_mutex);
|
|
|
+ complete(&dm_device.waiter_event);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
|
|
|
+ void *v)
|
|
|
+{
|
|
|
+ switch (val) {
|
|
|
+ case MEM_GOING_ONLINE:
|
|
|
+ acquire_region_mutex(true);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MEM_ONLINE:
|
|
|
+ case MEM_CANCEL_ONLINE:
|
|
|
+ release_region_mutex(true);
|
|
|
+ if (dm_device.ha_waiting) {
|
|
|
+ dm_device.ha_waiting = false;
|
|
|
+ complete(&dm_device.ol_waitevent);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MEM_GOING_OFFLINE:
|
|
|
+ case MEM_OFFLINE:
|
|
|
+ case MEM_CANCEL_OFFLINE:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return NOTIFY_OK;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block hv_memory_nb = {
|
|
|
+ .notifier_call = hv_memory_notifier,
|
|
|
+ .priority = 0
|
|
|
+};
|
|
|
+
|
|
|
|
|
|
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
|
|
|
{
|
|
@@ -591,6 +646,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
|
|
init_completion(&dm_device.ol_waitevent);
|
|
|
dm_device.ha_waiting = true;
|
|
|
|
|
|
+ release_region_mutex(false);
|
|
|
nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn));
|
|
|
ret = add_memory(nid, PFN_PHYS((start_pfn)),
|
|
|
(HA_CHUNK << PAGE_SHIFT));
|
|
@@ -619,6 +675,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
|
|
* have not been "onlined" within the allowed time.
|
|
|
*/
|
|
|
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
|
|
|
+ acquire_region_mutex(false);
|
|
|
post_status(&dm_device);
|
|
|
}
|
|
|
|
|
@@ -632,11 +689,6 @@ static void hv_online_page(struct page *pg)
|
|
|
unsigned long cur_start_pgp;
|
|
|
unsigned long cur_end_pgp;
|
|
|
|
|
|
- if (dm_device.ha_waiting) {
|
|
|
- dm_device.ha_waiting = false;
|
|
|
- complete(&dm_device.ol_waitevent);
|
|
|
- }
|
|
|
-
|
|
|
list_for_each(cur, &dm_device.ha_region_list) {
|
|
|
has = list_entry(cur, struct hv_hotadd_state, list);
|
|
|
cur_start_pgp = (unsigned long)
|
|
@@ -834,6 +886,7 @@ static void hot_add_req(struct work_struct *dummy)
|
|
|
resp.hdr.size = sizeof(struct dm_hot_add_response);
|
|
|
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
|
+ acquire_region_mutex(false);
|
|
|
pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
|
|
|
pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
|
|
|
|
|
@@ -865,6 +918,7 @@ static void hot_add_req(struct work_struct *dummy)
|
|
|
if (do_hot_add)
|
|
|
resp.page_count = process_hot_add(pg_start, pfn_cnt,
|
|
|
rg_start, rg_sz);
|
|
|
+ release_region_mutex(false);
|
|
|
#endif
|
|
|
/*
|
|
|
* The result field of the response structure has the
|
|
@@ -1388,7 +1442,9 @@ static int balloon_probe(struct hv_device *dev,
|
|
|
dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;
|
|
|
init_completion(&dm_device.host_event);
|
|
|
init_completion(&dm_device.config_event);
|
|
|
+ init_completion(&dm_device.waiter_event);
|
|
|
INIT_LIST_HEAD(&dm_device.ha_region_list);
|
|
|
+ mutex_init(&dm_device.ha_region_mutex);
|
|
|
INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
|
|
|
INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req);
|
|
|
dm_device.host_specified_ha_region = false;
|
|
@@ -1402,6 +1458,7 @@ static int balloon_probe(struct hv_device *dev,
|
|
|
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
|
set_online_page_callback(&hv_online_page);
|
|
|
+ register_memory_notifier(&hv_memory_nb);
|
|
|
#endif
|
|
|
|
|
|
hv_set_drvdata(dev, &dm_device);
|
|
@@ -1520,6 +1577,7 @@ static int balloon_remove(struct hv_device *dev)
|
|
|
kfree(send_buffer);
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
|
|
restore_online_page_callback(&hv_online_page);
|
|
|
+ unregister_memory_notifier(&hv_memory_nb);
|
|
|
#endif
|
|
|
list_for_each_safe(cur, tmp, &dm->ha_region_list) {
|
|
|
has = list_entry(cur, struct hv_hotadd_state, list);
|