* [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag
[not found] <20241008112049.2279307-1-linyunsheng@huawei.com>
@ 2024-10-08 11:20 ` Yunsheng Lin
2024-10-08 19:56 ` Shuah Khan
2024-10-08 11:20 ` [PATCH net-next v20 02/14] mm: move the page fragment allocator from page_alloc into its own file Yunsheng Lin
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Yunsheng Lin @ 2024-10-08 11:20 UTC (permalink / raw)
To: davem, kuba, pabeni
Cc: netdev, linux-kernel, Yunsheng Lin, Alexander Duyck,
Alexander Duyck, Andrew Morton, Shuah Khan, linux-mm,
linux-kselftest
The testing is done by ensuring that the fragment allocated
from a frag_frag_cache instance is pushed into a ptr_ring
instance in a kthread binded to a specified cpu, and a kthread
binded to a specified cpu will pop the fragment from the
ptr_ring and free the fragment.
CC: Alexander Duyck <alexander.duyck@gmail.com>
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
---
tools/testing/selftests/mm/Makefile | 3 +
tools/testing/selftests/mm/page_frag/Makefile | 18 ++
.../selftests/mm/page_frag/page_frag_test.c | 173 ++++++++++++++++++
tools/testing/selftests/mm/run_vmtests.sh | 8 +
tools/testing/selftests/mm/test_page_frag.sh | 171 +++++++++++++++++
5 files changed, 373 insertions(+)
create mode 100644 tools/testing/selftests/mm/page_frag/Makefile
create mode 100644 tools/testing/selftests/mm/page_frag/page_frag_test.c
create mode 100755 tools/testing/selftests/mm/test_page_frag.sh
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index 02e1204971b0..acec529baaca 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -36,6 +36,8 @@ MAKEFLAGS += --no-builtin-rules
CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
LDLIBS = -lrt -lpthread -lm
+TEST_GEN_MODS_DIR := page_frag
+
TEST_GEN_FILES = cow
TEST_GEN_FILES += compaction_test
TEST_GEN_FILES += gup_longterm
@@ -126,6 +128,7 @@ TEST_FILES += test_hmm.sh
TEST_FILES += va_high_addr_switch.sh
TEST_FILES += charge_reserved_hugetlb.sh
TEST_FILES += hugetlb_reparenting_test.sh
+TEST_FILES += test_page_frag.sh
# required by charge_reserved_hugetlb.sh
TEST_FILES += write_hugetlb_memory.sh
diff --git a/tools/testing/selftests/mm/page_frag/Makefile b/tools/testing/selftests/mm/page_frag/Makefile
new file mode 100644
index 000000000000..58dda74d50a3
--- /dev/null
+++ b/tools/testing/selftests/mm/page_frag/Makefile
@@ -0,0 +1,18 @@
+PAGE_FRAG_TEST_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
+KDIR ?= $(abspath $(PAGE_FRAG_TEST_DIR)/../../../../..)
+
+ifeq ($(V),1)
+Q =
+else
+Q = @
+endif
+
+MODULES = page_frag_test.ko
+
+obj-m += page_frag_test.o
+
+all:
+ +$(Q)make -C $(KDIR) M=$(PAGE_FRAG_TEST_DIR) modules
+
+clean:
+ +$(Q)make -C $(KDIR) M=$(PAGE_FRAG_TEST_DIR) clean
diff --git a/tools/testing/selftests/mm/page_frag/page_frag_test.c b/tools/testing/selftests/mm/page_frag/page_frag_test.c
new file mode 100644
index 000000000000..eeb2b6bc681a
--- /dev/null
+++ b/tools/testing/selftests/mm/page_frag/page_frag_test.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Test module for page_frag cache
+ *
+ * Copyright (C) 2024 Yunsheng Lin <linyunsheng@huawei.com>
+ */
+
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/cpumask.h>
+#include <linux/completion.h>
+#include <linux/ptr_ring.h>
+#include <linux/kthread.h>
+
+static struct ptr_ring ptr_ring;
+static int nr_objs = 512;
+static atomic_t nthreads;
+static struct completion wait;
+static struct page_frag_cache test_nc;
+static int test_popped;
+static int test_pushed;
+
+static int nr_test = 2000000;
+module_param(nr_test, int, 0);
+MODULE_PARM_DESC(nr_test, "number of iterations to test");
+
+static bool test_align;
+module_param(test_align, bool, 0);
+MODULE_PARM_DESC(test_align, "use align API for testing");
+
+static int test_alloc_len = 2048;
+module_param(test_alloc_len, int, 0);
+MODULE_PARM_DESC(test_alloc_len, "alloc len for testing");
+
+static int test_push_cpu;
+module_param(test_push_cpu, int, 0);
+MODULE_PARM_DESC(test_push_cpu, "test cpu for pushing fragment");
+
+static int test_pop_cpu;
+module_param(test_pop_cpu, int, 0);
+MODULE_PARM_DESC(test_pop_cpu, "test cpu for popping fragment");
+
+static int page_frag_pop_thread(void *arg)
+{
+ struct ptr_ring *ring = arg;
+
+ pr_info("page_frag pop test thread begins on cpu %d\n",
+ smp_processor_id());
+
+ while (test_popped < nr_test) {
+ void *obj = __ptr_ring_consume(ring);
+
+ if (obj) {
+ test_popped++;
+ page_frag_free(obj);
+ } else {
+ cond_resched();
+ }
+ }
+
+ if (atomic_dec_and_test(&nthreads))
+ complete(&wait);
+
+ pr_info("page_frag pop test thread exits on cpu %d\n",
+ smp_processor_id());
+
+ return 0;
+}
+
+static int page_frag_push_thread(void *arg)
+{
+ struct ptr_ring *ring = arg;
+
+ pr_info("page_frag push test thread begins on cpu %d\n",
+ smp_processor_id());
+
+ while (test_pushed < nr_test) {
+ void *va;
+ int ret;
+
+ if (test_align) {
+ va = page_frag_alloc_align(&test_nc, test_alloc_len,
+ GFP_KERNEL, SMP_CACHE_BYTES);
+
+ WARN_ONCE((unsigned long)va & (SMP_CACHE_BYTES - 1),
+ "unaligned va returned\n");
+ } else {
+ va = page_frag_alloc(&test_nc, test_alloc_len, GFP_KERNEL);
+ }
+
+ if (!va)
+ continue;
+
+ ret = __ptr_ring_produce(ring, va);
+ if (ret) {
+ page_frag_free(va);
+ cond_resched();
+ } else {
+ test_pushed++;
+ }
+ }
+
+ pr_info("page_frag push test thread exits on cpu %d\n",
+ smp_processor_id());
+
+ if (atomic_dec_and_test(&nthreads))
+ complete(&wait);
+
+ return 0;
+}
+
+static int __init page_frag_test_init(void)
+{
+ struct task_struct *tsk_push, *tsk_pop;
+ ktime_t start;
+ u64 duration;
+ int ret;
+
+ test_nc.va = NULL;
+ atomic_set(&nthreads, 2);
+ init_completion(&wait);
+
+ if (test_alloc_len > PAGE_SIZE || test_alloc_len <= 0 ||
+ !cpu_active(test_push_cpu) || !cpu_active(test_pop_cpu))
+ return -EINVAL;
+
+ ret = ptr_ring_init(&ptr_ring, nr_objs, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ tsk_push = kthread_create_on_cpu(page_frag_push_thread, &ptr_ring,
+ test_push_cpu, "page_frag_push");
+ if (IS_ERR(tsk_push))
+ return PTR_ERR(tsk_push);
+
+ tsk_pop = kthread_create_on_cpu(page_frag_pop_thread, &ptr_ring,
+ test_pop_cpu, "page_frag_pop");
+ if (IS_ERR(tsk_pop)) {
+ kthread_stop(tsk_push);
+ return PTR_ERR(tsk_pop);
+ }
+
+ start = ktime_get();
+ wake_up_process(tsk_push);
+ wake_up_process(tsk_pop);
+
+ pr_info("waiting for test to complete\n");
+
+ while (!wait_for_completion_timeout(&wait, msecs_to_jiffies(10000)))
+ pr_info("page_frag_test progress: pushed = %d, popped = %d\n",
+ test_pushed, test_popped);
+
+ duration = (u64)ktime_us_delta(ktime_get(), start);
+ pr_info("%d of iterations for %s testing took: %lluus\n", nr_test,
+ test_align ? "aligned" : "non-aligned", duration);
+
+ ptr_ring_cleanup(&ptr_ring, NULL);
+ page_frag_cache_drain(&test_nc);
+
+ return -EAGAIN;
+}
+
+static void __exit page_frag_test_exit(void)
+{
+}
+
+module_init(page_frag_test_init);
+module_exit(page_frag_test_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yunsheng Lin <linyunsheng@huawei.com>");
+MODULE_DESCRIPTION("Test module for page_frag");
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index c5797ad1d37b..2c5394584af4 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -75,6 +75,8 @@ separated by spaces:
read-only VMAs
- mdwe
test prctl(PR_SET_MDWE, ...)
+- page_frag
+ test handling of page fragment allocation and freeing
example: ./run_vmtests.sh -t "hmm mmap ksm"
EOF
@@ -456,6 +458,12 @@ CATEGORY="mkdirty" run_test ./mkdirty
CATEGORY="mdwe" run_test ./mdwe_test
+CATEGORY="page_frag" run_test ./test_page_frag.sh smoke
+
+CATEGORY="page_frag" run_test ./test_page_frag.sh aligned
+
+CATEGORY="page_frag" run_test ./test_page_frag.sh nonaligned
+
echo "SUMMARY: PASS=${count_pass} SKIP=${count_skip} FAIL=${count_fail}" | tap_prefix
echo "1..${count_total}" | tap_output
diff --git a/tools/testing/selftests/mm/test_page_frag.sh b/tools/testing/selftests/mm/test_page_frag.sh
new file mode 100755
index 000000000000..d750d910c899
--- /dev/null
+++ b/tools/testing/selftests/mm/test_page_frag.sh
@@ -0,0 +1,171 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2024 Yunsheng Lin <linyunsheng@huawei.com>
+# Copyright (C) 2018 Uladzislau Rezki (Sony) <urezki@gmail.com>
+#
+# This is a test script for the kernel test driver to test the
+# correctness and performance of page_frag's implementation.
+# Therefore it is just a kernel module loader. You can specify
+# and pass different parameters in order to:
+# a) analyse performance of page fragment allocations;
+# b) stressing and stability check of page_frag subsystem.
+
+DRIVER="./page_frag/page_frag_test.ko"
+CPU_LIST=$(grep -m 2 processor /proc/cpuinfo | cut -d ' ' -f 2)
+TEST_CPU_0=$(echo $CPU_LIST | awk '{print $1}')
+
+if [ $(echo $CPU_LIST | wc -w) -gt 1 ]; then
+ TEST_CPU_1=$(echo $CPU_LIST | awk '{print $2}')
+ NR_TEST=100000000
+else
+ TEST_CPU_1=$TEST_CPU_0
+ NR_TEST=1000000
+fi
+
+# 1 if fails
+exitcode=1
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+#
+# Static templates for testing of page_frag APIs.
+# Also it is possible to pass any supported parameters manually.
+#
+SMOKE_PARAM="test_push_cpu=$TEST_CPU_0 test_pop_cpu=$TEST_CPU_1"
+NONALIGNED_PARAM="$SMOKE_PARAM test_alloc_len=75 nr_test=$NR_TEST"
+ALIGNED_PARAM="$NONALIGNED_PARAM test_align=1"
+
+check_test_requirements()
+{
+ uid=$(id -u)
+ if [ $uid -ne 0 ]; then
+ echo "$0: Must be run as root"
+ exit $ksft_skip
+ fi
+
+ if ! which insmod > /dev/null 2>&1; then
+ echo "$0: You need insmod installed"
+ exit $ksft_skip
+ fi
+
+ if [ ! -f $DRIVER ]; then
+ echo "$0: You need to compile page_frag_test module"
+ exit $ksft_skip
+ fi
+}
+
+run_nonaligned_check()
+{
+ echo "Run performance tests to evaluate how fast nonaligned alloc API is."
+
+ insmod $DRIVER $NONALIGNED_PARAM > /dev/null 2>&1
+ echo "Done."
+ echo "Check the kernel ring buffer to see the summary."
+}
+
+run_aligned_check()
+{
+ echo "Run performance tests to evaluate how fast aligned alloc API is."
+
+ insmod $DRIVER $ALIGNED_PARAM > /dev/null 2>&1
+ echo "Done."
+ echo "Check the kernel ring buffer to see the summary."
+}
+
+run_smoke_check()
+{
+ echo "Run smoke test."
+
+ insmod $DRIVER $SMOKE_PARAM > /dev/null 2>&1
+ echo "Done."
+ echo "Check the kernel ring buffer to see the summary."
+}
+
+usage()
+{
+ echo -n "Usage: $0 [ aligned ] | [ nonaligned ] | | [ smoke ] | "
+ echo "manual parameters"
+ echo
+ echo "Valid tests and parameters:"
+ echo
+ modinfo $DRIVER
+ echo
+ echo "Example usage:"
+ echo
+ echo "# Shows help message"
+ echo "$0"
+ echo
+ echo "# Smoke testing"
+ echo "$0 smoke"
+ echo
+ echo "# Performance testing for nonaligned alloc API"
+ echo "$0 nonaligned"
+ echo
+ echo "# Performance testing for aligned alloc API"
+ echo "$0 aligned"
+ echo
+ exit 0
+}
+
+function validate_passed_args()
+{
+ VALID_ARGS=`modinfo $DRIVER | awk '/parm:/ {print $2}' | sed 's/:.*//'`
+
+ #
+ # Something has been passed, check it.
+ #
+ for passed_arg in $@; do
+ key=${passed_arg//=*/}
+ valid=0
+
+ for valid_arg in $VALID_ARGS; do
+ if [[ $key = $valid_arg ]]; then
+ valid=1
+ break
+ fi
+ done
+
+ if [[ $valid -ne 1 ]]; then
+ echo "Error: key is not correct: ${key}"
+ exit $exitcode
+ fi
+ done
+}
+
+function run_manual_check()
+{
+ #
+ # Validate passed parameters. If there is wrong one,
+ # the script exists and does not execute further.
+ #
+ validate_passed_args $@
+
+ echo "Run the test with following parameters: $@"
+ insmod $DRIVER $@ > /dev/null 2>&1
+ echo "Done."
+ echo "Check the kernel ring buffer to see the summary."
+}
+
+function run_test()
+{
+ if [ $# -eq 0 ]; then
+ usage
+ else
+ if [[ "$1" = "smoke" ]]; then
+ run_smoke_check
+ elif [[ "$1" = "nonaligned" ]]; then
+ run_nonaligned_check
+ elif [[ "$1" = "aligned" ]]; then
+ run_aligned_check
+ else
+ run_manual_check $@
+ fi
+ fi
+}
+
+check_test_requirements
+run_test $@
+
+exit 0
--
2.33.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v20 02/14] mm: move the page fragment allocator from page_alloc into its own file
[not found] <20241008112049.2279307-1-linyunsheng@huawei.com>
2024-10-08 11:20 ` [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag Yunsheng Lin
@ 2024-10-08 11:20 ` Yunsheng Lin
2024-10-08 11:20 ` [PATCH net-next v20 04/14] mm: page_frag: avoid caller accessing 'page_frag_cache' directly Yunsheng Lin
2024-10-08 11:20 ` [PATCH net-next v20 11/14] mm: page_frag: add testing for the newly added prepare API Yunsheng Lin
3 siblings, 0 replies; 7+ messages in thread
From: Yunsheng Lin @ 2024-10-08 11:20 UTC (permalink / raw)
To: davem, kuba, pabeni
Cc: netdev, linux-kernel, Yunsheng Lin, David Howells,
Alexander Duyck, Andrew Morton, Alexander Duyck, Eric Dumazet,
Shuah Khan, linux-mm, linux-kselftest
Inspired by [1], move the page fragment allocator from page_alloc
into its own c file and header file, as we are about to make more
change for it to replace another page_frag implementation in
sock.c
As this patchset is going to replace 'struct page_frag' with
'struct page_frag_cache' in sched.h, including page_frag_cache.h
in sched.h has a compiler error caused by interdependence between
mm_types.h and mm.h for asm-offsets.c, see [2]. So avoid the compiler
error by moving 'struct page_frag_cache' to mm_types_task.h as
suggested by Alexander, see [3].
1. https://lore.kernel.org/all/20230411160902.4134381-3-dhowells@redhat.com/
2. https://lore.kernel.org/all/15623dac-9358-4597-b3ee-3694a5956920@gmail.com/
3. https://lore.kernel.org/all/CAKgT0UdH1yD=LSCXFJ=YM_aiA4OomD-2wXykO42bizaWMt_HOA@mail.gmail.com/
CC: David Howells <dhowells@redhat.com>
CC: Alexander Duyck <alexander.duyck@gmail.com>
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Acked-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
---
include/linux/gfp.h | 22 ---
include/linux/mm_types.h | 18 ---
include/linux/mm_types_task.h | 18 +++
include/linux/page_frag_cache.h | 31 ++++
include/linux/skbuff.h | 1 +
mm/Makefile | 1 +
mm/page_alloc.c | 136 ----------------
mm/page_frag_cache.c | 145 ++++++++++++++++++
.../selftests/mm/page_frag/page_frag_test.c | 2 +-
9 files changed, 197 insertions(+), 177 deletions(-)
create mode 100644 include/linux/page_frag_cache.h
create mode 100644 mm/page_frag_cache.c
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index a951de920e20..a0a6d25f883f 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -371,28 +371,6 @@ __meminit void *alloc_pages_exact_nid_noprof(int nid, size_t size, gfp_t gfp_mas
extern void __free_pages(struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);
-struct page_frag_cache;
-void page_frag_cache_drain(struct page_frag_cache *nc);
-extern void __page_frag_cache_drain(struct page *page, unsigned int count);
-void *__page_frag_alloc_align(struct page_frag_cache *nc, unsigned int fragsz,
- gfp_t gfp_mask, unsigned int align_mask);
-
-static inline void *page_frag_alloc_align(struct page_frag_cache *nc,
- unsigned int fragsz, gfp_t gfp_mask,
- unsigned int align)
-{
- WARN_ON_ONCE(!is_power_of_2(align));
- return __page_frag_alloc_align(nc, fragsz, gfp_mask, -align);
-}
-
-static inline void *page_frag_alloc(struct page_frag_cache *nc,
- unsigned int fragsz, gfp_t gfp_mask)
-{
- return __page_frag_alloc_align(nc, fragsz, gfp_mask, ~0u);
-}
-
-extern void page_frag_free(void *addr);
-
#define __free_page(page) __free_pages((page), 0)
#define free_page(addr) free_pages((addr), 0)
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 6e3bdf8e38bc..92314ef2d978 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -521,9 +521,6 @@ static_assert(sizeof(struct ptdesc) <= sizeof(struct page));
*/
#define STRUCT_PAGE_MAX_SHIFT (order_base_2(sizeof(struct page)))
-#define PAGE_FRAG_CACHE_MAX_SIZE __ALIGN_MASK(32768, ~PAGE_MASK)
-#define PAGE_FRAG_CACHE_MAX_ORDER get_order(PAGE_FRAG_CACHE_MAX_SIZE)
-
/*
* page_private can be used on tail pages. However, PagePrivate is only
* checked by the VM on the head page. So page_private on the tail pages
@@ -542,21 +539,6 @@ static inline void *folio_get_private(struct folio *folio)
return folio->private;
}
-struct page_frag_cache {
- void * va;
-#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
- __u16 offset;
- __u16 size;
-#else
- __u32 offset;
-#endif
- /* we maintain a pagecount bias, so that we dont dirty cache line
- * containing page->_refcount every time we allocate a fragment.
- */
- unsigned int pagecnt_bias;
- bool pfmemalloc;
-};
-
typedef unsigned long vm_flags_t;
/*
diff --git a/include/linux/mm_types_task.h b/include/linux/mm_types_task.h
index bff5706b76e1..0ac6daebdd5c 100644
--- a/include/linux/mm_types_task.h
+++ b/include/linux/mm_types_task.h
@@ -8,6 +8,7 @@
* (These are defined separately to decouple sched.h from mm_types.h as much as possible.)
*/
+#include <linux/align.h>
#include <linux/types.h>
#include <asm/page.h>
@@ -43,6 +44,23 @@ struct page_frag {
#endif
};
+#define PAGE_FRAG_CACHE_MAX_SIZE __ALIGN_MASK(32768, ~PAGE_MASK)
+#define PAGE_FRAG_CACHE_MAX_ORDER get_order(PAGE_FRAG_CACHE_MAX_SIZE)
+struct page_frag_cache {
+ void *va;
+#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
+ __u16 offset;
+ __u16 size;
+#else
+ __u32 offset;
+#endif
+ /* we maintain a pagecount bias, so that we dont dirty cache line
+ * containing page->_refcount every time we allocate a fragment.
+ */
+ unsigned int pagecnt_bias;
+ bool pfmemalloc;
+};
+
/* Track pages that require TLB flushes */
struct tlbflush_unmap_batch {
#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
diff --git a/include/linux/page_frag_cache.h b/include/linux/page_frag_cache.h
new file mode 100644
index 000000000000..67ac8626ed9b
--- /dev/null
+++ b/include/linux/page_frag_cache.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_PAGE_FRAG_CACHE_H
+#define _LINUX_PAGE_FRAG_CACHE_H
+
+#include <linux/log2.h>
+#include <linux/mm_types_task.h>
+#include <linux/types.h>
+
+void page_frag_cache_drain(struct page_frag_cache *nc);
+void __page_frag_cache_drain(struct page *page, unsigned int count);
+void *__page_frag_alloc_align(struct page_frag_cache *nc, unsigned int fragsz,
+ gfp_t gfp_mask, unsigned int align_mask);
+
+static inline void *page_frag_alloc_align(struct page_frag_cache *nc,
+ unsigned int fragsz, gfp_t gfp_mask,
+ unsigned int align)
+{
+ WARN_ON_ONCE(!is_power_of_2(align));
+ return __page_frag_alloc_align(nc, fragsz, gfp_mask, -align);
+}
+
+static inline void *page_frag_alloc(struct page_frag_cache *nc,
+ unsigned int fragsz, gfp_t gfp_mask)
+{
+ return __page_frag_alloc_align(nc, fragsz, gfp_mask, ~0u);
+}
+
+void page_frag_free(void *addr);
+
+#endif
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 39f1d16f3628..560e2b49f98b 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -31,6 +31,7 @@
#include <linux/in6.h>
#include <linux/if_packet.h>
#include <linux/llist.h>
+#include <linux/page_frag_cache.h>
#include <net/flow.h>
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <linux/netfilter/nf_conntrack_common.h>
diff --git a/mm/Makefile b/mm/Makefile
index d5639b036166..dba52bb0da8a 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -65,6 +65,7 @@ page-alloc-$(CONFIG_SHUFFLE_PAGE_ALLOCATOR) += shuffle.o
memory-hotplug-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o
obj-y += page-alloc.o
+obj-y += page_frag_cache.o
obj-y += init-mm.o
obj-y += memblock.o
obj-y += $(memory-hotplug-y)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8afab64814dc..6ca2abce857b 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4836,142 +4836,6 @@ void free_pages(unsigned long addr, unsigned int order)
EXPORT_SYMBOL(free_pages);
-/*
- * Page Fragment:
- * An arbitrary-length arbitrary-offset area of memory which resides
- * within a 0 or higher order page. Multiple fragments within that page
- * are individually refcounted, in the page's reference counter.
- *
- * The page_frag functions below provide a simple allocation framework for
- * page fragments. This is used by the network stack and network device
- * drivers to provide a backing region of memory for use as either an
- * sk_buff->head, or to be used in the "frags" portion of skb_shared_info.
- */
-static struct page *__page_frag_cache_refill(struct page_frag_cache *nc,
- gfp_t gfp_mask)
-{
- struct page *page = NULL;
- gfp_t gfp = gfp_mask;
-
-#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
- gfp_mask = (gfp_mask & ~__GFP_DIRECT_RECLAIM) | __GFP_COMP |
- __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC;
- page = alloc_pages_node(NUMA_NO_NODE, gfp_mask,
- PAGE_FRAG_CACHE_MAX_ORDER);
- nc->size = page ? PAGE_FRAG_CACHE_MAX_SIZE : PAGE_SIZE;
-#endif
- if (unlikely(!page))
- page = alloc_pages_node(NUMA_NO_NODE, gfp, 0);
-
- nc->va = page ? page_address(page) : NULL;
-
- return page;
-}
-
-void page_frag_cache_drain(struct page_frag_cache *nc)
-{
- if (!nc->va)
- return;
-
- __page_frag_cache_drain(virt_to_head_page(nc->va), nc->pagecnt_bias);
- nc->va = NULL;
-}
-EXPORT_SYMBOL(page_frag_cache_drain);
-
-void __page_frag_cache_drain(struct page *page, unsigned int count)
-{
- VM_BUG_ON_PAGE(page_ref_count(page) == 0, page);
-
- if (page_ref_sub_and_test(page, count))
- free_unref_page(page, compound_order(page));
-}
-EXPORT_SYMBOL(__page_frag_cache_drain);
-
-void *__page_frag_alloc_align(struct page_frag_cache *nc,
- unsigned int fragsz, gfp_t gfp_mask,
- unsigned int align_mask)
-{
- unsigned int size = PAGE_SIZE;
- struct page *page;
- int offset;
-
- if (unlikely(!nc->va)) {
-refill:
- page = __page_frag_cache_refill(nc, gfp_mask);
- if (!page)
- return NULL;
-
-#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
- /* if size can vary use size else just use PAGE_SIZE */
- size = nc->size;
-#endif
- /* Even if we own the page, we do not use atomic_set().
- * This would break get_page_unless_zero() users.
- */
- page_ref_add(page, PAGE_FRAG_CACHE_MAX_SIZE);
-
- /* reset page count bias and offset to start of new frag */
- nc->pfmemalloc = page_is_pfmemalloc(page);
- nc->pagecnt_bias = PAGE_FRAG_CACHE_MAX_SIZE + 1;
- nc->offset = size;
- }
-
- offset = nc->offset - fragsz;
- if (unlikely(offset < 0)) {
- page = virt_to_page(nc->va);
-
- if (!page_ref_sub_and_test(page, nc->pagecnt_bias))
- goto refill;
-
- if (unlikely(nc->pfmemalloc)) {
- free_unref_page(page, compound_order(page));
- goto refill;
- }
-
-#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
- /* if size can vary use size else just use PAGE_SIZE */
- size = nc->size;
-#endif
- /* OK, page count is 0, we can safely set it */
- set_page_count(page, PAGE_FRAG_CACHE_MAX_SIZE + 1);
-
- /* reset page count bias and offset to start of new frag */
- nc->pagecnt_bias = PAGE_FRAG_CACHE_MAX_SIZE + 1;
- offset = size - fragsz;
- if (unlikely(offset < 0)) {
- /*
- * The caller is trying to allocate a fragment
- * with fragsz > PAGE_SIZE but the cache isn't big
- * enough to satisfy the request, this may
- * happen in low memory conditions.
- * We don't release the cache page because
- * it could make memory pressure worse
- * so we simply return NULL here.
- */
- return NULL;
- }
- }
-
- nc->pagecnt_bias--;
- offset &= align_mask;
- nc->offset = offset;
-
- return nc->va + offset;
-}
-EXPORT_SYMBOL(__page_frag_alloc_align);
-
-/*
- * Frees a page fragment allocated out of either a compound or order 0 page.
- */
-void page_frag_free(void *addr)
-{
- struct page *page = virt_to_head_page(addr);
-
- if (unlikely(put_page_testzero(page)))
- free_unref_page(page, compound_order(page));
-}
-EXPORT_SYMBOL(page_frag_free);
-
static void *make_alloc_exact(unsigned long addr, unsigned int order,
size_t size)
{
diff --git a/mm/page_frag_cache.c b/mm/page_frag_cache.c
new file mode 100644
index 000000000000..609a485cd02a
--- /dev/null
+++ b/mm/page_frag_cache.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Page fragment allocator
+ *
+ * Page Fragment:
+ * An arbitrary-length arbitrary-offset area of memory which resides within a
+ * 0 or higher order page. Multiple fragments within that page are
+ * individually refcounted, in the page's reference counter.
+ *
+ * The page_frag functions provide a simple allocation framework for page
+ * fragments. This is used by the network stack and network device drivers to
+ * provide a backing region of memory for use as either an sk_buff->head, or to
+ * be used in the "frags" portion of skb_shared_info.
+ */
+
+#include <linux/export.h>
+#include <linux/gfp_types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/page_frag_cache.h>
+#include "internal.h"
+
+static struct page *__page_frag_cache_refill(struct page_frag_cache *nc,
+ gfp_t gfp_mask)
+{
+ struct page *page = NULL;
+ gfp_t gfp = gfp_mask;
+
+#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
+ gfp_mask = (gfp_mask & ~__GFP_DIRECT_RECLAIM) | __GFP_COMP |
+ __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC;
+ page = alloc_pages_node(NUMA_NO_NODE, gfp_mask,
+ PAGE_FRAG_CACHE_MAX_ORDER);
+ nc->size = page ? PAGE_FRAG_CACHE_MAX_SIZE : PAGE_SIZE;
+#endif
+ if (unlikely(!page))
+ page = alloc_pages_node(NUMA_NO_NODE, gfp, 0);
+
+ nc->va = page ? page_address(page) : NULL;
+
+ return page;
+}
+
+void page_frag_cache_drain(struct page_frag_cache *nc)
+{
+ if (!nc->va)
+ return;
+
+ __page_frag_cache_drain(virt_to_head_page(nc->va), nc->pagecnt_bias);
+ nc->va = NULL;
+}
+EXPORT_SYMBOL(page_frag_cache_drain);
+
+void __page_frag_cache_drain(struct page *page, unsigned int count)
+{
+ VM_BUG_ON_PAGE(page_ref_count(page) == 0, page);
+
+ if (page_ref_sub_and_test(page, count))
+ free_unref_page(page, compound_order(page));
+}
+EXPORT_SYMBOL(__page_frag_cache_drain);
+
+void *__page_frag_alloc_align(struct page_frag_cache *nc,
+ unsigned int fragsz, gfp_t gfp_mask,
+ unsigned int align_mask)
+{
+ unsigned int size = PAGE_SIZE;
+ struct page *page;
+ int offset;
+
+ if (unlikely(!nc->va)) {
+refill:
+ page = __page_frag_cache_refill(nc, gfp_mask);
+ if (!page)
+ return NULL;
+
+#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
+ /* if size can vary use size else just use PAGE_SIZE */
+ size = nc->size;
+#endif
+ /* Even if we own the page, we do not use atomic_set().
+ * This would break get_page_unless_zero() users.
+ */
+ page_ref_add(page, PAGE_FRAG_CACHE_MAX_SIZE);
+
+ /* reset page count bias and offset to start of new frag */
+ nc->pfmemalloc = page_is_pfmemalloc(page);
+ nc->pagecnt_bias = PAGE_FRAG_CACHE_MAX_SIZE + 1;
+ nc->offset = size;
+ }
+
+ offset = nc->offset - fragsz;
+ if (unlikely(offset < 0)) {
+ page = virt_to_page(nc->va);
+
+ if (!page_ref_sub_and_test(page, nc->pagecnt_bias))
+ goto refill;
+
+ if (unlikely(nc->pfmemalloc)) {
+ free_unref_page(page, compound_order(page));
+ goto refill;
+ }
+
+#if (PAGE_SIZE < PAGE_FRAG_CACHE_MAX_SIZE)
+ /* if size can vary use size else just use PAGE_SIZE */
+ size = nc->size;
+#endif
+ /* OK, page count is 0, we can safely set it */
+ set_page_count(page, PAGE_FRAG_CACHE_MAX_SIZE + 1);
+
+ /* reset page count bias and offset to start of new frag */
+ nc->pagecnt_bias = PAGE_FRAG_CACHE_MAX_SIZE + 1;
+ offset = size - fragsz;
+ if (unlikely(offset < 0)) {
+ /*
+ * The caller is trying to allocate a fragment
+ * with fragsz > PAGE_SIZE but the cache isn't big
+ * enough to satisfy the request, this may
+ * happen in low memory conditions.
+ * We don't release the cache page because
+ * it could make memory pressure worse
+ * so we simply return NULL here.
+ */
+ return NULL;
+ }
+ }
+
+ nc->pagecnt_bias--;
+ offset &= align_mask;
+ nc->offset = offset;
+
+ return nc->va + offset;
+}
+EXPORT_SYMBOL(__page_frag_alloc_align);
+
+/*
+ * Frees a page fragment allocated out of either a compound or order 0 page.
+ */
+void page_frag_free(void *addr)
+{
+ struct page *page = virt_to_head_page(addr);
+
+ if (unlikely(put_page_testzero(page)))
+ free_unref_page(page, compound_order(page));
+}
+EXPORT_SYMBOL(page_frag_free);
diff --git a/tools/testing/selftests/mm/page_frag/page_frag_test.c b/tools/testing/selftests/mm/page_frag/page_frag_test.c
index eeb2b6bc681a..fdf204550c9a 100644
--- a/tools/testing/selftests/mm/page_frag/page_frag_test.c
+++ b/tools/testing/selftests/mm/page_frag/page_frag_test.c
@@ -6,12 +6,12 @@
* Copyright (C) 2024 Yunsheng Lin <linyunsheng@huawei.com>
*/
-#include <linux/mm.h>
#include <linux/module.h>
#include <linux/cpumask.h>
#include <linux/completion.h>
#include <linux/ptr_ring.h>
#include <linux/kthread.h>
+#include <linux/page_frag_cache.h>
static struct ptr_ring ptr_ring;
static int nr_objs = 512;
--
2.33.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v20 04/14] mm: page_frag: avoid caller accessing 'page_frag_cache' directly
[not found] <20241008112049.2279307-1-linyunsheng@huawei.com>
2024-10-08 11:20 ` [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag Yunsheng Lin
2024-10-08 11:20 ` [PATCH net-next v20 02/14] mm: move the page fragment allocator from page_alloc into its own file Yunsheng Lin
@ 2024-10-08 11:20 ` Yunsheng Lin
2024-10-08 11:20 ` [PATCH net-next v20 11/14] mm: page_frag: add testing for the newly added prepare API Yunsheng Lin
3 siblings, 0 replies; 7+ messages in thread
From: Yunsheng Lin @ 2024-10-08 11:20 UTC (permalink / raw)
To: davem, kuba, pabeni
Cc: netdev, linux-kernel, Yunsheng Lin, Alexander Duyck,
Alexander Duyck, Chuck Lever, Michael S. Tsirkin, Jason Wang,
Eugenio Pérez, Andrew Morton, Eric Dumazet, David Howells,
Marc Dionne, Trond Myklebust, Anna Schumaker, Jeff Layton,
Neil Brown, Olga Kornievskaia, Dai Ngo, Tom Talpey, Shuah Khan,
kvm, virtualization, linux-mm, linux-afs, linux-nfs,
linux-kselftest
Use appropriate frag_page API instead of caller accessing
'page_frag_cache' directly.
CC: Alexander Duyck <alexander.duyck@gmail.com>
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
Acked-by: Chuck Lever <chuck.lever@oracle.com>
---
drivers/vhost/net.c | 2 +-
include/linux/page_frag_cache.h | 10 ++++++++++
net/core/skbuff.c | 6 +++---
net/rxrpc/conn_object.c | 4 +---
net/rxrpc/local_object.c | 4 +---
net/sunrpc/svcsock.c | 6 ++----
tools/testing/selftests/mm/page_frag/page_frag_test.c | 2 +-
7 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index f16279351db5..9ad37c012189 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -1325,7 +1325,7 @@ static int vhost_net_open(struct inode *inode, struct file *f)
vqs[VHOST_NET_VQ_RX]);
f->private_data = n;
- n->pf_cache.va = NULL;
+ page_frag_cache_init(&n->pf_cache);
return 0;
}
diff --git a/include/linux/page_frag_cache.h b/include/linux/page_frag_cache.h
index 67ac8626ed9b..0a52f7a179c8 100644
--- a/include/linux/page_frag_cache.h
+++ b/include/linux/page_frag_cache.h
@@ -7,6 +7,16 @@
#include <linux/mm_types_task.h>
#include <linux/types.h>
+static inline void page_frag_cache_init(struct page_frag_cache *nc)
+{
+ nc->va = NULL;
+}
+
+static inline bool page_frag_cache_is_pfmemalloc(struct page_frag_cache *nc)
+{
+ return !!nc->pfmemalloc;
+}
+
void page_frag_cache_drain(struct page_frag_cache *nc);
void __page_frag_cache_drain(struct page *page, unsigned int count);
void *__page_frag_alloc_align(struct page_frag_cache *nc, unsigned int fragsz,
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 74149dc4ee31..ca01880c7ad0 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -753,14 +753,14 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len,
if (in_hardirq() || irqs_disabled()) {
nc = this_cpu_ptr(&netdev_alloc_cache);
data = page_frag_alloc(nc, len, gfp_mask);
- pfmemalloc = nc->pfmemalloc;
+ pfmemalloc = page_frag_cache_is_pfmemalloc(nc);
} else {
local_bh_disable();
local_lock_nested_bh(&napi_alloc_cache.bh_lock);
nc = this_cpu_ptr(&napi_alloc_cache.page);
data = page_frag_alloc(nc, len, gfp_mask);
- pfmemalloc = nc->pfmemalloc;
+ pfmemalloc = page_frag_cache_is_pfmemalloc(nc);
local_unlock_nested_bh(&napi_alloc_cache.bh_lock);
local_bh_enable();
@@ -850,7 +850,7 @@ struct sk_buff *napi_alloc_skb(struct napi_struct *napi, unsigned int len)
len = SKB_HEAD_ALIGN(len);
data = page_frag_alloc(&nc->page, len, gfp_mask);
- pfmemalloc = nc->page.pfmemalloc;
+ pfmemalloc = page_frag_cache_is_pfmemalloc(&nc->page);
}
local_unlock_nested_bh(&napi_alloc_cache.bh_lock);
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 1539d315afe7..694c4df7a1a3 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -337,9 +337,7 @@ static void rxrpc_clean_up_connection(struct work_struct *work)
*/
rxrpc_purge_queue(&conn->rx_queue);
- if (conn->tx_data_alloc.va)
- __page_frag_cache_drain(virt_to_page(conn->tx_data_alloc.va),
- conn->tx_data_alloc.pagecnt_bias);
+ page_frag_cache_drain(&conn->tx_data_alloc);
call_rcu(&conn->rcu, rxrpc_rcu_free_connection);
}
diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index 504453c688d7..a8cffe47cf01 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c
@@ -452,9 +452,7 @@ void rxrpc_destroy_local(struct rxrpc_local *local)
#endif
rxrpc_purge_queue(&local->rx_queue);
rxrpc_purge_client_connections(local);
- if (local->tx_alloc.va)
- __page_frag_cache_drain(virt_to_page(local->tx_alloc.va),
- local->tx_alloc.pagecnt_bias);
+ page_frag_cache_drain(&local->tx_alloc);
}
/*
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 825ec5357691..b785425c3315 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -1608,7 +1608,6 @@ static void svc_tcp_sock_detach(struct svc_xprt *xprt)
static void svc_sock_free(struct svc_xprt *xprt)
{
struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt);
- struct page_frag_cache *pfc = &svsk->sk_frag_cache;
struct socket *sock = svsk->sk_sock;
trace_svcsock_free(svsk, sock);
@@ -1618,8 +1617,7 @@ static void svc_sock_free(struct svc_xprt *xprt)
sockfd_put(sock);
else
sock_release(sock);
- if (pfc->va)
- __page_frag_cache_drain(virt_to_head_page(pfc->va),
- pfc->pagecnt_bias);
+
+ page_frag_cache_drain(&svsk->sk_frag_cache);
kfree(svsk);
}
diff --git a/tools/testing/selftests/mm/page_frag/page_frag_test.c b/tools/testing/selftests/mm/page_frag/page_frag_test.c
index fdf204550c9a..36543a129e40 100644
--- a/tools/testing/selftests/mm/page_frag/page_frag_test.c
+++ b/tools/testing/selftests/mm/page_frag/page_frag_test.c
@@ -117,7 +117,7 @@ static int __init page_frag_test_init(void)
u64 duration;
int ret;
- test_nc.va = NULL;
+ page_frag_cache_init(&test_nc);
atomic_set(&nthreads, 2);
init_completion(&wait);
--
2.33.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v20 11/14] mm: page_frag: add testing for the newly added prepare API
[not found] <20241008112049.2279307-1-linyunsheng@huawei.com>
` (2 preceding siblings ...)
2024-10-08 11:20 ` [PATCH net-next v20 04/14] mm: page_frag: avoid caller accessing 'page_frag_cache' directly Yunsheng Lin
@ 2024-10-08 11:20 ` Yunsheng Lin
3 siblings, 0 replies; 7+ messages in thread
From: Yunsheng Lin @ 2024-10-08 11:20 UTC (permalink / raw)
To: davem, kuba, pabeni
Cc: netdev, linux-kernel, Yunsheng Lin, Alexander Duyck,
Andrew Morton, Shuah Khan, linux-mm, linux-kselftest
Add testing for the newly added prepare API, for both aligned
and non-aligned API, also probe API is also tested along with
prepare API.
CC: Alexander Duyck <alexander.duyck@gmail.com>
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
---
.../selftests/mm/page_frag/page_frag_test.c | 66 +++++++++++++++++--
tools/testing/selftests/mm/run_vmtests.sh | 4 ++
tools/testing/selftests/mm/test_page_frag.sh | 31 +++++++++
3 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/mm/page_frag/page_frag_test.c b/tools/testing/selftests/mm/page_frag/page_frag_test.c
index 36543a129e40..567bcc6a181e 100644
--- a/tools/testing/selftests/mm/page_frag/page_frag_test.c
+++ b/tools/testing/selftests/mm/page_frag/page_frag_test.c
@@ -29,6 +29,10 @@ static bool test_align;
module_param(test_align, bool, 0);
MODULE_PARM_DESC(test_align, "use align API for testing");
+static bool test_prepare;
+module_param(test_prepare, bool, 0);
+MODULE_PARM_DESC(test_prepare, "use prepare API for testing");
+
static int test_alloc_len = 2048;
module_param(test_alloc_len, int, 0);
MODULE_PARM_DESC(test_alloc_len, "alloc len for testing");
@@ -68,6 +72,18 @@ static int page_frag_pop_thread(void *arg)
return 0;
}
+static void frag_frag_test_commit(struct page_frag_cache *nc,
+ struct page_frag *prepare_pfrag,
+ struct page_frag *probe_pfrag,
+ unsigned int used_sz)
+{
+ WARN_ON_ONCE(prepare_pfrag->page != probe_pfrag->page ||
+ prepare_pfrag->offset != probe_pfrag->offset ||
+ prepare_pfrag->size != probe_pfrag->size);
+
+ page_frag_commit(nc, prepare_pfrag, used_sz);
+}
+
static int page_frag_push_thread(void *arg)
{
struct ptr_ring *ring = arg;
@@ -80,13 +96,52 @@ static int page_frag_push_thread(void *arg)
int ret;
if (test_align) {
- va = page_frag_alloc_align(&test_nc, test_alloc_len,
- GFP_KERNEL, SMP_CACHE_BYTES);
+ if (test_prepare) {
+ struct page_frag prepare_frag, probe_frag;
+ void *probe_va;
+
+ va = page_frag_alloc_refill_prepare_align(&test_nc,
+ test_alloc_len,
+ &prepare_frag,
+ GFP_KERNEL,
+ SMP_CACHE_BYTES);
+
+ probe_va = __page_frag_alloc_refill_probe_align(&test_nc,
+ test_alloc_len,
+ &probe_frag,
+ -SMP_CACHE_BYTES);
+ WARN_ON_ONCE(va != probe_va);
+
+ if (likely(va))
+ frag_frag_test_commit(&test_nc, &prepare_frag,
+ &probe_frag, test_alloc_len);
+ } else {
+ va = page_frag_alloc_align(&test_nc,
+ test_alloc_len,
+ GFP_KERNEL,
+ SMP_CACHE_BYTES);
+ }
WARN_ONCE((unsigned long)va & (SMP_CACHE_BYTES - 1),
"unaligned va returned\n");
} else {
- va = page_frag_alloc(&test_nc, test_alloc_len, GFP_KERNEL);
+ if (test_prepare) {
+ struct page_frag prepare_frag, probe_frag;
+ void *probe_va;
+
+ va = page_frag_alloc_refill_prepare(&test_nc, test_alloc_len,
+ &prepare_frag, GFP_KERNEL);
+
+ probe_va = page_frag_alloc_refill_probe(&test_nc, test_alloc_len,
+ &probe_frag);
+
+ WARN_ON_ONCE(va != probe_va);
+ if (likely(va))
+ frag_frag_test_commit(&test_nc, &prepare_frag,
+ &probe_frag, test_alloc_len);
+ } else {
+ va = page_frag_alloc(&test_nc, test_alloc_len, GFP_KERNEL);
+ }
}
if (!va)
@@ -152,8 +207,9 @@ static int __init page_frag_test_init(void)
test_pushed, test_popped);
duration = (u64)ktime_us_delta(ktime_get(), start);
- pr_info("%d of iterations for %s testing took: %lluus\n", nr_test,
- test_align ? "aligned" : "non-aligned", duration);
+ pr_info("%d of iterations for %s %s API testing took: %lluus\n", nr_test,
+ test_align ? "aligned" : "non-aligned",
+ test_prepare ? "prepare" : "alloc", duration);
ptr_ring_cleanup(&ptr_ring, NULL);
page_frag_cache_drain(&test_nc);
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 2c5394584af4..f6ff9080a6f2 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -464,6 +464,10 @@ CATEGORY="page_frag" run_test ./test_page_frag.sh aligned
CATEGORY="page_frag" run_test ./test_page_frag.sh nonaligned
+CATEGORY="page_frag" run_test ./test_page_frag.sh aligned_prepare
+
+CATEGORY="page_frag" run_test ./test_page_frag.sh nonaligned_prepare
+
echo "SUMMARY: PASS=${count_pass} SKIP=${count_skip} FAIL=${count_fail}" | tap_prefix
echo "1..${count_total}" | tap_output
diff --git a/tools/testing/selftests/mm/test_page_frag.sh b/tools/testing/selftests/mm/test_page_frag.sh
index d750d910c899..71c3531fa38e 100755
--- a/tools/testing/selftests/mm/test_page_frag.sh
+++ b/tools/testing/selftests/mm/test_page_frag.sh
@@ -36,6 +36,8 @@ ksft_skip=4
SMOKE_PARAM="test_push_cpu=$TEST_CPU_0 test_pop_cpu=$TEST_CPU_1"
NONALIGNED_PARAM="$SMOKE_PARAM test_alloc_len=75 nr_test=$NR_TEST"
ALIGNED_PARAM="$NONALIGNED_PARAM test_align=1"
+NONALIGNED_PREPARE_PARAM="$NONALIGNED_PARAM test_prepare=1"
+ALIGNED_PREPARE_PARAM="$ALIGNED_PARAM test_prepare=1"
check_test_requirements()
{
@@ -74,6 +76,24 @@ run_aligned_check()
echo "Check the kernel ring buffer to see the summary."
}
+run_nonaligned_prepare_check()
+{
+ echo "Run performance tests to evaluate how fast nonaligned prepare API is."
+
+ insmod $DRIVER $NONALIGNED_PREPARE_PARAM > /dev/null 2>&1
+ echo "Done."
+ echo "Ccheck the kernel ring buffer to see the summary."
+}
+
+run_aligned_prepare_check()
+{
+ echo "Run performance tests to evaluate how fast aligned prepare API is."
+
+ insmod $DRIVER $ALIGNED_PREPARE_PARAM > /dev/null 2>&1
+ echo "Done."
+ echo "Check the kernel ring buffer to see the summary."
+}
+
run_smoke_check()
{
echo "Run smoke test."
@@ -86,6 +106,7 @@ run_smoke_check()
usage()
{
echo -n "Usage: $0 [ aligned ] | [ nonaligned ] | | [ smoke ] | "
+ echo "[ aligned_prepare ] | [ nonaligned_prepare ] | "
echo "manual parameters"
echo
echo "Valid tests and parameters:"
@@ -106,6 +127,12 @@ usage()
echo "# Performance testing for aligned alloc API"
echo "$0 aligned"
echo
+ echo "# Performance testing for nonaligned prepare API"
+ echo "$0 nonaligned_prepare"
+ echo
+ echo "# Performance testing for aligned prepare API"
+ echo "$0 aligned_prepare"
+ echo
exit 0
}
@@ -159,6 +186,10 @@ function run_test()
run_nonaligned_check
elif [[ "$1" = "aligned" ]]; then
run_aligned_check
+ elif [[ "$1" = "nonaligned_prepare" ]]; then
+ run_nonaligned_prepare_check
+ elif [[ "$1" = "aligned_prepare" ]]; then
+ run_aligned_prepare_check
else
run_manual_check $@
fi
--
2.33.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag
2024-10-08 11:20 ` [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag Yunsheng Lin
@ 2024-10-08 19:56 ` Shuah Khan
2024-10-09 3:59 ` Yunsheng Lin
0 siblings, 1 reply; 7+ messages in thread
From: Shuah Khan @ 2024-10-08 19:56 UTC (permalink / raw)
To: Yunsheng Lin, davem, kuba, pabeni
Cc: netdev, linux-kernel, Alexander Duyck, Alexander Duyck,
Andrew Morton, Shuah Khan, linux-mm, linux-kselftest, Shuah Khan
On 10/8/24 05:20, Yunsheng Lin wrote:
> The testing is done by ensuring that the fragment allocated
> from a frag_frag_cache instance is pushed into a ptr_ring
> instance in a kthread binded to a specified cpu, and a kthread
> binded to a specified cpu will pop the fragment from the
> ptr_ring and free the fragment.
>
> CC: Alexander Duyck <alexander.duyck@gmail.com>
> Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
> Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
Signed-off-by should be last. Same comment on all the other
patches in this series. When you have 4 patches, it is a good
practice to add cover-letter.
> ---
> tools/testing/selftests/mm/Makefile | 3 +
> tools/testing/selftests/mm/page_frag/Makefile | 18 ++
> .../selftests/mm/page_frag/page_frag_test.c | 173 ++++++++++++++++++
> tools/testing/selftests/mm/run_vmtests.sh | 8 +
> tools/testing/selftests/mm/test_page_frag.sh | 171 +++++++++++++++++
> 5 files changed, 373 insertions(+)
> create mode 100644 tools/testing/selftests/mm/page_frag/Makefile
> create mode 100644 tools/testing/selftests/mm/page_frag/page_frag_test.c
> create mode 100755 tools/testing/selftests/mm/test_page_frag.sh
>
> diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
> index 02e1204971b0..acec529baaca 100644
> --- a/tools/testing/selftests/mm/Makefile
> +++ b/tools/testing/selftests/mm/Makefile
> @@ -36,6 +36,8 @@ MAKEFLAGS += --no-builtin-rules
> CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
> LDLIBS = -lrt -lpthread -lm
>
> +TEST_GEN_MODS_DIR := page_frag
> +
> TEST_GEN_FILES = cow
> TEST_GEN_FILES += compaction_test
> TEST_GEN_FILES += gup_longterm
> @@ -126,6 +128,7 @@ TEST_FILES += test_hmm.sh
> TEST_FILES += va_high_addr_switch.sh
> TEST_FILES += charge_reserved_hugetlb.sh
> TEST_FILES += hugetlb_reparenting_test.sh
> +TEST_FILES += test_page_frag.sh
>
> # required by charge_reserved_hugetlb.sh
> TEST_FILES += write_hugetlb_memory.sh
> diff --git a/tools/testing/selftests/mm/page_frag/Makefile b/tools/testing/selftests/mm/page_frag/Makefile
> new file mode 100644
> index 000000000000..58dda74d50a3
> --- /dev/null
> +++ b/tools/testing/selftests/mm/page_frag/Makefile
> @@ -0,0 +1,18 @@
> +PAGE_FRAG_TEST_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
> +KDIR ?= $(abspath $(PAGE_FRAG_TEST_DIR)/../../../../..)
> +
> +ifeq ($(V),1)
> +Q =
> +else
> +Q = @
> +endif
> +
> +MODULES = page_frag_test.ko
> +
> +obj-m += page_frag_test.o
> +
> +all:
> + +$(Q)make -C $(KDIR) M=$(PAGE_FRAG_TEST_DIR) modules
> +
> +clean:
> + +$(Q)make -C $(KDIR) M=$(PAGE_FRAG_TEST_DIR) clean
> diff --git a/tools/testing/selftests/mm/page_frag/page_frag_test.c b/tools/testing/selftests/mm/page_frag/page_frag_test.c
> new file mode 100644
> index 000000000000..eeb2b6bc681a
> --- /dev/null
> +++ b/tools/testing/selftests/mm/page_frag/page_frag_test.c
> @@ -0,0 +1,173 @@
> +// SPDX-License-Identifier: GPL-2.0
I think this would throw a checkpatch warning about
comment should be "/*" and not "//"
> +
> +/*
> + * Test module for page_frag cache
> + *
> + * Copyright (C) 2024 Yunsheng Lin <linyunsheng@huawei.com>
> + */
> +
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/cpumask.h>
> +#include <linux/completion.h>
> +#include <linux/ptr_ring.h>
> +#include <linux/kthread.h>
> +
> +static struct ptr_ring ptr_ring;
> +static int nr_objs = 512;
> +static atomic_t nthreads;
> +static struct completion wait;
> +static struct page_frag_cache test_nc;
> +static int test_popped;
> +static int test_pushed;
> +
> +static int nr_test = 2000000;
> +module_param(nr_test, int, 0);
> +MODULE_PARM_DESC(nr_test, "number of iterations to test");
> +
> +static bool test_align;
> +module_param(test_align, bool, 0);
> +MODULE_PARM_DESC(test_align, "use align API for testing");
> +
> +static int test_alloc_len = 2048;
> +module_param(test_alloc_len, int, 0);
> +MODULE_PARM_DESC(test_alloc_len, "alloc len for testing");
> +
> +static int test_push_cpu;
> +module_param(test_push_cpu, int, 0);
> +MODULE_PARM_DESC(test_push_cpu, "test cpu for pushing fragment");
> +
> +static int test_pop_cpu;
> +module_param(test_pop_cpu, int, 0);
> +MODULE_PARM_DESC(test_pop_cpu, "test cpu for popping fragment");
> +
> +static int page_frag_pop_thread(void *arg)
> +{
> + struct ptr_ring *ring = arg;
> +
> + pr_info("page_frag pop test thread begins on cpu %d\n",
> + smp_processor_id());
> +
> + while (test_popped < nr_test) {
> + void *obj = __ptr_ring_consume(ring);
> +
> + if (obj) {
> + test_popped++;
> + page_frag_free(obj);
> + } else {
> + cond_resched();
> + }
> + }
> +
> + if (atomic_dec_and_test(&nthreads))
> + complete(&wait);
> +
> + pr_info("page_frag pop test thread exits on cpu %d\n",
> + smp_processor_id());
> +
> + return 0;
> +}
> +
> +static int page_frag_push_thread(void *arg)
> +{
> + struct ptr_ring *ring = arg;
> +
> + pr_info("page_frag push test thread begins on cpu %d\n",
> + smp_processor_id());
> +
> + while (test_pushed < nr_test) {
> + void *va;
> + int ret;
> +
> + if (test_align) {
> + va = page_frag_alloc_align(&test_nc, test_alloc_len,
> + GFP_KERNEL, SMP_CACHE_BYTES);
> +
> + WARN_ONCE((unsigned long)va & (SMP_CACHE_BYTES - 1),
> + "unaligned va returned\n");
> + } else {
> + va = page_frag_alloc(&test_nc, test_alloc_len, GFP_KERNEL);
> + }
> +
> + if (!va)
> + continue;
> +
> + ret = __ptr_ring_produce(ring, va);
> + if (ret) {
> + page_frag_free(va);
> + cond_resched();
> + } else {
> + test_pushed++;
> + }
> + }
> +
> + pr_info("page_frag push test thread exits on cpu %d\n",
> + smp_processor_id());
> +
> + if (atomic_dec_and_test(&nthreads))
> + complete(&wait);
> +
> + return 0;
> +}
> +
> +static int __init page_frag_test_init(void)
> +{
> + struct task_struct *tsk_push, *tsk_pop;
> + ktime_t start;
> + u64 duration;
> + int ret;
> +
> + test_nc.va = NULL;
> + atomic_set(&nthreads, 2);
> + init_completion(&wait);
> +
> + if (test_alloc_len > PAGE_SIZE || test_alloc_len <= 0 ||
> + !cpu_active(test_push_cpu) || !cpu_active(test_pop_cpu))
> + return -EINVAL;
> +
> + ret = ptr_ring_init(&ptr_ring, nr_objs, GFP_KERNEL);
> + if (ret)
> + return ret;
> +
> + tsk_push = kthread_create_on_cpu(page_frag_push_thread, &ptr_ring,
> + test_push_cpu, "page_frag_push");
> + if (IS_ERR(tsk_push))
> + return PTR_ERR(tsk_push);
> +
> + tsk_pop = kthread_create_on_cpu(page_frag_pop_thread, &ptr_ring,
> + test_pop_cpu, "page_frag_pop");
> + if (IS_ERR(tsk_pop)) {
> + kthread_stop(tsk_push);
> + return PTR_ERR(tsk_pop);
> + }
> +
> + start = ktime_get();
> + wake_up_process(tsk_push);
> + wake_up_process(tsk_pop);
> +
> + pr_info("waiting for test to complete\n");
> +
> + while (!wait_for_completion_timeout(&wait, msecs_to_jiffies(10000)))
> + pr_info("page_frag_test progress: pushed = %d, popped = %d\n",
> + test_pushed, test_popped);
> +
> + duration = (u64)ktime_us_delta(ktime_get(), start);
> + pr_info("%d of iterations for %s testing took: %lluus\n", nr_test,
> + test_align ? "aligned" : "non-aligned", duration);
> +
> + ptr_ring_cleanup(&ptr_ring, NULL);
> + page_frag_cache_drain(&test_nc);
> +
> + return -EAGAIN;
> +}
> +
> +static void __exit page_frag_test_exit(void)
> +{
> +}
> +
> +module_init(page_frag_test_init);
> +module_exit(page_frag_test_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Yunsheng Lin <linyunsheng@huawei.com>");
> +MODULE_DESCRIPTION("Test module for page_frag");
> diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
> index c5797ad1d37b..2c5394584af4 100755
> --- a/tools/testing/selftests/mm/run_vmtests.sh
> +++ b/tools/testing/selftests/mm/run_vmtests.sh
> @@ -75,6 +75,8 @@ separated by spaces:
> read-only VMAs
> - mdwe
> test prctl(PR_SET_MDWE, ...)
> +- page_frag
> + test handling of page fragment allocation and freeing
>
> example: ./run_vmtests.sh -t "hmm mmap ksm"
> EOF
> @@ -456,6 +458,12 @@ CATEGORY="mkdirty" run_test ./mkdirty
>
> CATEGORY="mdwe" run_test ./mdwe_test
>
> +CATEGORY="page_frag" run_test ./test_page_frag.sh smoke
> +
> +CATEGORY="page_frag" run_test ./test_page_frag.sh aligned
> +
> +CATEGORY="page_frag" run_test ./test_page_frag.sh nonaligned
> +
> echo "SUMMARY: PASS=${count_pass} SKIP=${count_skip} FAIL=${count_fail}" | tap_prefix
> echo "1..${count_total}" | tap_output
>
> diff --git a/tools/testing/selftests/mm/test_page_frag.sh b/tools/testing/selftests/mm/test_page_frag.sh
> new file mode 100755
> index 000000000000..d750d910c899
> --- /dev/null
> +++ b/tools/testing/selftests/mm/test_page_frag.sh
> @@ -0,0 +1,171 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Copyright (C) 2024 Yunsheng Lin <linyunsheng@huawei.com>
> +# Copyright (C) 2018 Uladzislau Rezki (Sony) <urezki@gmail.com>
> +#
> +# This is a test script for the kernel test driver to test the
> +# correctness and performance of page_frag's implementation.
> +# Therefore it is just a kernel module loader. You can specify
> +# and pass different parameters in order to:
> +# a) analyse performance of page fragment allocations;
> +# b) stressing and stability check of page_frag subsystem.
> +
> +DRIVER="./page_frag/page_frag_test.ko"
> +CPU_LIST=$(grep -m 2 processor /proc/cpuinfo | cut -d ' ' -f 2)
> +TEST_CPU_0=$(echo $CPU_LIST | awk '{print $1}')
> +
> +if [ $(echo $CPU_LIST | wc -w) -gt 1 ]; then
> + TEST_CPU_1=$(echo $CPU_LIST | awk '{print $2}')
> + NR_TEST=100000000
> +else
> + TEST_CPU_1=$TEST_CPU_0
> + NR_TEST=1000000
> +fi
> +
> +# 1 if fails
> +exitcode=1
> +
> +# Kselftest framework requirement - SKIP code is 4.
> +ksft_skip=4
> +
> +#
> +# Static templates for testing of page_frag APIs.
> +# Also it is possible to pass any supported parameters manually.
> +#
> +SMOKE_PARAM="test_push_cpu=$TEST_CPU_0 test_pop_cpu=$TEST_CPU_1"
> +NONALIGNED_PARAM="$SMOKE_PARAM test_alloc_len=75 nr_test=$NR_TEST"
> +ALIGNED_PARAM="$NONALIGNED_PARAM test_align=1"
> +
> +check_test_requirements()
> +{
> + uid=$(id -u)
> + if [ $uid -ne 0 ]; then
> + echo "$0: Must be run as root"
> + exit $ksft_skip
> + fi
> +
> + if ! which insmod > /dev/null 2>&1; then
> + echo "$0: You need insmod installed"
> + exit $ksft_skip
> + fi
> +
> + if [ ! -f $DRIVER ]; then
> + echo "$0: You need to compile page_frag_test module"
> + exit $ksft_skip
> + fi
> +}
> +
> +run_nonaligned_check()
> +{
> + echo "Run performance tests to evaluate how fast nonaligned alloc API is."
> +
> + insmod $DRIVER $NONALIGNED_PARAM > /dev/null 2>&1
> + echo "Done."
> + echo "Check the kernel ring buffer to see the summary."
> +}
> +
> +run_aligned_check()
> +{
> + echo "Run performance tests to evaluate how fast aligned alloc API is."
> +
> + insmod $DRIVER $ALIGNED_PARAM > /dev/null 2>&1
> + echo "Done."
> + echo "Check the kernel ring buffer to see the summary."
> +}
> +
> +run_smoke_check()
> +{
> + echo "Run smoke test."
> +
> + insmod $DRIVER $SMOKE_PARAM > /dev/null 2>&1
> + echo "Done."
> + echo "Check the kernel ring buffer to see the summary."
> +}
> +
> +usage()
> +{
> + echo -n "Usage: $0 [ aligned ] | [ nonaligned ] | | [ smoke ] | "
> + echo "manual parameters"
> + echo
> + echo "Valid tests and parameters:"
> + echo
> + modinfo $DRIVER
> + echo
> + echo "Example usage:"
> + echo
> + echo "# Shows help message"
> + echo "$0"
> + echo
> + echo "# Smoke testing"
> + echo "$0 smoke"
> + echo
> + echo "# Performance testing for nonaligned alloc API"
> + echo "$0 nonaligned"
> + echo
> + echo "# Performance testing for aligned alloc API"
> + echo "$0 aligned"
> + echo
> + exit 0
> +}
> +
> +function validate_passed_args()
> +{
> + VALID_ARGS=`modinfo $DRIVER | awk '/parm:/ {print $2}' | sed 's/:.*//'`
> +
> + #
> + # Something has been passed, check it.
> + #
> + for passed_arg in $@; do
> + key=${passed_arg//=*/}
> + valid=0
> +
> + for valid_arg in $VALID_ARGS; do
> + if [[ $key = $valid_arg ]]; then
> + valid=1
> + break
> + fi
> + done
> +
> + if [[ $valid -ne 1 ]]; then
> + echo "Error: key is not correct: ${key}"
> + exit $exitcode
> + fi
> + done
> +}
> +
> +function run_manual_check()
> +{
> + #
> + # Validate passed parameters. If there is wrong one,
> + # the script exists and does not execute further.
> + #
> + validate_passed_args $@
> +
> + echo "Run the test with following parameters: $@"
Is this marker good enough to isolate the test results in the
dmesg? Include the test name in the message.
> + insmod $DRIVER $@ > /dev/null 2>&1
> + echo "Done."
Is this marker good enough to isolate the test results in the
dmesg? Include the test name in the message.
> + echo "Check the kernel ring buffer to see the summary."
Usually the test would run dmesg and filter out the test results
from the dmesg and include them in the test script output.
You can refer to other tests that do that: powerpc/scripts/hmi.sh
is one example.
> +}
> +
> +function run_test()
> +{
> + if [ $# -eq 0 ]; then
> + usage
> + else
> + if [[ "$1" = "smoke" ]]; then
> + run_smoke_check
> + elif [[ "$1" = "nonaligned" ]]; then
> + run_nonaligned_check
> + elif [[ "$1" = "aligned" ]]; then
> + run_aligned_check
> + else
> + run_manual_check $@
> + fi
> + fi
> +}
> +
> +check_test_requirements
> +run_test $@
> +
> +exit 0
thanks,
-- Shuah
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag
2024-10-08 19:56 ` Shuah Khan
@ 2024-10-09 3:59 ` Yunsheng Lin
2024-10-10 21:18 ` Shuah Khan
0 siblings, 1 reply; 7+ messages in thread
From: Yunsheng Lin @ 2024-10-09 3:59 UTC (permalink / raw)
To: Shuah Khan, davem, kuba, pabeni
Cc: netdev, linux-kernel, Alexander Duyck, Alexander Duyck,
Andrew Morton, Shuah Khan, linux-mm, linux-kselftest
On 2024/10/9 3:56, Shuah Khan wrote:
> On 10/8/24 05:20, Yunsheng Lin wrote:
>> The testing is done by ensuring that the fragment allocated
>> from a frag_frag_cache instance is pushed into a ptr_ring
>> instance in a kthread binded to a specified cpu, and a kthread
>> binded to a specified cpu will pop the fragment from the
>> ptr_ring and free the fragment.
>>
>> CC: Alexander Duyck <alexander.duyck@gmail.com>
>> Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
>> Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
>
> Signed-off-by should be last. Same comment on all the other
Hi, Shuah
I used 'git am' to collect those tag, it seems that is the order
the tool applied, and I checking other applied commit, it seems
only Signed-off-by from the committer is the last, like the below
recent mm commit:
6901cf55de22
ff7f5ad7bce4
> patches in this series. When you have 4 patches, it is a good
> practice to add cover-letter.
I guess the cover-letter meant below?
https://lore.kernel.org/all/20241008112049.2279307-1-linyunsheng@huawei.com/
>
>> ---
>> tools/testing/selftests/mm/Makefile | 3 +
>> tools/testing/selftests/mm/page_frag/Makefile | 18 ++
>> .../selftests/mm/page_frag/page_frag_test.c | 173 ++++++++++++++++++
>> tools/testing/selftests/mm/run_vmtests.sh | 8 +
>> tools/testing/selftests/mm/test_page_frag.sh | 171 +++++++++++++++++
>> 5 files changed, 373 insertions(+)
>> create mode 100644 tools/testing/selftests/mm/page_frag/Makefile
>> create mode 100644 tools/testing/selftests/mm/page_frag/page_frag_test.c
>> create mode 100755 tools/testing/selftests/mm/test_page_frag.sh
>>
>> diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
>> index 02e1204971b0..acec529baaca 100644
>> --- a/tools/testing/selftests/mm/Makefile
>> +++ b/tools/testing/selftests/mm/Makefile
>> @@ -36,6 +36,8 @@ MAKEFLAGS += --no-builtin-rules
>> CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
>> LDLIBS = -lrt -lpthread -lm
>> +TEST_GEN_MODS_DIR := page_frag
>> +
>> TEST_GEN_FILES = cow
>> TEST_GEN_FILES += compaction_test
>> TEST_GEN_FILES += gup_longterm
>> @@ -126,6 +128,7 @@ TEST_FILES += test_hmm.sh
>> TEST_FILES += va_high_addr_switch.sh
>> TEST_FILES += charge_reserved_hugetlb.sh
>> TEST_FILES += hugetlb_reparenting_test.sh
>> +TEST_FILES += test_page_frag.sh
>> # required by charge_reserved_hugetlb.sh
>> TEST_FILES += write_hugetlb_memory.sh
>> diff --git a/tools/testing/selftests/mm/page_frag/Makefile b/tools/testing/selftests/mm/page_frag/Makefile
>> new file mode 100644
>> index 000000000000..58dda74d50a3
>> --- /dev/null
>> +++ b/tools/testing/selftests/mm/page_frag/Makefile
>> @@ -0,0 +1,18 @@
>> +PAGE_FRAG_TEST_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
>> +KDIR ?= $(abspath $(PAGE_FRAG_TEST_DIR)/../../../../..)
>> +
>> +ifeq ($(V),1)
>> +Q =
>> +else
>> +Q = @
>> +endif
>> +
>> +MODULES = page_frag_test.ko
>> +
>> +obj-m += page_frag_test.o
>> +
>> +all:
>> + +$(Q)make -C $(KDIR) M=$(PAGE_FRAG_TEST_DIR) modules
>> +
>> +clean:
>> + +$(Q)make -C $(KDIR) M=$(PAGE_FRAG_TEST_DIR) clean
>> diff --git a/tools/testing/selftests/mm/page_frag/page_frag_test.c b/tools/testing/selftests/mm/page_frag/page_frag_test.c
>> new file mode 100644
>> index 000000000000..eeb2b6bc681a
>> --- /dev/null
>> +++ b/tools/testing/selftests/mm/page_frag/page_frag_test.c
>> @@ -0,0 +1,173 @@
>> +// SPDX-License-Identifier: GPL-2.0
>
> I think this would throw a checkpatch warning about
> comment should be "/*" and not "//"
using "git grep 'SPDX-License' mm", "//" seems like a more common
case.
And I did using './scripts/checkpatch.pl --strict --codespell', and
it does not throw a checkpatch warning.
>> +
>> +/*
>> + * Test module for page_frag cache
>> + *
...
>> +function run_manual_check()
>> +{
>> + #
>> + # Validate passed parameters. If there is wrong one,
>> + # the script exists and does not execute further.
>> + #
>> + validate_passed_args $@
>> +
>> + echo "Run the test with following parameters: $@"
>
> Is this marker good enough to isolate the test results in the
> dmesg? Include the test name in the message.
>
>
>> + insmod $DRIVER $@ > /dev/null 2>&1
>> + echo "Done."
>
> Is this marker good enough to isolate the test results in the
> dmesg? Include the test name in the message.
>
>> + echo "Check the kernel ring buffer to see the summary."
>
> Usually the test would run dmesg and filter out the test results
> from the dmesg and include them in the test script output.
>
> You can refer to other tests that do that: powerpc/scripts/hmi.sh
> is one example.
Thanks, will check that.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag
2024-10-09 3:59 ` Yunsheng Lin
@ 2024-10-10 21:18 ` Shuah Khan
0 siblings, 0 replies; 7+ messages in thread
From: Shuah Khan @ 2024-10-10 21:18 UTC (permalink / raw)
To: Yunsheng Lin, davem, kuba, pabeni
Cc: netdev, linux-kernel, Alexander Duyck, Alexander Duyck,
Andrew Morton, Shuah Khan, linux-mm, linux-kselftest, Shuah Khan
On 10/8/24 21:59, Yunsheng Lin wrote:
> On 2024/10/9 3:56, Shuah Khan wrote:
>> On 10/8/24 05:20, Yunsheng Lin wrote:
>>> The testing is done by ensuring that the fragment allocated
>>> from a frag_frag_cache instance is pushed into a ptr_ring
>>> instance in a kthread binded to a specified cpu, and a kthread
>>> binded to a specified cpu will pop the fragment from the
>>> ptr_ring and free the fragment.
>>>
>>> CC: Alexander Duyck <alexander.duyck@gmail.com>
>>> Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
>>> Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
>>
>> Signed-off-by should be last. Same comment on all the other
>
> Hi, Shuah
>
> I used 'git am' to collect those tag, it seems that is the order
> the tool applied, and I checking other applied commit, it seems
> only Signed-off-by from the committer is the last, like the below
> recent mm commit:
> 6901cf55de22
> ff7f5ad7bce4
>
okay.
>> patches in this series. When you have 4 patches, it is a good
>> practice to add cover-letter.
>
> I guess the cover-letter meant below?
> https://lore.kernel.org/all/20241008112049.2279307-1-linyunsheng@huawei.com/
Somehow this isn't in my Inbox.
>
>>
[snip]
> ...
>
>>> +function run_manual_check()
>>> +{
>>> + #
>>> + # Validate passed parameters. If there is wrong one,
>>> + # the script exists and does not execute further.
>>> + #
>>> + validate_passed_args $@
>>> +
>>> + echo "Run the test with following parameters: $@"
>>
>> Is this marker good enough to isolate the test results in the
>> dmesg? Include the test name in the message.
>>
>>
>>> + insmod $DRIVER $@ > /dev/null 2>&1
>>> + echo "Done."
>>
>> Is this marker good enough to isolate the test results in the
>> dmesg? Include the test name in the message.
>>
>>> + echo "Check the kernel ring buffer to see the summary."
>>
>> Usually the test would run dmesg and filter out the test results
>> from the dmesg and include them in the test script output.
>>
>> You can refer to other tests that do that: powerpc/scripts/hmi.sh
>> is one example.
>
> Thanks, will check that.
thanks,
-- Shuah
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2024-10-10 21:19 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20241008112049.2279307-1-linyunsheng@huawei.com>
2024-10-08 11:20 ` [PATCH net-next v20 01/14] mm: page_frag: add a test module for page_frag Yunsheng Lin
2024-10-08 19:56 ` Shuah Khan
2024-10-09 3:59 ` Yunsheng Lin
2024-10-10 21:18 ` Shuah Khan
2024-10-08 11:20 ` [PATCH net-next v20 02/14] mm: move the page fragment allocator from page_alloc into its own file Yunsheng Lin
2024-10-08 11:20 ` [PATCH net-next v20 04/14] mm: page_frag: avoid caller accessing 'page_frag_cache' directly Yunsheng Lin
2024-10-08 11:20 ` [PATCH net-next v20 11/14] mm: page_frag: add testing for the newly added prepare API Yunsheng Lin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox