.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2012, Microsoft Corporation. |
---|
3 | 4 | * |
---|
4 | 5 | * Author: |
---|
5 | 6 | * K. Y. Srinivasan <kys@microsoft.com> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify it |
---|
8 | | - * under the terms of the GNU General Public License version 2 as published |
---|
9 | | - * by the Free Software Foundation. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, but |
---|
12 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or |
---|
14 | | - * NON INFRINGEMENT. See the GNU General Public License for more |
---|
15 | | - * details. |
---|
16 | | - * |
---|
17 | 7 | */ |
---|
18 | 8 | |
---|
19 | 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
.. | .. |
---|
33 | 23 | #include <linux/percpu_counter.h> |
---|
34 | 24 | |
---|
35 | 25 | #include <linux/hyperv.h> |
---|
| 26 | +#include <asm/hyperv-tlfs.h> |
---|
| 27 | + |
---|
| 28 | +#include <asm/mshyperv.h> |
---|
36 | 29 | |
---|
37 | 30 | #define CREATE_TRACE_POINTS |
---|
38 | 31 | #include "hv_trace_balloon.h" |
---|
.. | .. |
---|
351 | 344 | * |
---|
352 | 345 | * mem_range: Memory range to hot add. |
---|
353 | 346 | * |
---|
354 | | - * On Linux we currently don't support this since we cannot hot add |
---|
355 | | - * arbitrary granularity of memory. |
---|
356 | 347 | */ |
---|
357 | 348 | |
---|
358 | 349 | struct dm_hot_add { |
---|
.. | .. |
---|
467 | 458 | struct work_struct wrk; |
---|
468 | 459 | }; |
---|
469 | 460 | |
---|
| 461 | +static bool allow_hibernation; |
---|
470 | 462 | static bool hot_add = true; |
---|
471 | 463 | static bool do_hot_add; |
---|
472 | 464 | /* |
---|
.. | .. |
---|
487 | 479 | MODULE_PARM_DESC(pressure_report_delay, "Delay in secs in reporting pressure"); |
---|
488 | 480 | static atomic_t trans_id = ATOMIC_INIT(0); |
---|
489 | 481 | |
---|
490 | | -static int dm_ring_size = (5 * PAGE_SIZE); |
---|
| 482 | +static int dm_ring_size = 20 * 1024; |
---|
491 | 483 | |
---|
492 | 484 | /* |
---|
493 | 485 | * Driver specific state. |
---|
.. | .. |
---|
503 | 495 | }; |
---|
504 | 496 | |
---|
505 | 497 | |
---|
506 | | -static __u8 recv_buffer[PAGE_SIZE]; |
---|
507 | | -static __u8 *send_buffer; |
---|
508 | | -#define PAGES_IN_2M 512 |
---|
509 | | -#define HA_CHUNK (32 * 1024) |
---|
| 498 | +static __u8 recv_buffer[HV_HYP_PAGE_SIZE]; |
---|
| 499 | +static __u8 balloon_up_send_buffer[HV_HYP_PAGE_SIZE]; |
---|
| 500 | +#define PAGES_IN_2M (2 * 1024 * 1024 / PAGE_SIZE) |
---|
| 501 | +#define HA_CHUNK (128 * 1024 * 1024 / PAGE_SIZE) |
---|
510 | 502 | |
---|
511 | 503 | struct hv_dynmem_device { |
---|
512 | 504 | struct hv_device *dev; |
---|
.. | .. |
---|
541 | 533 | * State to synchronize hot-add. |
---|
542 | 534 | */ |
---|
543 | 535 | struct completion ol_waitevent; |
---|
544 | | - bool ha_waiting; |
---|
545 | 536 | /* |
---|
546 | 537 | * This thread handles hot-add |
---|
547 | 538 | * requests from the host as well as notifying |
---|
.. | .. |
---|
642 | 633 | switch (val) { |
---|
643 | 634 | case MEM_ONLINE: |
---|
644 | 635 | case MEM_CANCEL_ONLINE: |
---|
645 | | - if (dm_device.ha_waiting) { |
---|
646 | | - dm_device.ha_waiting = false; |
---|
647 | | - complete(&dm_device.ol_waitevent); |
---|
648 | | - } |
---|
| 636 | + complete(&dm_device.ol_waitevent); |
---|
649 | 637 | break; |
---|
650 | 638 | |
---|
651 | 639 | case MEM_OFFLINE: |
---|
.. | .. |
---|
681 | 669 | /* Check if the particular page is backed and can be onlined and online it. */ |
---|
682 | 670 | static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) |
---|
683 | 671 | { |
---|
684 | | - if (!has_pfn_is_backed(has, page_to_pfn(pg))) |
---|
| 672 | + if (!has_pfn_is_backed(has, page_to_pfn(pg))) { |
---|
| 673 | + if (!PageOffline(pg)) |
---|
| 674 | + __SetPageOffline(pg); |
---|
685 | 675 | return; |
---|
| 676 | + } |
---|
| 677 | + if (PageOffline(pg)) |
---|
| 678 | + __ClearPageOffline(pg); |
---|
686 | 679 | |
---|
687 | 680 | /* This frame is currently backed; online the page. */ |
---|
688 | | - __online_page_set_limits(pg); |
---|
689 | | - __online_page_increment_counters(pg); |
---|
690 | | - __online_page_free(pg); |
---|
| 681 | + generic_online_page(pg, 0); |
---|
691 | 682 | |
---|
692 | | - WARN_ON_ONCE(!spin_is_locked(&dm_device.ha_lock)); |
---|
| 683 | + lockdep_assert_held(&dm_device.ha_lock); |
---|
693 | 684 | dm_device.num_pages_onlined++; |
---|
694 | 685 | } |
---|
695 | 686 | |
---|
.. | .. |
---|
731 | 722 | has->covered_end_pfn += processed_pfn; |
---|
732 | 723 | spin_unlock_irqrestore(&dm_device.ha_lock, flags); |
---|
733 | 724 | |
---|
734 | | - init_completion(&dm_device.ol_waitevent); |
---|
735 | | - dm_device.ha_waiting = !memhp_auto_online; |
---|
| 725 | + reinit_completion(&dm_device.ol_waitevent); |
---|
736 | 726 | |
---|
737 | 727 | nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn)); |
---|
738 | 728 | ret = add_memory(nid, PFN_PHYS((start_pfn)), |
---|
739 | | - (HA_CHUNK << PAGE_SHIFT)); |
---|
| 729 | + (HA_CHUNK << PAGE_SHIFT), MEMHP_MERGE_RESOURCE); |
---|
740 | 730 | |
---|
741 | 731 | if (ret) { |
---|
742 | 732 | pr_err("hot_add memory failed error is %d\n", ret); |
---|
.. | .. |
---|
758 | 748 | } |
---|
759 | 749 | |
---|
760 | 750 | /* |
---|
761 | | - * Wait for the memory block to be onlined when memory onlining |
---|
762 | | - * is done outside of kernel (memhp_auto_online). Since the hot |
---|
763 | | - * add has succeeded, it is ok to proceed even if the pages in |
---|
764 | | - * the hot added region have not been "onlined" within the |
---|
765 | | - * allowed time. |
---|
| 751 | + * Wait for memory to get onlined. If the kernel onlined the |
---|
| 752 | + * memory when adding it, this will return directly. Otherwise, |
---|
| 753 | + * it will wait for user space to online the memory. This helps |
---|
| 754 | + * to avoid adding memory faster than it is getting onlined. As |
---|
| 755 | + * adding succeeded, it is ok to proceed even if the memory was |
---|
| 756 | + * not onlined in time. |
---|
766 | 757 | */ |
---|
767 | | - if (dm_device.ha_waiting) |
---|
768 | | - wait_for_completion_timeout(&dm_device.ol_waitevent, |
---|
769 | | - 5*HZ); |
---|
| 758 | + wait_for_completion_timeout(&dm_device.ol_waitevent, 5 * HZ); |
---|
770 | 759 | post_status(&dm_device); |
---|
771 | 760 | } |
---|
772 | 761 | } |
---|
773 | 762 | |
---|
774 | | -static void hv_online_page(struct page *pg) |
---|
| 763 | +static void hv_online_page(struct page *pg, unsigned int order) |
---|
775 | 764 | { |
---|
776 | 765 | struct hv_hotadd_state *has; |
---|
777 | 766 | unsigned long flags; |
---|
.. | .. |
---|
780 | 769 | spin_lock_irqsave(&dm_device.ha_lock, flags); |
---|
781 | 770 | list_for_each_entry(has, &dm_device.ha_region_list, list) { |
---|
782 | 771 | /* The page belongs to a different HAS. */ |
---|
783 | | - if ((pfn < has->start_pfn) || (pfn >= has->end_pfn)) |
---|
| 772 | + if ((pfn < has->start_pfn) || |
---|
| 773 | + (pfn + (1UL << order) > has->end_pfn)) |
---|
784 | 774 | continue; |
---|
785 | 775 | |
---|
786 | | - hv_page_online_one(has, pg); |
---|
| 776 | + hv_bring_pgs_online(has, pfn, 1UL << order); |
---|
787 | 777 | break; |
---|
788 | 778 | } |
---|
789 | 779 | spin_unlock_irqrestore(&dm_device.ha_lock, flags); |
---|
.. | .. |
---|
1057 | 1047 | else |
---|
1058 | 1048 | resp.result = 0; |
---|
1059 | 1049 | |
---|
1060 | | - if (!do_hot_add || (resp.page_count == 0)) |
---|
1061 | | - pr_err("Memory hot add failed\n"); |
---|
| 1050 | + if (!do_hot_add || resp.page_count == 0) { |
---|
| 1051 | + if (!allow_hibernation) |
---|
| 1052 | + pr_err("Memory hot add failed\n"); |
---|
| 1053 | + else |
---|
| 1054 | + pr_info("Ignore hot-add request!\n"); |
---|
| 1055 | + } |
---|
1062 | 1056 | |
---|
1063 | 1057 | dm->state = DM_INITIALIZED; |
---|
1064 | 1058 | resp.hdr.trans_id = atomic_inc_return(&trans_id); |
---|
.. | .. |
---|
1080 | 1074 | __u64 *max_page_count = (__u64 *)&info_hdr[1]; |
---|
1081 | 1075 | |
---|
1082 | 1076 | pr_info("Max. dynamic memory size: %llu MB\n", |
---|
1083 | | - (*max_page_count) >> (20 - PAGE_SHIFT)); |
---|
| 1077 | + (*max_page_count) >> (20 - HV_HYP_PAGE_SHIFT)); |
---|
1084 | 1078 | } |
---|
1085 | 1079 | |
---|
1086 | 1080 | break; |
---|
.. | .. |
---|
1092 | 1086 | static unsigned long compute_balloon_floor(void) |
---|
1093 | 1087 | { |
---|
1094 | 1088 | unsigned long min_pages; |
---|
| 1089 | + unsigned long nr_pages = totalram_pages(); |
---|
1095 | 1090 | #define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) |
---|
1096 | 1091 | /* Simple continuous piecewiese linear function: |
---|
1097 | 1092 | * max MiB -> min MiB gradient |
---|
.. | .. |
---|
1104 | 1099 | * 8192 744 (1/16) |
---|
1105 | 1100 | * 32768 1512 (1/32) |
---|
1106 | 1101 | */ |
---|
1107 | | - if (totalram_pages < MB2PAGES(128)) |
---|
1108 | | - min_pages = MB2PAGES(8) + (totalram_pages >> 1); |
---|
1109 | | - else if (totalram_pages < MB2PAGES(512)) |
---|
1110 | | - min_pages = MB2PAGES(40) + (totalram_pages >> 2); |
---|
1111 | | - else if (totalram_pages < MB2PAGES(2048)) |
---|
1112 | | - min_pages = MB2PAGES(104) + (totalram_pages >> 3); |
---|
1113 | | - else if (totalram_pages < MB2PAGES(8192)) |
---|
1114 | | - min_pages = MB2PAGES(232) + (totalram_pages >> 4); |
---|
| 1102 | + if (nr_pages < MB2PAGES(128)) |
---|
| 1103 | + min_pages = MB2PAGES(8) + (nr_pages >> 1); |
---|
| 1104 | + else if (nr_pages < MB2PAGES(512)) |
---|
| 1105 | + min_pages = MB2PAGES(40) + (nr_pages >> 2); |
---|
| 1106 | + else if (nr_pages < MB2PAGES(2048)) |
---|
| 1107 | + min_pages = MB2PAGES(104) + (nr_pages >> 3); |
---|
| 1108 | + else if (nr_pages < MB2PAGES(8192)) |
---|
| 1109 | + min_pages = MB2PAGES(232) + (nr_pages >> 4); |
---|
1115 | 1110 | else |
---|
1116 | | - min_pages = MB2PAGES(488) + (totalram_pages >> 5); |
---|
| 1111 | + min_pages = MB2PAGES(488) + (nr_pages >> 5); |
---|
1117 | 1112 | #undef MB2PAGES |
---|
1118 | 1113 | return min_pages; |
---|
1119 | 1114 | } |
---|
.. | .. |
---|
1200 | 1195 | |
---|
1201 | 1196 | for (i = 0; i < num_pages; i++) { |
---|
1202 | 1197 | pg = pfn_to_page(i + start_frame); |
---|
| 1198 | + __ClearPageOffline(pg); |
---|
1203 | 1199 | __free_page(pg); |
---|
1204 | 1200 | dm->num_pages_ballooned--; |
---|
1205 | 1201 | } |
---|
.. | .. |
---|
1212 | 1208 | struct dm_balloon_response *bl_resp, |
---|
1213 | 1209 | int alloc_unit) |
---|
1214 | 1210 | { |
---|
1215 | | - unsigned int i = 0; |
---|
| 1211 | + unsigned int i, j; |
---|
1216 | 1212 | struct page *pg; |
---|
1217 | 1213 | |
---|
1218 | 1214 | for (i = 0; i < num_pages / alloc_unit; i++) { |
---|
1219 | 1215 | if (bl_resp->hdr.size + sizeof(union dm_mem_page_range) > |
---|
1220 | | - PAGE_SIZE) |
---|
| 1216 | + HV_HYP_PAGE_SIZE) |
---|
1221 | 1217 | return i * alloc_unit; |
---|
1222 | 1218 | |
---|
1223 | 1219 | /* |
---|
.. | .. |
---|
1240 | 1236 | |
---|
1241 | 1237 | if (alloc_unit != 1) |
---|
1242 | 1238 | split_page(pg, get_order(alloc_unit << PAGE_SHIFT)); |
---|
| 1239 | + |
---|
| 1240 | + /* mark all pages offline */ |
---|
| 1241 | + for (j = 0; j < (1 << get_order(alloc_unit << PAGE_SHIFT)); j++) |
---|
| 1242 | + __SetPageOffline(pg + j); |
---|
1243 | 1243 | |
---|
1244 | 1244 | bl_resp->range_count++; |
---|
1245 | 1245 | bl_resp->range_array[i].finfo.start_page = |
---|
.. | .. |
---|
1266 | 1266 | |
---|
1267 | 1267 | /* |
---|
1268 | 1268 | * We will attempt 2M allocations. However, if we fail to |
---|
1269 | | - * allocate 2M chunks, we will go back to 4k allocations. |
---|
| 1269 | + * allocate 2M chunks, we will go back to PAGE_SIZE allocations. |
---|
1270 | 1270 | */ |
---|
1271 | | - alloc_unit = 512; |
---|
| 1271 | + alloc_unit = PAGES_IN_2M; |
---|
1272 | 1272 | |
---|
1273 | 1273 | avail_pages = si_mem_available(); |
---|
1274 | 1274 | floor = compute_balloon_floor(); |
---|
.. | .. |
---|
1283 | 1283 | } |
---|
1284 | 1284 | |
---|
1285 | 1285 | while (!done) { |
---|
1286 | | - bl_resp = (struct dm_balloon_response *)send_buffer; |
---|
1287 | | - memset(send_buffer, 0, PAGE_SIZE); |
---|
| 1286 | + memset(balloon_up_send_buffer, 0, HV_HYP_PAGE_SIZE); |
---|
| 1287 | + bl_resp = (struct dm_balloon_response *)balloon_up_send_buffer; |
---|
1288 | 1288 | bl_resp->hdr.type = DM_BALLOON_RESPONSE; |
---|
1289 | 1289 | bl_resp->hdr.size = sizeof(struct dm_balloon_response); |
---|
1290 | 1290 | bl_resp->more_pages = 1; |
---|
.. | .. |
---|
1482 | 1482 | |
---|
1483 | 1483 | memset(recv_buffer, 0, sizeof(recv_buffer)); |
---|
1484 | 1484 | vmbus_recvpacket(dev->channel, recv_buffer, |
---|
1485 | | - PAGE_SIZE, &recvlen, &requestid); |
---|
| 1485 | + HV_HYP_PAGE_SIZE, &recvlen, &requestid); |
---|
1486 | 1486 | |
---|
1487 | 1487 | if (recvlen > 0) { |
---|
1488 | 1488 | dm_msg = (struct dm_message *)recv_buffer; |
---|
.. | .. |
---|
1500 | 1500 | break; |
---|
1501 | 1501 | |
---|
1502 | 1502 | case DM_BALLOON_REQUEST: |
---|
| 1503 | + if (allow_hibernation) { |
---|
| 1504 | + pr_info("Ignore balloon-up request!\n"); |
---|
| 1505 | + break; |
---|
| 1506 | + } |
---|
| 1507 | + |
---|
1503 | 1508 | if (dm->state == DM_BALLOON_UP) |
---|
1504 | 1509 | pr_warn("Currently ballooning\n"); |
---|
1505 | 1510 | bal_msg = (struct dm_balloon *)recv_buffer; |
---|
.. | .. |
---|
1509 | 1514 | break; |
---|
1510 | 1515 | |
---|
1511 | 1516 | case DM_UNBALLOON_REQUEST: |
---|
| 1517 | + if (allow_hibernation) { |
---|
| 1518 | + pr_info("Ignore balloon-down request!\n"); |
---|
| 1519 | + break; |
---|
| 1520 | + } |
---|
| 1521 | + |
---|
1512 | 1522 | dm->state = DM_BALLOON_DOWN; |
---|
1513 | 1523 | balloon_down(dm, |
---|
1514 | 1524 | (struct dm_unballoon_request *)recv_buffer); |
---|
.. | .. |
---|
1548 | 1558 | break; |
---|
1549 | 1559 | |
---|
1550 | 1560 | default: |
---|
1551 | | - pr_warn("Unhandled message: type: %d\n", dm_hdr->type); |
---|
| 1561 | + pr_warn_ratelimited("Unhandled message: type: %d\n", dm_hdr->type); |
---|
1552 | 1562 | |
---|
1553 | 1563 | } |
---|
1554 | 1564 | } |
---|
1555 | 1565 | |
---|
1556 | 1566 | } |
---|
1557 | 1567 | |
---|
1558 | | -static int balloon_probe(struct hv_device *dev, |
---|
1559 | | - const struct hv_vmbus_device_id *dev_id) |
---|
| 1568 | +static int balloon_connect_vsp(struct hv_device *dev) |
---|
1560 | 1569 | { |
---|
1561 | | - int ret; |
---|
1562 | | - unsigned long t; |
---|
1563 | 1570 | struct dm_version_request version_req; |
---|
1564 | 1571 | struct dm_capabilities cap_msg; |
---|
1565 | | - |
---|
1566 | | -#ifdef CONFIG_MEMORY_HOTPLUG |
---|
1567 | | - do_hot_add = hot_add; |
---|
1568 | | -#else |
---|
1569 | | - do_hot_add = false; |
---|
1570 | | -#endif |
---|
1571 | | - |
---|
1572 | | - /* |
---|
1573 | | - * First allocate a send buffer. |
---|
1574 | | - */ |
---|
1575 | | - |
---|
1576 | | - send_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); |
---|
1577 | | - if (!send_buffer) |
---|
1578 | | - return -ENOMEM; |
---|
| 1572 | + unsigned long t; |
---|
| 1573 | + int ret; |
---|
1579 | 1574 | |
---|
1580 | 1575 | ret = vmbus_open(dev->channel, dm_ring_size, dm_ring_size, NULL, 0, |
---|
1581 | | - balloon_onchannelcallback, dev); |
---|
1582 | | - |
---|
| 1576 | + balloon_onchannelcallback, dev); |
---|
1583 | 1577 | if (ret) |
---|
1584 | | - goto probe_error0; |
---|
| 1578 | + return ret; |
---|
1585 | 1579 | |
---|
1586 | | - dm_device.dev = dev; |
---|
1587 | | - dm_device.state = DM_INITIALIZING; |
---|
1588 | | - dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN8; |
---|
1589 | | - init_completion(&dm_device.host_event); |
---|
1590 | | - init_completion(&dm_device.config_event); |
---|
1591 | | - INIT_LIST_HEAD(&dm_device.ha_region_list); |
---|
1592 | | - spin_lock_init(&dm_device.ha_lock); |
---|
1593 | | - INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up); |
---|
1594 | | - INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req); |
---|
1595 | | - dm_device.host_specified_ha_region = false; |
---|
1596 | | - |
---|
1597 | | - dm_device.thread = |
---|
1598 | | - kthread_run(dm_thread_func, &dm_device, "hv_balloon"); |
---|
1599 | | - if (IS_ERR(dm_device.thread)) { |
---|
1600 | | - ret = PTR_ERR(dm_device.thread); |
---|
1601 | | - goto probe_error1; |
---|
1602 | | - } |
---|
1603 | | - |
---|
1604 | | -#ifdef CONFIG_MEMORY_HOTPLUG |
---|
1605 | | - set_online_page_callback(&hv_online_page); |
---|
1606 | | - register_memory_notifier(&hv_memory_nb); |
---|
1607 | | -#endif |
---|
1608 | | - |
---|
1609 | | - hv_set_drvdata(dev, &dm_device); |
---|
1610 | 1580 | /* |
---|
1611 | 1581 | * Initiate the hand shake with the host and negotiate |
---|
1612 | 1582 | * a version that the host can support. We start with the |
---|
.. | .. |
---|
1622 | 1592 | dm_device.version = version_req.version.version; |
---|
1623 | 1593 | |
---|
1624 | 1594 | ret = vmbus_sendpacket(dev->channel, &version_req, |
---|
1625 | | - sizeof(struct dm_version_request), |
---|
1626 | | - (unsigned long)NULL, |
---|
1627 | | - VM_PKT_DATA_INBAND, 0); |
---|
| 1595 | + sizeof(struct dm_version_request), |
---|
| 1596 | + (unsigned long)NULL, VM_PKT_DATA_INBAND, 0); |
---|
1628 | 1597 | if (ret) |
---|
1629 | | - goto probe_error2; |
---|
| 1598 | + goto out; |
---|
1630 | 1599 | |
---|
1631 | 1600 | t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ); |
---|
1632 | 1601 | if (t == 0) { |
---|
1633 | 1602 | ret = -ETIMEDOUT; |
---|
1634 | | - goto probe_error2; |
---|
| 1603 | + goto out; |
---|
1635 | 1604 | } |
---|
1636 | 1605 | |
---|
1637 | 1606 | /* |
---|
.. | .. |
---|
1639 | 1608 | * fail the probe function. |
---|
1640 | 1609 | */ |
---|
1641 | 1610 | if (dm_device.state == DM_INIT_ERROR) { |
---|
1642 | | - ret = -ETIMEDOUT; |
---|
1643 | | - goto probe_error2; |
---|
| 1611 | + ret = -EPROTO; |
---|
| 1612 | + goto out; |
---|
1644 | 1613 | } |
---|
1645 | 1614 | |
---|
1646 | 1615 | pr_info("Using Dynamic Memory protocol version %u.%u\n", |
---|
.. | .. |
---|
1655 | 1624 | cap_msg.hdr.size = sizeof(struct dm_capabilities); |
---|
1656 | 1625 | cap_msg.hdr.trans_id = atomic_inc_return(&trans_id); |
---|
1657 | 1626 | |
---|
| 1627 | + /* |
---|
| 1628 | + * When hibernation (i.e. virtual ACPI S4 state) is enabled, the host |
---|
| 1629 | + * currently still requires the bits to be set, so we have to add code |
---|
| 1630 | + * to fail the host's hot-add and balloon up/down requests, if any. |
---|
| 1631 | + */ |
---|
1658 | 1632 | cap_msg.caps.cap_bits.balloon = 1; |
---|
1659 | 1633 | cap_msg.caps.cap_bits.hot_add = 1; |
---|
1660 | 1634 | |
---|
.. | .. |
---|
1673 | 1647 | cap_msg.max_page_number = -1; |
---|
1674 | 1648 | |
---|
1675 | 1649 | ret = vmbus_sendpacket(dev->channel, &cap_msg, |
---|
1676 | | - sizeof(struct dm_capabilities), |
---|
1677 | | - (unsigned long)NULL, |
---|
1678 | | - VM_PKT_DATA_INBAND, 0); |
---|
| 1650 | + sizeof(struct dm_capabilities), |
---|
| 1651 | + (unsigned long)NULL, VM_PKT_DATA_INBAND, 0); |
---|
1679 | 1652 | if (ret) |
---|
1680 | | - goto probe_error2; |
---|
| 1653 | + goto out; |
---|
1681 | 1654 | |
---|
1682 | 1655 | t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ); |
---|
1683 | 1656 | if (t == 0) { |
---|
1684 | 1657 | ret = -ETIMEDOUT; |
---|
1685 | | - goto probe_error2; |
---|
| 1658 | + goto out; |
---|
1686 | 1659 | } |
---|
1687 | 1660 | |
---|
1688 | 1661 | /* |
---|
.. | .. |
---|
1690 | 1663 | * fail the probe function. |
---|
1691 | 1664 | */ |
---|
1692 | 1665 | if (dm_device.state == DM_INIT_ERROR) { |
---|
1693 | | - ret = -ETIMEDOUT; |
---|
1694 | | - goto probe_error2; |
---|
| 1666 | + ret = -EPROTO; |
---|
| 1667 | + goto out; |
---|
1695 | 1668 | } |
---|
1696 | 1669 | |
---|
| 1670 | + return 0; |
---|
| 1671 | +out: |
---|
| 1672 | + vmbus_close(dev->channel); |
---|
| 1673 | + return ret; |
---|
| 1674 | +} |
---|
| 1675 | + |
---|
| 1676 | +static int balloon_probe(struct hv_device *dev, |
---|
| 1677 | + const struct hv_vmbus_device_id *dev_id) |
---|
| 1678 | +{ |
---|
| 1679 | + int ret; |
---|
| 1680 | + |
---|
| 1681 | + allow_hibernation = hv_is_hibernation_supported(); |
---|
| 1682 | + if (allow_hibernation) |
---|
| 1683 | + hot_add = false; |
---|
| 1684 | + |
---|
| 1685 | +#ifdef CONFIG_MEMORY_HOTPLUG |
---|
| 1686 | + do_hot_add = hot_add; |
---|
| 1687 | +#else |
---|
| 1688 | + do_hot_add = false; |
---|
| 1689 | +#endif |
---|
| 1690 | + dm_device.dev = dev; |
---|
| 1691 | + dm_device.state = DM_INITIALIZING; |
---|
| 1692 | + dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN8; |
---|
| 1693 | + init_completion(&dm_device.host_event); |
---|
| 1694 | + init_completion(&dm_device.config_event); |
---|
| 1695 | + INIT_LIST_HEAD(&dm_device.ha_region_list); |
---|
| 1696 | + spin_lock_init(&dm_device.ha_lock); |
---|
| 1697 | + INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up); |
---|
| 1698 | + INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req); |
---|
| 1699 | + dm_device.host_specified_ha_region = false; |
---|
| 1700 | + |
---|
| 1701 | +#ifdef CONFIG_MEMORY_HOTPLUG |
---|
| 1702 | + set_online_page_callback(&hv_online_page); |
---|
| 1703 | + init_completion(&dm_device.ol_waitevent); |
---|
| 1704 | + register_memory_notifier(&hv_memory_nb); |
---|
| 1705 | +#endif |
---|
| 1706 | + |
---|
| 1707 | + hv_set_drvdata(dev, &dm_device); |
---|
| 1708 | + |
---|
| 1709 | + ret = balloon_connect_vsp(dev); |
---|
| 1710 | + if (ret != 0) |
---|
| 1711 | + return ret; |
---|
| 1712 | + |
---|
1697 | 1713 | dm_device.state = DM_INITIALIZED; |
---|
1698 | | - last_post_time = jiffies; |
---|
| 1714 | + |
---|
| 1715 | + dm_device.thread = |
---|
| 1716 | + kthread_run(dm_thread_func, &dm_device, "hv_balloon"); |
---|
| 1717 | + if (IS_ERR(dm_device.thread)) { |
---|
| 1718 | + ret = PTR_ERR(dm_device.thread); |
---|
| 1719 | + goto probe_error; |
---|
| 1720 | + } |
---|
1699 | 1721 | |
---|
1700 | 1722 | return 0; |
---|
1701 | 1723 | |
---|
1702 | | -probe_error2: |
---|
| 1724 | +probe_error: |
---|
| 1725 | + dm_device.state = DM_INIT_ERROR; |
---|
| 1726 | + dm_device.thread = NULL; |
---|
| 1727 | + vmbus_close(dev->channel); |
---|
1703 | 1728 | #ifdef CONFIG_MEMORY_HOTPLUG |
---|
| 1729 | + unregister_memory_notifier(&hv_memory_nb); |
---|
1704 | 1730 | restore_online_page_callback(&hv_online_page); |
---|
1705 | 1731 | #endif |
---|
1706 | | - kthread_stop(dm_device.thread); |
---|
1707 | | - |
---|
1708 | | -probe_error1: |
---|
1709 | | - vmbus_close(dev->channel); |
---|
1710 | | -probe_error0: |
---|
1711 | | - kfree(send_buffer); |
---|
1712 | 1732 | return ret; |
---|
1713 | 1733 | } |
---|
1714 | 1734 | |
---|
.. | .. |
---|
1725 | 1745 | cancel_work_sync(&dm->balloon_wrk.wrk); |
---|
1726 | 1746 | cancel_work_sync(&dm->ha_wrk.wrk); |
---|
1727 | 1747 | |
---|
1728 | | - vmbus_close(dev->channel); |
---|
1729 | 1748 | kthread_stop(dm->thread); |
---|
1730 | | - kfree(send_buffer); |
---|
| 1749 | + vmbus_close(dev->channel); |
---|
1731 | 1750 | #ifdef CONFIG_MEMORY_HOTPLUG |
---|
1732 | | - restore_online_page_callback(&hv_online_page); |
---|
1733 | 1751 | unregister_memory_notifier(&hv_memory_nb); |
---|
| 1752 | + restore_online_page_callback(&hv_online_page); |
---|
1734 | 1753 | #endif |
---|
1735 | 1754 | spin_lock_irqsave(&dm_device.ha_lock, flags); |
---|
1736 | 1755 | list_for_each_entry_safe(has, tmp, &dm->ha_region_list, list) { |
---|
.. | .. |
---|
1744 | 1763 | spin_unlock_irqrestore(&dm_device.ha_lock, flags); |
---|
1745 | 1764 | |
---|
1746 | 1765 | return 0; |
---|
| 1766 | +} |
---|
| 1767 | + |
---|
| 1768 | +static int balloon_suspend(struct hv_device *hv_dev) |
---|
| 1769 | +{ |
---|
| 1770 | + struct hv_dynmem_device *dm = hv_get_drvdata(hv_dev); |
---|
| 1771 | + |
---|
| 1772 | + tasklet_disable(&hv_dev->channel->callback_event); |
---|
| 1773 | + |
---|
| 1774 | + cancel_work_sync(&dm->balloon_wrk.wrk); |
---|
| 1775 | + cancel_work_sync(&dm->ha_wrk.wrk); |
---|
| 1776 | + |
---|
| 1777 | + if (dm->thread) { |
---|
| 1778 | + kthread_stop(dm->thread); |
---|
| 1779 | + dm->thread = NULL; |
---|
| 1780 | + vmbus_close(hv_dev->channel); |
---|
| 1781 | + } |
---|
| 1782 | + |
---|
| 1783 | + tasklet_enable(&hv_dev->channel->callback_event); |
---|
| 1784 | + |
---|
| 1785 | + return 0; |
---|
| 1786 | + |
---|
| 1787 | +} |
---|
| 1788 | + |
---|
| 1789 | +static int balloon_resume(struct hv_device *dev) |
---|
| 1790 | +{ |
---|
| 1791 | + int ret; |
---|
| 1792 | + |
---|
| 1793 | + dm_device.state = DM_INITIALIZING; |
---|
| 1794 | + |
---|
| 1795 | + ret = balloon_connect_vsp(dev); |
---|
| 1796 | + |
---|
| 1797 | + if (ret != 0) |
---|
| 1798 | + goto out; |
---|
| 1799 | + |
---|
| 1800 | + dm_device.thread = |
---|
| 1801 | + kthread_run(dm_thread_func, &dm_device, "hv_balloon"); |
---|
| 1802 | + if (IS_ERR(dm_device.thread)) { |
---|
| 1803 | + ret = PTR_ERR(dm_device.thread); |
---|
| 1804 | + dm_device.thread = NULL; |
---|
| 1805 | + goto close_channel; |
---|
| 1806 | + } |
---|
| 1807 | + |
---|
| 1808 | + dm_device.state = DM_INITIALIZED; |
---|
| 1809 | + return 0; |
---|
| 1810 | +close_channel: |
---|
| 1811 | + vmbus_close(dev->channel); |
---|
| 1812 | +out: |
---|
| 1813 | + dm_device.state = DM_INIT_ERROR; |
---|
| 1814 | +#ifdef CONFIG_MEMORY_HOTPLUG |
---|
| 1815 | + unregister_memory_notifier(&hv_memory_nb); |
---|
| 1816 | + restore_online_page_callback(&hv_online_page); |
---|
| 1817 | +#endif |
---|
| 1818 | + return ret; |
---|
1747 | 1819 | } |
---|
1748 | 1820 | |
---|
1749 | 1821 | static const struct hv_vmbus_device_id id_table[] = { |
---|
.. | .. |
---|
1760 | 1832 | .id_table = id_table, |
---|
1761 | 1833 | .probe = balloon_probe, |
---|
1762 | 1834 | .remove = balloon_remove, |
---|
| 1835 | + .suspend = balloon_suspend, |
---|
| 1836 | + .resume = balloon_resume, |
---|
1763 | 1837 | .driver = { |
---|
1764 | 1838 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
---|
1765 | 1839 | }, |
---|