Commit 8be4868c authored by Maíra Canal's avatar Maíra Canal Committed by Euler
Browse files

mm: shmem: override mTHP shmem default with a kernel parameter

mainline inclusion
from mainline-v6.12-rc1
commit 24f9cd195fbc9382ae0ed8b332e6302d1722d8e0
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IBG3J8

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=24f9cd195fbc9382ae0ed8b332e6302d1722d8e0

--------------------------------

Add the ``thp_shmem=`` kernel command line to allow specifying the default
policy of each supported shmem hugepage size.  The kernel parameter
accepts the following format:

thp_shmem=<size>[KMG],<size>[KMG]:<policy>;<size>[KMG]-<size>[KMG]:<policy>

For example,

thp_shmem=16K-64K:always;128K,512K:inherit;256K:advise;1M-2M:never;4M-8M:within_size

Some GPUs may benefit from using huge pages.  Since DRM GEM uses shmem to
allocate anonymous pageable memory, it's essential to control the huge
page allocation policy for the internal shmem mount.  This control can be
achieved through the ``transparent_hugepage_shmem=`` parameter.

Beyond just setting the allocation policy, it's crucial to have granular
control over the size of huge pages that can be allocated.  The GPU may
support only specific huge page sizes, and allocating pages larger/smaller
than those sizes would be ineffective.

Link: https://lkml.kernel.org/r/20241101165719.1074234-6-mcanal@igalia.com


Signed-off-by: default avatarMaíra Canal <mcanal@igalia.com>
Reviewed-by: default avatarBaolin Wang <baolin.wang@linux.alibaba.com>
Cc: Barry Song <baohua@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Lance Yang <ioworker0@gmail.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Conflicts:
    mm/shmem.c
    Documentation/admin-guide/kernel-parameters.rst
[Context conflicts in shmem.c due to miss commit e6584c3964f2, yet use strcpy]
[Context conflicts in shmem.c due to miss euler hulk commit 8b52f97e]
[Context conflicts in kernel-parameters.rst due to miss commit 600716592a3a]
Signed-off-by: default avatarWang Lian <dev01404@linx-info.com>
---
parent 2843b11a
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -6537,6 +6537,16 @@
			Force threading of all interrupt handlers except those
			marked explicitly IRQF_NO_THREAD.

	thp_shmem=	[KNL]
			Format: <size>[KMG],<size>[KMG]:<policy>;<size>[KMG]-<size>[KMG]:<policy>
			Control the default policy of each hugepage size for the
			internal shmem mount. <policy> is one of policies available
			for the shmem mount ("always", "inherit", "never", "within_size",
			and "advise").
			It can be used multiple times for multiple shmem THP sizes.
			See Documentation/admin-guide/mm/transhuge.rst for more
			details.

	topology=	[S390]
			Format: {off | on}
			Specify if the kernel should make use of the cpu
+17 −0
Original line number Diff line number Diff line
@@ -355,6 +355,23 @@ allocation policy for the internal shmem mount by using the kernel parameter
seven valid policies for shmem (``always``, ``within_size``, ``advise``,
``never``, ``deny``, and ``force``).

In the same manner as ``thp_anon`` controls each supported anonymous THP
size, ``thp_shmem`` controls each supported shmem THP size. ``thp_shmem``
has the same format as ``thp_anon``, but also supports the policy
``within_size``.

``thp_shmem=`` may be specified multiple times to configure all THP sizes
as required. If ``thp_shmem=`` is specified at least once, any shmem THP
sizes not explicitly configured on the command line are implicitly set to
``never``.

``transparent_hugepage_shmem`` setting only affects the global toggle. If
``thp_shmem`` is not specified, PMD_ORDER hugepage will default to
``inherit``. However, if a valid ``thp_shmem`` setting is provided by the
user, the PMD_ORDER hugepage policy will be overridden. If the policy for
PMD_ORDER is not defined within a valid ``thp_shmem``, its policy will
default to ``never``.

Hugepages in tmpfs/shmem
========================

+104 −1
Original line number Diff line number Diff line
@@ -136,6 +136,7 @@ static unsigned long huge_shmem_orders_always __read_mostly;
static unsigned long huge_shmem_orders_madvise __read_mostly;
static unsigned long huge_shmem_orders_inherit __read_mostly;
static unsigned long huge_shmem_orders_within_size __read_mostly;
static bool shmem_orders_configured __initdata;
#endif

#ifdef CONFIG_TMPFS
@@ -4973,6 +4974,7 @@ void __init shmem_init(void)
	 * Default to setting PMD-sized THP to inherit the global setting and
	 * disable all other multi-size THPs.
	 */
	if (!shmem_orders_configured)
		huge_shmem_orders_inherit = BIT(HPAGE_PMD_ORDER);
#endif

@@ -5137,6 +5139,107 @@ static int __init setup_transparent_hugepage_shmem(char *str)
}
__setup("transparent_hugepage_shmem=", setup_transparent_hugepage_shmem);

static char str_dup[PAGE_SIZE] __initdata;
static int __init setup_thp_shmem(char *str)
{
	char *token, *range, *policy, *subtoken;
	unsigned long always, inherit, madvise, within_size;
	char *start_size, *end_size;
	int start, end, nr;
	char *p;

	if (!str || strlen(str) + 1 > PAGE_SIZE)
		goto err;
	strcpy(str_dup, str);

	always = huge_shmem_orders_always;
	inherit = huge_shmem_orders_inherit;
	madvise = huge_shmem_orders_madvise;
	within_size = huge_shmem_orders_within_size;
	p = str_dup;
	while ((token = strsep(&p, ";")) != NULL) {
		range = strsep(&token, ":");
		policy = token;

		if (!policy)
			goto err;

		while ((subtoken = strsep(&range, ",")) != NULL) {
			if (strchr(subtoken, '-')) {
				start_size = strsep(&subtoken, "-");
				end_size = subtoken;

				start = get_order_from_str(start_size,
							   THP_ORDERS_ALL_FILE_DEFAULT);
				end = get_order_from_str(end_size,
							 THP_ORDERS_ALL_FILE_DEFAULT);
			} else {
				start_size = end_size = subtoken;
				start = end = get_order_from_str(subtoken,
								 THP_ORDERS_ALL_FILE_DEFAULT);
			}

			if (start == -EINVAL) {
				pr_err("invalid size %s in thp_shmem boot parameter\n",
				       start_size);
				goto err;
			}

			if (end == -EINVAL) {
				pr_err("invalid size %s in thp_shmem boot parameter\n",
				       end_size);
				goto err;
			}

			if (start < 0 || end < 0 || start > end)
				goto err;

			nr = end - start + 1;
			if (!strcmp(policy, "always")) {
				bitmap_set(&always, start, nr);
				bitmap_clear(&inherit, start, nr);
				bitmap_clear(&madvise, start, nr);
				bitmap_clear(&within_size, start, nr);
			} else if (!strcmp(policy, "advise")) {
				bitmap_set(&madvise, start, nr);
				bitmap_clear(&inherit, start, nr);
				bitmap_clear(&always, start, nr);
				bitmap_clear(&within_size, start, nr);
			} else if (!strcmp(policy, "inherit")) {
				bitmap_set(&inherit, start, nr);
				bitmap_clear(&madvise, start, nr);
				bitmap_clear(&always, start, nr);
				bitmap_clear(&within_size, start, nr);
			} else if (!strcmp(policy, "within_size")) {
				bitmap_set(&within_size, start, nr);
				bitmap_clear(&inherit, start, nr);
				bitmap_clear(&madvise, start, nr);
				bitmap_clear(&always, start, nr);
			} else if (!strcmp(policy, "never")) {
				bitmap_clear(&inherit, start, nr);
				bitmap_clear(&madvise, start, nr);
				bitmap_clear(&always, start, nr);
				bitmap_clear(&within_size, start, nr);
			} else {
				pr_err("invalid policy %s in thp_shmem boot parameter\n", policy);
				goto err;
			}
		}
	}

	huge_shmem_orders_always = always;
	huge_shmem_orders_madvise = madvise;
	huge_shmem_orders_inherit = inherit;
	huge_shmem_orders_within_size = within_size;
	shmem_orders_configured = true;
	return 1;

err:
	pr_warn("thp_shmem=%s: error parsing string, ignoring setting\n", str);
	return 0;
}
__setup("thp_shmem=", setup_thp_shmem);

#endif /* CONFIG_TRANSPARENT_HUGEPAGE */

#else /* !CONFIG_SHMEM */