mirror of
https://github.com/opnsense/src.git
synced 2026-03-16 07:41:02 -04:00
This is to fix slowdowns with drm-kmod that get worse over time as physical memory become more fragmented (and probably also depending on other factors). Based on information posted in this bug report: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=277476 By default, linux_alloc_pages() retries failed allocations by calling vm_page_reclaim_contig() to attempt to free contiguous physical memory pages. vm_page_reclaim_contig() does not always succeed and calling it can be very slow even when it fails. When physical memory is very fragmented, vm_page_reclaim_contig() can end up being called (and failing) after every allocation attempt. This could cause very noticeable graphical desktop hangs (which could last seconds). The drm-kmod code in question attempts to allocate multiple contiguous pages at once but does not actually require them to be contiguous. It can fallback to doing multiple smaller allocations when larger allocations fail. It passes alloc_pages() the __GFP_NORETRY flag in this case. This patch makes linux_alloc_pages() fail early (without retrying) when this flag is passed. [olce: The problem this patch fixes is longer and longer GUI freezes as a machine's memory gets filled and becomes fragmented, when using amdgpu from DRM kmod 5.15 and DRM kmod 6.1 (DRM kmod 5.10 is unaffected; newer Linux kernel introduced an "optimization" by which a pool of pages is filled preferentially with contiguous pages, which triggered the problem for us). The original commit message above evokes freezes lasting seconds, but I occasionally witnessed some lasting tens of minutes, rendering a machine completely useless. The patch has been reviewed for its potential impacts to other LinuxKPI parts and our existing DRM kmods' code. In particular, there is no other user of __GFP_NORETRY/GFP_NORETRY with Linux's alloc_pages*() functions in our tree or DRM kmod ports. It has also been tested extensively, by me for months against 14-STABLE and sporadically on -CURRENT on a RX580, and by several others as reported below and as is visible in more details in the quoted bugzilla PR and in the initial drm-kmod issue at https://github.com/freebsd/drm-kmod/issues/302, on a variety of other AMD GPUs (several RX580, RX570, Radeon Pro WX5100, Green Sardine 5600G, Ryzen 9 4900H with embedded Renoir).] PR: 277476 Reported by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net> Reviewed by: olce Tested by: many (olce, Pierre Pronchery, Evgenii Khramtsov, chaplina, rk) MFC after: 2 weeks Relnotes: yes Sponsored by: The FreeBSD Foundation (review and part of testing) (cherry picked from commit 718d1928f8748fe4429c011296f94f194d63c695)
213 lines
5.2 KiB
C
213 lines
5.2 KiB
C
/*-
|
|
* Copyright (c) 2010 Isilon Systems, Inc.
|
|
* Copyright (c) 2010 iX Systems, Inc.
|
|
* Copyright (c) 2010 Panasas, Inc.
|
|
* Copyright (c) 2013-2017 Mellanox Technologies, Ltd.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice unmodified, this list of conditions, and the following
|
|
* disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef _LINUXKPI_LINUX_GFP_H_
|
|
#define _LINUXKPI_LINUX_GFP_H_
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <sys/types.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
|
|
#include <linux/page.h>
|
|
|
|
#include <vm/vm_param.h>
|
|
#include <vm/vm_object.h>
|
|
#include <vm/vm_extern.h>
|
|
#include <vm/vm_kern.h>
|
|
|
|
#define __GFP_NOWARN 0
|
|
#define __GFP_HIGHMEM 0
|
|
#define __GFP_ZERO M_ZERO
|
|
#define __GFP_NOMEMALLOC 0
|
|
#define __GFP_RECLAIM 0
|
|
#define __GFP_RECLAIMABLE 0
|
|
#define __GFP_RETRY_MAYFAIL 0
|
|
#define __GFP_MOVABLE 0
|
|
#define __GFP_COMP 0
|
|
#define __GFP_KSWAPD_RECLAIM 0
|
|
|
|
#define __GFP_IO 0
|
|
#define __GFP_NO_KSWAPD 0
|
|
#define __GFP_KSWAPD_RECLAIM 0
|
|
#define __GFP_WAIT M_WAITOK
|
|
#define __GFP_DMA32 (1U << 24) /* LinuxKPI only */
|
|
#define __GFP_NORETRY (1U << 25) /* LinuxKPI only */
|
|
#define __GFP_BITS_SHIFT 26
|
|
#define __GFP_BITS_MASK ((1 << __GFP_BITS_SHIFT) - 1)
|
|
#define __GFP_NOFAIL M_WAITOK
|
|
|
|
#define GFP_NOWAIT M_NOWAIT
|
|
#define GFP_ATOMIC (M_NOWAIT | M_USE_RESERVE)
|
|
#define GFP_KERNEL M_WAITOK
|
|
#define GFP_USER M_WAITOK
|
|
#define GFP_HIGHUSER M_WAITOK
|
|
#define GFP_HIGHUSER_MOVABLE M_WAITOK
|
|
#define GFP_IOFS M_NOWAIT
|
|
#define GFP_NOIO M_NOWAIT
|
|
#define GFP_NOFS M_NOWAIT
|
|
#define GFP_DMA32 __GFP_DMA32
|
|
#define GFP_TEMPORARY M_NOWAIT
|
|
#define GFP_NATIVE_MASK (M_NOWAIT | M_WAITOK | M_USE_RESERVE | M_ZERO)
|
|
#define GFP_TRANSHUGE 0
|
|
#define GFP_TRANSHUGE_LIGHT 0
|
|
|
|
CTASSERT((__GFP_DMA32 & GFP_NATIVE_MASK) == 0);
|
|
CTASSERT((__GFP_BITS_MASK & GFP_NATIVE_MASK) == GFP_NATIVE_MASK);
|
|
|
|
struct page_frag_cache {
|
|
void *va;
|
|
int pagecnt_bias;
|
|
};
|
|
|
|
/*
|
|
* Page management for unmapped pages:
|
|
*/
|
|
struct page *linux_alloc_pages(gfp_t flags, unsigned int order);
|
|
void linux_free_pages(struct page *page, unsigned int order);
|
|
void *linuxkpi_page_frag_alloc(struct page_frag_cache *, size_t, gfp_t);
|
|
void linuxkpi_page_frag_free(void *);
|
|
void linuxkpi__page_frag_cache_drain(struct page *, size_t);
|
|
|
|
static inline struct page *
|
|
alloc_page(gfp_t flags)
|
|
{
|
|
|
|
return (linux_alloc_pages(flags, 0));
|
|
}
|
|
|
|
static inline struct page *
|
|
alloc_pages(gfp_t flags, unsigned int order)
|
|
{
|
|
|
|
return (linux_alloc_pages(flags, order));
|
|
}
|
|
|
|
static inline struct page *
|
|
alloc_pages_node(int node_id, gfp_t flags, unsigned int order)
|
|
{
|
|
|
|
return (linux_alloc_pages(flags, order));
|
|
}
|
|
|
|
static inline void
|
|
__free_pages(struct page *page, unsigned int order)
|
|
{
|
|
|
|
linux_free_pages(page, order);
|
|
}
|
|
|
|
static inline void
|
|
__free_page(struct page *page)
|
|
{
|
|
|
|
linux_free_pages(page, 0);
|
|
}
|
|
|
|
static inline struct page *
|
|
dev_alloc_pages(unsigned int order)
|
|
{
|
|
return (linux_alloc_pages(GFP_ATOMIC, order));
|
|
}
|
|
|
|
/*
|
|
* Page management for mapped pages:
|
|
*/
|
|
vm_offset_t linux_alloc_kmem(gfp_t flags, unsigned int order);
|
|
void linux_free_kmem(vm_offset_t, unsigned int order);
|
|
|
|
static inline vm_offset_t
|
|
get_zeroed_page(gfp_t flags)
|
|
{
|
|
|
|
return (linux_alloc_kmem(flags | __GFP_ZERO, 0));
|
|
}
|
|
|
|
static inline vm_offset_t
|
|
__get_free_page(gfp_t flags)
|
|
{
|
|
|
|
return (linux_alloc_kmem(flags, 0));
|
|
}
|
|
|
|
static inline vm_offset_t
|
|
__get_free_pages(gfp_t flags, unsigned int order)
|
|
{
|
|
|
|
return (linux_alloc_kmem(flags, order));
|
|
}
|
|
|
|
static inline void
|
|
free_pages(uintptr_t addr, unsigned int order)
|
|
{
|
|
if (addr == 0)
|
|
return;
|
|
|
|
linux_free_kmem(addr, order);
|
|
}
|
|
|
|
static inline void
|
|
free_page(uintptr_t addr)
|
|
{
|
|
if (addr == 0)
|
|
return;
|
|
|
|
linux_free_kmem(addr, 0);
|
|
}
|
|
|
|
static inline void *
|
|
page_frag_alloc(struct page_frag_cache *pfc, size_t fragsz, gfp_t gfp)
|
|
{
|
|
|
|
return (linuxkpi_page_frag_alloc(pfc, fragsz, gfp));
|
|
}
|
|
|
|
static inline void
|
|
page_frag_free(void *addr)
|
|
{
|
|
|
|
linuxkpi_page_frag_free(addr);
|
|
}
|
|
|
|
static inline void
|
|
__page_frag_cache_drain(struct page *page, size_t count)
|
|
{
|
|
|
|
linuxkpi__page_frag_cache_drain(page, count);
|
|
}
|
|
|
|
static inline bool
|
|
gfpflags_allow_blocking(const gfp_t gfp_flags)
|
|
{
|
|
return ((gfp_flags & (M_WAITOK | M_NOWAIT)) == M_WAITOK);
|
|
}
|
|
|
|
#define SetPageReserved(page) do { } while (0) /* NOP */
|
|
#define ClearPageReserved(page) do { } while (0) /* NOP */
|
|
|
|
#endif /* _LINUXKPI_LINUX_GFP_H_ */
|