* [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros
2026-03-17 17:36 [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization wen.yang
@ 2026-03-17 17:36 ` wen.yang
2026-03-17 17:36 ` [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX() wen.yang
2026-03-19 14:12 ` [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization Joel Granados
2 siblings, 0 replies; 7+ messages in thread
From: wen.yang @ 2026-03-17 17:36 UTC (permalink / raw)
To: Joel Granados; +Cc: linux-kernel, Wen Yang
From: Wen Yang <wen.yang@linux.dev>
Add a family of CTLTBL_ENTRY_XXX() macros to simplify struct ctl_table
initialization. Each macro variant covers a different use case, using
_Generic() for automatic type detection and proc_handler selection,
with sensible defaults to reduce boilerplate and potential errors.
The following macro variants are introduced:
CTLTBL_ENTRY_V(__var)
- Uses variable name as procname, mode 0444,
and auto-selected handler based on variable type
CTLTBL_ENTRY_VM(__var, __mode)
- Uses variable name as procname, custom mode,
and auto-selected handler based on variable type
CTLTBL_ENTRY_VMR(__var, __mode, __min, __max)
- Uses variable name as procname, custom mode,
and auto-selected range-checking handler based on variable type
CTLTBL_ENTRY_VN(__var, __name)
- Custom procname, mode 0444,
and auto-selected handler based on variable type
CTLTBL_ENTRY_VNM(__var, __name, __mode)
- Custom procname and mode,
and auto-selected handler based on variable type
CTLTBL_ENTRY_VNMH(__var, __name, __mode, __handler)
- Custom procname, mode, and proc_handler
CTLTBL_ENTRY_VNMR(__var, __name, __mode, __min, __max)
- Auto-selected range-checking handler based on variable type
CTLTBL_ENTRY_VNMHR(__var, __name, __mode, __handler, __min, __max)
- Custom handler with extra1/extra2 range bounds
CTLTBL_ENTRY_VNMHRL(__var, __name, __mode, __handler, __min, __max, __maxlen)
- Full control including explicit maxlen override
All variants share the following features:
- Automatic type detection via _Generic(): supports int, unsigned int,
long, unsigned long; selects the appropriate proc_dointvec,
proc_douintvec, or proc_doulongvec_minmax handler automatically
- Auto address-of: the macro takes the address of the variable, so
callers do not need to pass &var explicitly
- Auto maxlen: uses sizeof(var) when maxlen is not explicitly provided
- SYSCTL_NULL marker: use SYSCTL_NULL as the variable argument for
entries where .data should be NULL
- Compile-time validation: NAME must be a string literal; MODE and
HANDLER are checked for type compatibility
No functional change intended.
Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
include/linux/sysctl.h | 294 +++++++++++++++++++++++++++++++++++++++++
kernel/sysctl.c | 2 +
2 files changed, 296 insertions(+)
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 2886fbceb5d6..840a3fba5fa9 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -175,6 +175,300 @@ struct ctl_table {
void *extra2;
} __randomize_layout;
+/**
+ * struct _sysctl_null_type - sentinel type for variable-less entries
+ */
+struct _sysctl_null_type { char __dummy; };
+
+/**
+ * _sysctl_null_marker - unique instance of the sentinel type
+ *
+ * Define once per link unit:
+ * const struct _sysctl_null_type _sysctl_null_marker;
+ */
+extern const struct _sysctl_null_type _sysctl_null_marker;
+
+/**
+ * SYSCTL_NULL - pass as __var for entries with no associated variable
+ */
+#define SYSCTL_NULL (_sysctl_null_marker)
+
+/**
+ * __CTL_AUTO_HANDLER - select proc_handler based on the variable type
+ * @__var: kernel variable (value, not address), or SYSCTL_NULL
+ */
+#define __CTL_AUTO_HANDLER(__var) \
+ _Generic((__var), \
+ int : proc_dointvec, \
+ unsigned int : proc_douintvec, \
+ long : proc_doulongvec_minmax, \
+ unsigned long : proc_doulongvec_minmax, \
+ default : \
+ 0xdeadbeaf)
+
+/**
+ * __CTL_AUTO_HANDLER_RANGE - select proc_handler for a range-constrained entry
+ * @__var: kernel variable (value, not address), or SYSCTL_NULL
+ */
+#define __CTL_AUTO_HANDLER_RANGE(__var) \
+ _Generic((__var), \
+ int : proc_dointvec_minmax, \
+ unsigned int : proc_douintvec_minmax, \
+ long : proc_doulongvec_minmax, \
+ unsigned long : proc_doulongvec_minmax, \
+ default : \
+ 0xdeadbeaf)
+
+/**
+ * __CTL_PROCNAME - validate and return the procname string
+ *
+ * "" __name "" enforces a string-literal argument at compile time.
+ *
+ * @__name: procname string literal
+ */
+#define __CTL_PROCNAME(__name) ("" __name "")
+
+/**
+ * __CTL_DATA - assert __var is addressable; return its address
+ *
+ * SYSCTL_NULL -> (void *)NULL
+ * lvalue __var -> (void *)&(__var)
+ *
+ * @__var: kernel variable (without &), or SYSCTL_NULL
+ */
+#define __CTL_DATA(__var) \
+ _Generic((__var), \
+ struct _sysctl_null_type : (void *)NULL, \
+ default : \
+ (void *)&(__var))
+
+/* Compute maxlen for NULL entries: use explicit MAXLEN if >0, else 0 */
+#define __SYSCTL_MAXLEN_NULL(__maxlen) \
+ ((__maxlen) > 0 ? (size_t)(__maxlen) : (size_t)0)
+
+/* Compute maxlen: use explicit MAXLEN if >0, else sizeof(VAR) */
+#define __SYSCTL_MAXLEN_VAR(__var, __maxlen) \
+ ((__maxlen) >= 0 ? (size_t)(__maxlen) : (size_t)sizeof(__var))
+
+/**
+ * __CTL_MAXLEN - compute the .maxlen value
+ *
+ * @__var: kernel variable (without &), or SYSCTL_NULL
+ * @__maxlen: explicit override, or -1 for auto-sizing
+ */
+#define __CTL_MAXLEN(__var, __maxlen) \
+ _Generic((__var), \
+ struct _sysctl_null_type : \
+ __SYSCTL_MAXLEN_NULL(__maxlen), \
+ default : \
+ __SYSCTL_MAXLEN_VAR(__var, __maxlen) \
+ )
+
+/**
+ * __CTL_MODE - validate and return file permission bits
+ *
+ * @__mode: file permission bits (e.g. 0644)
+ */
+#define __CTL_MODE(__mode) (0 ? (umode_t)0 : (__mode))
+
+/**
+ * __CTL_HANDLER - validate and return proc_handler
+ *
+ * @__handler: proc_handler
+ */
+#define __CTL_HANDLER(__handler) \
+ (0 ? (typeof(proc_handler) *)0 : (__handler))
+
+/* Validate PTR is pointer-compatible and return it as void* */
+#define __CTL_EXTRA(PTR) \
+ (0 ? (void *)0 : (PTR))
+
+/**
+ * Internal primitive (single source of truth)
+ *
+ * All public macros delegate here.
+ *
+ * @_var: kernel variable (without &), or SYSCTL_NULL
+ * @_name: procname string literal
+ * @_mode: file permission bits
+ * @_handler: proc_handler-compatible function pointer
+ * @_min: lower bound pointer, or NULL
+ * @_max: upper bound pointer, or NULL
+ * @_maxlen: explicit .maxlen, or -1 for auto-sizing
+ */
+#define __CTLTBL_ENTRY(_var, _name, _mode, _handler, _min, _max, _maxlen) \
+{ \
+ .procname = __CTL_PROCNAME(_name), \
+ .data = __CTL_DATA(_var), \
+ .maxlen = __CTL_MAXLEN(_var, _maxlen), \
+ .mode = __CTL_MODE(_mode), \
+ .proc_handler = __CTL_HANDLER(_handler), \
+ .poll = NULL, \
+ .extra1 = __CTL_EXTRA(_min), \
+ .extra2 = __CTL_EXTRA(_max), \
+}
+
+/**
+ * Public API
+ *
+ * Naming convention:
+ * V - Variable is auto-named via #__var
+ * N - explicit Name string
+ * M - explicit Mode
+ * H - explicit Handler
+ * R - Range checking (min/max, selects _minmax handler)
+ * L - explicit Length (.maxlen override)
+ */
+
+/**
+ * CTLTBL_ENTRY_V - read-only entry; procname == stringified variable name
+ *
+ * Proto: (T __var)
+ *
+ * .procname = #__var, .mode = 0444, .extra1 = .extra2 = NULL.
+ * proc_handler and .maxlen inferred from typeof(__var).
+ *
+ * @__var: kernel variable (without &); must be an addressable lvalue
+ */
+#define CTLTBL_ENTRY_V(__var) \
+ __CTLTBL_ENTRY(__var, #__var, 0444, \
+ __CTL_AUTO_HANDLER(__var), NULL, NULL, -1)
+
+/**
+ * CTLTBL_ENTRY_VM - entry with custom mode; procname == #__var
+ *
+ * Proto: (T __var, umode_t __mode)
+ *
+ * @__var: kernel variable (without &)
+ * @__mode: file permission bits
+ */
+#define CTLTBL_ENTRY_VM(__var, __mode) \
+ __CTLTBL_ENTRY(__var, #__var, __mode, \
+ __CTL_AUTO_HANDLER(__var), NULL, NULL, -1)
+
+/**
+ * CTLTBL_ENTRY_VMR - custom mode with range checking; procname == #__var
+ *
+ * Proto: (T __var, umode_t __mode, T *__min, T *__max)
+ *
+ * @__var: kernel variable (without &)
+ * @__mode: file permission bits
+ * @__min: pointer to minimum value; typeof(*__min) must == typeof(__var)
+ * @__max: pointer to maximum value; typeof(*__max) must == typeof(__var)
+ */
+#define CTLTBL_ENTRY_VMR(__var, __mode, __min, __max) \
+ __CTLTBL_ENTRY(__var, #__var, __mode, \
+ __CTL_AUTO_HANDLER_RANGE(__var), __min, __max, -1)
+
+/**
+ * CTLTBL_ENTRY_VN - read-only entry with explicit procname
+ *
+ * Proto: (T __var, const char *__name)
+ *
+ * @__var: kernel variable (without &)
+ * @__name: procname string literal
+ */
+#define CTLTBL_ENTRY_VN(__var, __name) \
+ __CTLTBL_ENTRY(__var, __name, 0444, \
+ __CTL_AUTO_HANDLER(__var), NULL, NULL, -1)
+
+/**
+ * CTLTBL_ENTRY_VNM - entry with explicit procname and mode
+ *
+ * Proto: (T __var, const char *__name, umode_t __mode)
+ *
+ * @__var: kernel variable (without &)
+ * @__name: procname string literal
+ * @__mode: file permission bits
+ */
+#define CTLTBL_ENTRY_VNM(__var, __name, __mode) \
+ __CTLTBL_ENTRY(__var, __name, __mode, \
+ __CTL_AUTO_HANDLER(__var), NULL, NULL, -1)
+
+/**
+ * CTLTBL_ENTRY_VNMH - entry with explicit procname, mode and handler
+ *
+ * Proto: (T __var, const char *__name, umode_t __mode,
+ * proc_handler *__handler)
+ *
+ * @__var: kernel variable (without &), or SYSCTL_NULL
+ * @__name: procname string literal
+ * @__mode: file permission bits
+ * @__handler: proc_handler-compatible function pointer
+ */
+#define CTLTBL_ENTRY_VNMH(__var, __name, __mode, __handler) \
+ __CTLTBL_ENTRY(__var, __name, __mode, __handler, NULL, NULL, -1)
+
+/**
+ * CTLTBL_ENTRY_VNMR - entry with procname, mode and range checking
+ *
+ * Proto: (T __var, const char *__name, umode_t __mode,
+ * T *__min, T *__max)
+ *
+ * @__var: kernel variable (without &)
+ * @__name: procname string literal
+ * @__mode: file permission bits
+ * @__min: pointer to minimum value; typeof(*__min) must == typeof(__var)
+ * @__max: pointer to maximum value; typeof(*__max) must == typeof(__var)
+ */
+#define CTLTBL_ENTRY_VNMR(__var, __name, __mode, __min, __max) \
+ __CTLTBL_ENTRY(__var, __name, __mode, \
+ __CTL_AUTO_HANDLER_RANGE(__var), __min, __max, -1)
+
+/**
+ * CTLTBL_ENTRY_VNMHR - entry with explicit handler and range
+ *
+ * Proto: (T __var, const char *__name, umode_t __mode,
+ * proc_handler *__handler, T *__min, T *__max)
+ *
+ * @__var: kernel variable (without &), or SYSCTL_NULL
+ * @__name: procname string literal
+ * @__mode: file permission bits
+ * @__handler: proc_handler-compatible function pointer
+ * @__min: pointer to minimum value for .extra1
+ * @__max: pointer to maximum value for .extra2
+ */
+#define CTLTBL_ENTRY_VNMHR(__var, __name, __mode, __handler, __min, __max) \
+ __CTLTBL_ENTRY(__var, __name, __mode, __handler, __min, __max, -1)
+
+/**
+ * CTLTBL_ENTRY_VNMHRL - fully explicit entry
+ *
+ * Proto: (T __var, const char *__name, umode_t __mode,
+ * proc_handler *__handler, T *__min, T *__max,
+ * size_t __maxlen)
+ *
+ * Pass -1 for __maxlen to use sizeof(__var) / 0 (auto).
+ * Pass 0 to set .maxlen to zero explicitly.
+ *
+ * @__var: kernel variable (without &), or SYSCTL_NULL
+ * @__name: procname string literal
+ * @__mode: file permission bits
+ * @__handler: proc_handler-compatible function pointer
+ * @__min: pointer to minimum value for .extra1
+ * @__max: pointer to maximum value for .extra2
+ * @__maxlen: explicit value for .maxlen
+ */
+#define CTLTBL_ENTRY_VNMHRL(__var, __name, __mode, __handler, \
+ __min, __max, __maxlen) \
+ __CTLTBL_ENTRY(__var, __name, __mode, __handler, __min, __max, __maxlen)
+
+/**
+ * CTLTBL_ENTRY_NMH - custom-handler entry with no associated variable
+ *
+ * Proto: (const char *__name, umode_t __mode,
+ * proc_handler *__handler)
+ *
+ * Shorthand for CTLTBL_ENTRY_VNMH(SYSCTL_NULL, ...).
+ * .data = NULL, .maxlen = 0, .extra1 = NULL, .extra2 = NULL.
+ *
+ * @__name: procname string literal
+ * @__mode: file permission bits
+ * @__handler: proc_handler-compatible function pointer
+ */
+#define CTLTBL_ENTRY_NMH(__name, __mode, __handler) \
+ CTLTBL_ENTRY_VNMH(SYSCTL_NULL, __name, __mode, __handler)
+
struct ctl_node {
struct rb_node node;
struct ctl_table_header *header;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 9d3a666ffde1..121e743e7709 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -29,6 +29,8 @@ EXPORT_SYMBOL(sysctl_vals);
const unsigned long sysctl_long_vals[] = { 0, 1, LONG_MAX };
EXPORT_SYMBOL_GPL(sysctl_long_vals);
+const struct _sysctl_null_type _sysctl_null_marker;
+
#if defined(CONFIG_SYSCTL)
/* Constants used for minimum and maximum */
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC PATCH v4 2/2] sysctl: convert kernel/sysctl-test.c to use CTLTBL_ENTRY_XXX()
2026-03-17 17:36 [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization wen.yang
2026-03-17 17:36 ` [RFC PATCH v4 1/2] sysctl: introduce CTLTBL_ENTRY_XXX() helper macros wen.yang
@ 2026-03-17 17:36 ` wen.yang
2026-03-19 14:15 ` Joel Granados
2026-03-19 14:12 ` [RFC PATCH v4 0/2] sysctl: refactor ctl_table initialization Joel Granados
2 siblings, 1 reply; 7+ messages in thread
From: wen.yang @ 2026-03-17 17:36 UTC (permalink / raw)
To: Joel Granados; +Cc: linux-kernel, Wen Yang
From: Wen Yang <wen.yang@linux.dev>
Convert all struct ctl_table initializations in kernel/sysctl-test.c
from direct field assignment to indirect initialization via the
CTLTBL_ENTRY_XXX() macros introduced in the previous patch. This
demonstrates the macros in real usage and validates their correctness
against the existing test suite.
The following macro variants are exercised:
- CTLTBL_ENTRY_VNMR(var, name, mode, min, max)
Used for the majority of test cases that initialize a standard int
entry with range checking. The macro automatically selects the
range-checking handler (proc_dointvec_minmax for int) based on the
variable type and computes maxlen via sizeof():
Before:
struct ctl_table table = {
.procname = "foo",
.data = &data,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE_HUNDRED,
};
After:
struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
- CTLTBL_ENTRY_VNMHR(var, name, mode, handler, min, max)
Used for the NULL data test case, passing SYSCTL_NULL as the data
argument and proc_dointvec as the explicit handler:
Before:
struct ctl_table null_data_table = {
.procname = "foo",
.data = NULL,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE_HUNDRED,
};
After:
struct ctl_table null_data_table = CTLTBL_ENTRY_VNMHR(
SYSCTL_NULL, "foo", 0644, proc_dointvec,
SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
- CTLTBL_ENTRY_VNMHRL(var, name, mode, handler, min, max, maxlen)
Used for the maxlen=0 test case, passing an explicit maxlen of 0 to
override the default sizeof()-based value:
Before:
struct ctl_table data_maxlen_unset_table = {
.procname = "foo",
.data = &data,
.maxlen = 0,
.mode = 0644,
.proc_handler = proc_dointvec,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE_HUNDRED,
};
After:
struct ctl_table data_maxlen_unset_table = CTLTBL_ENTRY_VNMHRL(
data, "foo", 0644, proc_dointvec,
SYSCTL_ZERO, SYSCTL_ONE_HUNDRED, 0);
New tests are added to cover the remaining macro variants:
- CTLTBL_ENTRY_V(var)
Verifies that the macro initializes .procname to the variable name,
.mode to 0444, .data to &var, .maxlen to sizeof(var), and selects
proc_dointvec as the handler for an int variable.
- CTLTBL_ENTRY_VM(var, mode)
Verifies that the macro initializes .procname to the variable name,
.mode to 0644, .data to &var, .maxlen to sizeof(var), and selects
proc_dointvec as the handler for an int variable.
- CTLTBL_ENTRY_VMR(var, mode, min, max)
Verifies that the macro initializes .procname to the variable name,
.mode to 0644, .data to &var, .maxlen to sizeof(var),
and auto-selecting proc_dointvec_minmax as the handler.
- CTLTBL_ENTRY_VN(var, name)
Verifies that the macro initializes .procname to the custom name while
keeping .mode at 0444 and auto-selecting the handler.
- CTLTBL_ENTRY_VNM(name, var, mode)
Verifies that the macro initializes .procname and .mode as specified
while auto-selecting the handler.
- CTLTBL_ENTRY_VNMH(var, name, mode, handler)
Verifies that the macro initializes all fields including an explicitly
provided proc_handler.
All tests pass:
[13:53:49] ================ sysctl_test (16 subtests) =================
[13:53:49] [PASSED] sysctl_test_api_dointvec_null_tbl_data
[13:53:49] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset
[13:53:49] [PASSED] sysctl_test_api_dointvec_table_len_is_zero
[13:53:49] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set
[13:53:49] [PASSED] sysctl_test_dointvec_read_happy_single_positive
[13:53:49] [PASSED] sysctl_test_dointvec_read_happy_single_negative
[13:53:49] [PASSED] sysctl_test_dointvec_write_happy_single_positive
[13:53:49] [PASSED] sysctl_test_dointvec_write_happy_single_negative
[13:53:49] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min
[13:53:49] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_v
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vm
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vmr
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vn
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vnm
[13:53:49] [PASSED] sysctl_test_api_ctltbl_entry_vnmh
[13:53:49] =================== [PASSED] sysctl_test ===================
[13:53:49] ============================================================
[13:53:49] Testing complete. Ran 16 tests: passed: 16
Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
kernel/sysctl-test.c | 240 +++++++++++++++++++++++++------------------
1 file changed, 141 insertions(+), 99 deletions(-)
diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index 92f94ea28957..410e6ce32ee1 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -15,20 +15,14 @@
*/
static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
{
- struct ctl_table null_data_table = {
- .procname = "foo",
- /*
- * Here we are testing that proc_dointvec behaves correctly when
- * we give it a NULL .data field. Normally this would point to a
- * piece of memory where the value would be stored.
- */
- .data = NULL,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ /*
+ * Here we are testing that proc_dointvec behaves correctly when
+ * we give it a NULL .data field. Normally this would point to a
+ * piece of memory where the value would be stored.
+ */
+ struct ctl_table null_data_table = CTLTBL_ENTRY_VNMHR(
+ SYSCTL_NULL, "foo", 0644, proc_dointvec,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
/*
* proc_dointvec expects a buffer in user space, so we allocate one. We
* also need to cast it to __user so sparse doesn't get mad.
@@ -66,19 +60,14 @@ static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
{
int data = 0;
- struct ctl_table data_maxlen_unset_table = {
- .procname = "foo",
- .data = &data,
- /*
- * So .data is no longer NULL, but we tell proc_dointvec its
- * length is 0, so it still shouldn't try to use it.
- */
- .maxlen = 0,
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ /*
+ * So .data is no longer NULL, but we tell proc_dointvec its
+ * length is 0, so it still shouldn't try to use it.
+ */
+ struct ctl_table data_maxlen_unset_table = CTLTBL_ENTRY_VNMHRL(
+ data, "foo", 0644, proc_dointvec,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED, 0);
+
void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
GFP_USER);
size_t len;
@@ -113,15 +102,8 @@ static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo",
+ 0644, SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
GFP_USER);
/*
@@ -147,15 +129,8 @@ static void sysctl_test_api_dointvec_table_read_but_position_set(
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
GFP_USER);
/*
@@ -182,15 +157,8 @@ static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t len = 4;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, len, GFP_USER);
@@ -213,15 +181,8 @@ static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t len = 5;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, len, GFP_USER);
@@ -242,15 +203,8 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
{
int data = 0;
/* Good table. */
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
char input[] = "9";
size_t len = sizeof(input) - 1;
loff_t pos = 0;
@@ -272,15 +226,8 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
{
int data = 0;
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
char input[] = "-9";
size_t len = sizeof(input) - 1;
loff_t pos = 0;
@@ -304,15 +251,8 @@ static void sysctl_test_api_dointvec_write_single_less_int_min(
struct kunit *test)
{
int data = 0;
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t max_len = 32, len = max_len;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
@@ -342,15 +282,8 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
struct kunit *test)
{
int data = 0;
- struct ctl_table table = {
- .procname = "foo",
- .data = &data,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE_HUNDRED,
- };
+ struct ctl_table table = CTLTBL_ENTRY_VNMR(data, "foo", 0644,
+ SYSCTL_ZERO, SYSCTL_ONE_HUNDRED);
size_t max_len = 32, len = max_len;
loff_t pos = 0;
char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
@@ -367,6 +300,109 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
}
+/*
+ * Test CTLTBL_ENTRY_V: variable name used as procname, mode 0444,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_v(struct kunit *test)
+{
+ int foo = 123;
+ struct ctl_table table = CTLTBL_ENTRY_V(foo);
+
+ KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+ KUNIT_EXPECT_EQ(test, (umode_t)0444, table.mode);
+ KUNIT_EXPECT_PTR_EQ(test, &foo, table.data);
+ KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+ KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+ table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VM: variable name used as procname, custom mode,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vm(struct kunit *test)
+{
+ int foo = 123;
+ struct ctl_table table = CTLTBL_ENTRY_VM(foo, 0644);
+
+ KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+ KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+ KUNIT_EXPECT_PTR_EQ(test, &foo, table.data);
+ KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+ KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+ table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VMR: variable name used as procname, custom mode,
+ * auto-selected range checkint handler (proc_dointvec_minmax for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vmr(struct kunit *test)
+{
+ int foo = 123;
+ struct ctl_table table = CTLTBL_ENTRY_VMR(foo, 0644, SYSCTL_ZERO,
+ SYSCTL_ONE_HUNDRED);
+
+ KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+ KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+ KUNIT_EXPECT_PTR_EQ(test, &foo, table.data);
+ KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+ KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec_minmax,
+ table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VN: custom procname, mode 0444,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vn(struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = CTLTBL_ENTRY_VN(data, "foo");
+
+ KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+ KUNIT_EXPECT_EQ(test, (umode_t)0444, table.mode);
+ KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
+ KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+ KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+ table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VNM: custom procname and mode,
+ * auto-selected handler (proc_dointvec for int).
+ */
+static void sysctl_test_api_ctltbl_entry_vnm(struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = CTLTBL_ENTRY_VNM(data, "foo", 0644);
+
+ KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+ KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+ KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
+ KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+ KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+ table.proc_handler);
+}
+
+/*
+ * Test CTLTBL_ENTRY_VNMH: custom procname, mode and explicit handler.
+ */
+static void sysctl_test_api_ctltbl_entry_vnmh(struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = CTLTBL_ENTRY_VNMH(data, "foo", 0644,
+ proc_dointvec);
+
+ KUNIT_EXPECT_STREQ(test, "foo", table.procname);
+ KUNIT_EXPECT_EQ(test, (umode_t)0644, table.mode);
+ KUNIT_EXPECT_PTR_EQ(test, &data, table.data);
+ KUNIT_EXPECT_EQ(test, sizeof(int), table.maxlen);
+ KUNIT_EXPECT_PTR_EQ(test, (proc_handler *)proc_dointvec,
+ table.proc_handler);
+}
+
static struct kunit_case sysctl_test_cases[] = {
KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
@@ -378,6 +414,12 @@ static struct kunit_case sysctl_test_cases[] = {
KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
+ KUNIT_CASE(sysctl_test_api_ctltbl_entry_v),
+ KUNIT_CASE(sysctl_test_api_ctltbl_entry_vm),
+ KUNIT_CASE(sysctl_test_api_ctltbl_entry_vmr),
+ KUNIT_CASE(sysctl_test_api_ctltbl_entry_vn),
+ KUNIT_CASE(sysctl_test_api_ctltbl_entry_vnm),
+ KUNIT_CASE(sysctl_test_api_ctltbl_entry_vnmh),
{}
};
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread