Received: by 10.223.176.46 with SMTP id f43csp592572wra; Wed, 24 Jan 2018 03:02:53 -0800 (PST) X-Google-Smtp-Source: AH8x22711BC9ombE5rFDhyVpIPE3LsMy0uv7JfSJ4ebGbcyIoIdn+EDzlpSg9AIvapNJLTOQiutr X-Received: by 10.98.28.209 with SMTP id c200mr3188285pfc.24.1516791773305; Wed, 24 Jan 2018 03:02:53 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516791773; cv=none; d=google.com; s=arc-20160816; b=Qt4cY9Q+L4MAbwNULygtkO18mcR56gACNCs8e+pbNeMe43GK6NztUeI2F4kGS9wh4k 6wLLxdI2s8NK3ia8M3SzYqCLxrWVlk5Wx6w4YHrp5CGMjDjXuaIoQD1JyXqjSvf7K9v7 WNKp1QhbJoTZq7nh0IDHKQY8OpuzIZNT+TpT0NC3gxPKPS/o4O63JBzV5clgTYbFSlPx I7J6xtf56aXxWjOzKDvGBh+BS/tLG4fkal3UAg4Ym81I1DAfmVvi9gvnz/2v7f16TIX/ eeJQeFbrs5ZFpyOlBjRxLPRNgjUlUfJPZqgB5aNKJPPDbGNe4HWvCYbT5j+PBrm7JwDy O97g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=YFIRtfXJP+PulrtyM4WUkO/vXHHdK7FQGkLulzupGbs=; b=hPww9SDmfPgbO4H0bfHBF+U1Ngmof2Mpua1pRkY9j67+bkxuqTia6kRFKKO/dEwZv3 IzwK+NzMepLqMvCxaaxP6rF+/8z5hc7NuGEcKwzQEcNoYwHnx6kJn20fr39j7m3mcCh7 pWW8vHQ2rDvexFxuPUQPXP7Ge4kaF100GDZoj8ZwCABKdR95omdeiVrK2DBEf2hhsIYx EFvqroFuSr5kKb7cB8lsgqnzcUMHSjpoQ1mTfPVpUDQjPrdLjlQg3t+5hglTNaboABdc C4DvCawE86KctZ5yK7xwVzDrwT2YHl/jJVu8SV+CrkNRgWFfM+99amwj3KNhFcb59K/b 28lw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x124si9667pgb.437.2018.01.24.03.02.39; Wed, 24 Jan 2018 03:02:53 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933179AbeAXLBp (ORCPT + 99 others); Wed, 24 Jan 2018 06:01:45 -0500 Received: from mga03.intel.com ([134.134.136.65]:50845 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933090AbeAXLBl (ORCPT ); Wed, 24 Jan 2018 06:01:41 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 24 Jan 2018 03:01:41 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.46,406,1511856000"; d="scan'208";a="22195020" Received: from devel-ww.sh.intel.com ([10.239.48.110]) by FMSMGA003.fm.intel.com with ESMTP; 24 Jan 2018 03:01:38 -0800 From: Wei Wang To: virtio-dev@lists.oasis-open.org, linux-kernel@vger.kernel.org, virtualization@lists.linux-foundation.org, kvm@vger.kernel.org, linux-mm@kvack.org, mst@redhat.com, mhocko@kernel.org, akpm@linux-foundation.org Cc: pbonzini@redhat.com, wei.w.wang@intel.com, liliang.opensource@gmail.com, yang.zhang.wz@gmail.com, quan.xu0@gmail.com, nilal@redhat.com, riel@redhat.com Subject: [PATCH v24 1/2] mm: support reporting free page blocks Date: Wed, 24 Jan 2018 18:42:41 +0800 Message-Id: <1516790562-37889-2-git-send-email-wei.w.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1516790562-37889-1-git-send-email-wei.w.wang@intel.com> References: <1516790562-37889-1-git-send-email-wei.w.wang@intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds support to walk through the free page blocks in the system and report them via a callback function. Some page blocks may leave the free list after zone->lock is released, so it is the caller's responsibility to either detect or prevent the use of such pages. One use example of this patch is to accelerate live migration by skipping the transfer of free pages reported from the guest. A popular method used by the hypervisor to track which part of memory is written during live migration is to write-protect all the guest memory. So, those pages that are reported as free pages but are written after the report function returns will be captured by the hypervisor, and they will be added to the next round of memory transfer. Signed-off-by: Wei Wang Signed-off-by: Liang Li Cc: Michal Hocko Cc: Michael S. Tsirkin Acked-by: Michal Hocko --- include/linux/mm.h | 6 ++++ mm/page_alloc.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index ea818ff..b3077dd 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1938,6 +1938,12 @@ extern void free_area_init_node(int nid, unsigned long * zones_size, unsigned long zone_start_pfn, unsigned long *zholes_size); extern void free_initmem(void); +extern void walk_free_mem_block(void *opaque, + int min_order, + bool (*report_pfn_range)(void *opaque, + unsigned long pfn, + unsigned long num)); + /* * Free reserved pages within range [PAGE_ALIGN(start), end & PAGE_MASK) * into the buddy system. The freed pages will be poisoned with pattern diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 76c9688..705de22 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4899,6 +4899,97 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask) show_swap_cache_info(); } +/* + * Walk through a free page list and report the found pfn range via the + * callback. + * + * Return false if the callback requests to stop reporting. Otherwise, + * return true. + */ +static bool walk_free_page_list(void *opaque, + struct zone *zone, + int order, + enum migratetype mt, + bool (*report_pfn_range)(void *, + unsigned long, + unsigned long)) +{ + struct page *page; + struct list_head *list; + unsigned long pfn, flags; + bool ret; + + spin_lock_irqsave(&zone->lock, flags); + list = &zone->free_area[order].free_list[mt]; + list_for_each_entry(page, list, lru) { + pfn = page_to_pfn(page); + ret = report_pfn_range(opaque, pfn, 1 << order); + if (!ret) + break; + } + spin_unlock_irqrestore(&zone->lock, flags); + + return ret; +} + +/** + * walk_free_mem_block - Walk through the free page blocks in the system + * @opaque: the context passed from the caller + * @min_order: the minimum order of free lists to check + * @report_pfn_range: the callback to report the pfn range of the free pages + * + * If the callback returns false, stop iterating the list of free page blocks. + * Otherwise, continue to report. + * + * Please note that there are no locking guarantees for the callback and + * that the reported pfn range might be freed or disappear after the + * callback returns so the caller has to be very careful how it is used. + * + * The callback itself must not sleep or perform any operations which would + * require any memory allocations directly (not even GFP_NOWAIT/GFP_ATOMIC) + * or via any lock dependency. It is generally advisable to implement + * the callback as simple as possible and defer any heavy lifting to a + * different context. + * + * There is no guarantee that each free range will be reported only once + * during one walk_free_mem_block invocation. + * + * pfn_to_page on the given range is strongly discouraged and if there is + * an absolute need for that make sure to contact MM people to discuss + * potential problems. + * + * The function itself might sleep so it cannot be called from atomic + * contexts. + * + * In general low orders tend to be very volatile and so it makes more + * sense to query larger ones first for various optimizations which like + * ballooning etc... This will reduce the overhead as well. + */ +void walk_free_mem_block(void *opaque, + int min_order, + bool (*report_pfn_range)(void *opaque, + unsigned long pfn, + unsigned long num)) +{ + struct zone *zone; + int order; + enum migratetype mt; + bool ret; + + for_each_populated_zone(zone) { + for (order = MAX_ORDER - 1; order >= min_order; order--) { + for (mt = 0; mt < MIGRATE_TYPES; mt++) { + ret = walk_free_page_list(opaque, zone, + order, mt, + report_pfn_range); + if (!ret) + return; + } + } + } +} +EXPORT_SYMBOL_GPL(walk_free_mem_block); + static void zoneref_set_zone(struct zone *zone, struct zoneref *zoneref) { zoneref->zone = zone; -- 2.7.4