mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Wen Yang <wen.yang@linux.dev>
To: Joel Granados <joel.granados@kernel.org>,
	David Laight <david.laight.linux@gmail.com>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context
Date: Wed, 11 Nov 2026 01:45:15 +0800	[thread overview]
Message-ID: <e3c7bf98-64f9-4c29-b300-f54b7a4813d0@linux.dev> (raw)
In-Reply-To: <avGVyB9erjZl8zIO@hobbes>



On 11/9/26 18:07, Joel Granados wrote:
> On Sat, Nov 07, 2026 at 05:05:07PM +0000, David Laight wrote:
>> On Sat, 7 Nov 2026 16:27:39 +0100
>> Joel Granados <joel.granados@kernel.org> wrote:
>>
>>> On Mon, Nov 02, 2026 at 11:40:40PM +0800, Wen Yang wrote:
>>>>
>>>>
>>>> On 10/29/26 18:58, David Laight wrote:
>>>>> On Thu, 29 Oct 2026 00:34:39 +0800
>>>>> wen.yang@linux.dev wrote:
>>>>>    
>>>>>> From: Wen Yang <wen.yang@linux.dev>
>>>>>>
>>>>>> struct ctl_table describes an entry with data, maxlen and, until this
>>>>>> series, two untyped misc pointers in extra1 and extra2. Those two pointers
>>>>>> have accumulated at least seven unrelated jobs: a fixed bound, a bound read
>>>>>> through a pointer on every call, an object or namespace pointer, a small
>>>>>> integer cookie encoded into the pointer, a lock signal, NULL standing for
>>>>>> "no bound", and a value the core itself casts to unsigned int while
>>>>>> validating a u8 range. Nothing in the entry says which job applies, so
>>>>>> readers infer it from the handler, and the core gets it wrong where it
>>>>>> treats extra1 as an unsigned int.
>>> <--- snip --->
>>>>>> (~574 new min/max initializers) instead of pointing at constants. The
>>>>>> sysctl ABI is unchanged; the parport spintime minimum is corrected as
>>>>>> described above.
>>>>>>
>>>>>> The cost is 8 bytes per ctl_table entry on 64-bit (56 -> 64) and 4 bytes on
>>>>>> 32-bit, because priv is appended; the range read path gains no indirection
>>>>>> and the migrated entries no longer dereference a bound pointer.
>>>>>
>>>>> Can you remove the little-used 'priv' parameter by making the extra
>>>>> information just follow the table entry (nested structure) and have
>>>>> the relevant functions use container_of() to get the outer structure
>>>>> and extra values?
>>>>>    
>>>>
>>>>
>>>> Hi David, Hi Joel,
>>>>
>>>> I reworked the context side of the series so that priv is gone entirely;
>>>> and the final tree has no per-entry context pointer.
>>>> A small helper recovers the enclosing object, but it needs the slot
>>>> index from the caller:
>>>>
>>>>    #define sysctl_table_container(entry, type, member, index) ({       \
>>>>            static_assert(__same_type(*(entry),                         \
>>>>                                      typeof_member(type, member[0])),  \
>>>>                          "pointer type mismatch");                     \
>>>>            (type *)((void *)(entry) - offsetof(type, member) -         \
>>>>                     (size_t)(index) * sizeof(*(entry)));               \
>>>>    })
>>>>
>>>> The reason plain container_of() is not enough is that the core keeps the
>>>> table as a contiguous array.  For
>>>>
>>>>    struct addrconf_sysctl_table {
>>>>            struct addrconf_sysctl_ctx context;
>>>>            struct ctl_table table[];
>>>>    };
>>>>
>>>> the handler receives an interior entry, and
>>>>
>>>>    container_of(ctl, struct addrconf_sysctl_table, table)
>>>>        == (char *)outer + index * sizeof(struct ctl_table)
>>>>
>>>> for ctl == &outer->table[index].  It only returns the outer object for
>>>> index 0.  Passing the index explicitly keeps the original registered
>>>> entry usable:
>>>>
>>>>    static int addrconf_sysctl_forward(const struct ctl_table *ctl, ...)
>>>>    {
>>>>            struct addrconf_sysctl_table *t =
>>>>                    sysctl_table_container(ctl, struct addrconf_sysctl_table,
>>>>                                           table, ADDRCONF_SYSCTL_FORWARDING);
>>>>            const struct addrconf_sysctl_ctx *ctx = &t->context;
>>>>            ...
>>>>    }
>>>>
>>>> Shared handlers get one wrapper per slot; the common body does not need
>>>> to know its position:
>>>>
>>>>    #define DEVINET_CONF_WRAPPER(attr)                                  \
>>>>    static int devinet_conf_##attr(const struct ctl_table *ctl, ...)    \
>>>>    {                                                                   \
>>>>            struct devinet_sysctl_table *t =                            \
>>>>                    sysctl_table_container(ctl,                         \
>>>>                            struct devinet_sysctl_table, devinet_vars,  \
>>>>                            IPV4_DEVCONF_##attr - 1);                   \
>>>>            return devinet_conf_proc_common(ctl, &t->context, ...);     \
>>>>    }
>>>
>>> Two questions before seeing the actual implementation of this.
>>>
>>> 1. This means that we will have to add more macros to the sysctl
>>>     callers? There is a clear signal in [1] against overuse of macros.
>>> 2. I understand that you are doing this to avoid having priv. Is it to
>>>     save memory?  Yes. priv is a void* and we are also trying to get rid
>>>     of these. But this changes removes the use of two void pointers and
>>>     replaces it with just one.
>>
>> The idea behind removing the 'void *priv' is mostly to save memory.
>> There are a lot of systctl and hardly any need it.
>>
>> This solution does look nasty.
>> I suggested another where the size of the array entries was passed in
>> by the caller.

Hi Joel, David,

> That would mean changing all the proc handler signature. Right?
> 

Thanks. With David's stride approach, the signature could stay unchanged:

struct  nf_log_sysctl_entry {
	struct ctl_table ct;
	struct net *net;
	int family;
};

static int nf_log_handler(const struct ctl_table *table, int write,
			  void *buffer, size_t *lenp, loff_t *ppos)
{
	const struct nf_log_sysctl_entry *entry =
		container_of(table, struct nf_log_sysctl_entry, ctl);

	return nf_log_common(table, entry->net, entry->family,
			     write, buffer, lenp, ppos);
}

Proposed regisger API, not implemented yet:
	regisge_net_sysctl_stride(net, "net/netfilter/nf_log",
				  entries, ARRAY_SIZE(entries),
				  sizeof(entries[0]));


>> If that works it would allow the per-value function use container_of()
>> to get extra fields.
> Would this mean that the callers would have to have to keep track of the
> ctl_table offsets? in other words the sysctl indices and the caller's
> indices would have to be synced. right? 


   static const struct ctl_table *
   sysctl_entry_at(const struct ctl_table_header *head, size_t i)
   {
           return (const struct ctl_table *)
                   ((const char *)head->ctl_table +
                    i * head->ctl_table_stride);
   }

Table walks would retain the index:

   for (i = 0; i < head->ctl_table_size; i++) {
           entry = sysctl_entry_at(head, i);
           node = &head->node[i];
           ...
   }

Insertion/deletion would take the node directly. Readdir would retain
its RB-tree node cursor, recovering each entry through that node's header:

     entry = sysctl_entry_at(node->header, node - node->header->node);

No entry - head->ctl_table, caller slot constants or per-slot wrappers.
RB-tree order remains unchanged. Old APIs and generated directory/link
tables use sizeof(struct ctl_table).

The two mechanisms have existing precedents:

   /* include/linux/bsearch.h: explicit element size */
   pivot = base + (num >> 1) * size;
   result = cmp(key, pivot);

   /* include/linux/hwmon-sysfs.h: extended descriptor */
   struct sensor_device_attribute {
           struct device_attribute dev_attr;
           int index;
   };

   /* drivers/hwmon/adcxx.c: ordinary device_attribute callback */
   const struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);

hwmon registers &ad_input[i].dev_attr via device_create_file(); the core
passes that same base descriptor to the callback. These are precedents
for explicit-size traversal and embedded callback context separately,
not an existing strided sysctl registration API.

I agree that my fixed-slot solution adds maintenance overhead. Would
it make sense to explore this in three small steps?

   1. Entry-access helpers, unchanged packed layout.
   2. Header stride and compatible registration wrappers.
   3. nf_log conversion and code/memory comparison.

I would appreciate your thoughts on whether this is worth pursuing.


Thanks,
Wen


>>
>> David
>>
> 
> 
> 
>>>
>>> Best
>>>
>>> [1] https://lore.kernel.org/CAHk-=wjUmLrJd4KM4xvJHUN4P0r8oSu5LyvG4JipnhxizBrE9Q@mail.gmail.com
>>>>
>>>> Single-entry tables simply use slot 0:
>>>>
>>>>    struct devinet_forward_table *t =
>>>>            sysctl_table_container(ctl, struct devinet_forward_table, table,
>>>> 0);
>>>>
>>>>
>>>> The series is 20 commits over 134 files, with no priv at any point.  If
>>>> you would prefer a different shape for the helper or the wrappers, I am
>>>> happy to adjust it.
>>>>
>>>>
>>>> Thanks,
>>>> Wen
>>>>    
>>>>> David
>>>>>    
>>

  reply	other threads:[~2026-11-10 17:45 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28 16:34 wen.yang
2026-10-28 16:34 ` [RFC PATCH 01/39] " wen.yang
2026-10-28 21:09   ` David Laight
2026-11-07 17:43   ` Joel Granados
2026-10-28 16:34 ` [RFC PATCH 02/39] test_sysctl: convert bounds to numeric values wen.yang
2026-10-28 16:34 ` [RFC PATCH 03/39] sysctl: add KUnit coverage for numeric bounds and handlers wen.yang
2026-10-28 16:34 ` [RFC PATCH 04/39] sysctl: cover runtime ranges and context lifecycles in KUnit wen.yang
2026-10-28 16:34 ` [RFC PATCH 05/39] sysctl: cover priv in KUnit and the debug test module wen.yang
2026-10-28 16:34 ` [RFC PATCH 06/39] sysctl: convert core table bounds to numeric values wen.yang
2026-10-28 16:34 ` [RFC PATCH 07/39] sysctl: store architecture sysctl bounds by value wen.yang
2026-10-28 16:34 ` [RFC PATCH 08/39] sysctl: store crypto " wen.yang
2026-10-28 16:34 ` [RFC PATCH 09/39] sysctl: store device " wen.yang
2026-10-28 16:34 ` [RFC PATCH 10/39] sysctl: store filesystem " wen.yang
2026-10-28 16:34 ` [RFC PATCH 11/39] sysctl: store message-queue " wen.yang
2026-10-28 16:34 ` [RFC PATCH 12/39] sysctl: store kernel " wen.yang
2026-10-28 16:34 ` [RFC PATCH 13/39] sysctl: store memory-management " wen.yang
2026-10-28 16:34 ` [RFC PATCH 14/39] sysctl: store IPv6 scalar " wen.yang
2026-10-28 16:34 ` [RFC PATCH 15/39] sysctl: store netfilter " wen.yang
2026-10-28 16:34 ` [RFC PATCH 16/39] sysctl: store RPC " wen.yang
2026-10-28 16:34 ` [RFC PATCH 17/39] sysctl: store remaining network " wen.yang
2026-10-28 16:34 ` [RFC PATCH 18/39] sysctl: store security " wen.yang
2026-10-28 16:34 ` [RFC PATCH 19/39] io_uring: store its " wen.yang
2026-10-28 16:34 ` [RFC PATCH 20/39] sysctl: refresh dynamic scheduler and PID bounds per call wen.yang
2026-10-28 16:35 ` [RFC PATCH 21/39] sysctl: refresh device-dependent " wen.yang
2026-10-28 16:35 ` [RFC PATCH 22/39] sysctl: refresh namespace IPC " wen.yang
2026-10-28 16:35 ` [RFC PATCH 23/39] yama: preserve scope locking with numeric sysctl bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 24/39] net: refresh core sysctl limits without bound pointers wen.yang
2026-10-28 16:35 ` [RFC PATCH 25/39] sctp: resolve numeric ranges from the current namespace wen.yang
2026-10-28 16:35 ` [RFC PATCH 26/39] rds: preserve live reconnect limits with numeric bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 27/39] ipv4: preserve live and vector bounds with numeric sysctls wen.yang
2026-10-28 16:35 ` [RFC PATCH 28/39] net: refresh fragment and remaining dynamic sysctl bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 29/39] neighbour: derive sysctl ownership independently of bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 30/39] net: resolve sysctl owners from their backing data wen.yang
2026-10-28 16:35 ` [RFC PATCH 31/39] ipvs: derive sysctl ownership from numeric storage wen.yang
2026-10-28 16:35 ` [RFC PATCH 32/39] sysctl: remove obsolete nr_open bound variables wen.yang
2026-10-28 16:35 ` [RFC PATCH 33/39] ipv6: move address sysctl context to priv wen.yang
2026-10-28 16:35 ` [RFC PATCH 34/39] ipv4: move device " wen.yang
2026-10-28 16:35 ` [RFC PATCH 35/39] netfilter: move logger " wen.yang
2026-10-28 16:35 ` [RFC PATCH 36/39] parport: move device " wen.yang
2026-10-28 16:35 ` [RFC PATCH 37/39] net: move namespace " wen.yang
2026-10-28 16:35 ` [RFC PATCH 38/39] arm64: move vector-length " wen.yang
2026-10-28 16:35 ` [RFC PATCH 39/39] sysctl: remove the extra pointers and the compatibility bounds read wen.yang
2026-10-29 10:58 ` [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context David Laight
2026-11-02 15:40   ` Wen Yang
2026-11-02 16:54     ` David Laight
2026-11-04 18:32       ` Wen Yang
2026-11-07 15:27     ` Joel Granados
2026-11-07 17:05       ` David Laight
2026-11-09 10:07         ` Joel Granados
2026-11-10 17:45           ` Wen Yang [this message]
2026-11-09  9:25     ` Joel Granados
2026-11-07 21:26 ` Joel Granados

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e3c7bf98-64f9-4c29-b300-f54b7a4813d0@linux.dev \
    --to=wen.yang@linux.dev \
    --cc=david.laight.linux@gmail.com \
    --cc=joel.granados@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®