On Wed, Jul 23, 2025 at 11:01 AM Thierry Reding <thierry.red...@gmail.com> wrote: > > On Tue, Jul 22, 2025 at 04:08:09PM +0200, Greg Kroah-Hartman wrote: > > On Tue, Jul 22, 2025 at 03:56:40PM +0200, Thierry Reding wrote: > > > On Sat, Jul 19, 2025 at 08:52:41AM +0200, Greg Kroah-Hartman wrote: > > > > On Fri, Jul 18, 2025 at 03:49:37PM +0200, Thierry Reding wrote: > > > > > On Thu, Jul 17, 2025 at 02:11:41PM +0200, Greg Kroah-Hartman wrote: > > > > > > On Thu, Jul 17, 2025 at 12:32:34PM +0200, Thierry Reding wrote: > > > [...] > > > > > struct syscore; > > > > > > > > > > struct syscore_ops { > > > > > int (*suspend)(struct syscore *syscore); > > > > > void (*resume)(struct syscore *syscore); > > > > > void (*shutdown)(struct syscore *syscore); > > > > > }; > > > > > > > > > > struct syscore { > > > > > const struct syscore_ops *ops; > > > > > struct list_head node; > > > > > }; > > > > > > > > > > Is that what you had in mind? > > > > > > > > I missed the list_head, so yes, this would be better, but don't pass > > > > back the syscore structure, how about just a void * instead, making the > > > > whole container_of() stuff go away? > > > > > > Yeah, that's a possibility. I personally don't like passing the void * > > > around because it's easier to make mistakes that way. I also find it > > > unintuitive because it doesn't immediately show you what the functions > > > expect. > > > > > > My understanding is that the container_of() should get optimized away > > > most of the time, so there aren't any obvious downsides that I can see. > > > > container_of() is just pointer math, but a cast is even faster :) > > > > > But I don't feel very strongly, so if you have a strong preference for > > > void pointers, I can do that. > > > > That's what you really want to have here, it's a syscore data type > > thing, that the callback wants to reference. Just like a irqrequest_t > > function passes back a void * that the handler "knows" how to deal with > > properly. > > IRQ handlers are different, though, because you pass the void * data > when you register the interrupt. That void * then gets stored and passed > to the handler when the interrupt is processed. > > We'd have to change it to something like this: > > struct syscore_ops { > /* parameters now changed to driver-specific data */ > int (*suspend)(void *data); > void (*resume)(void *data); > void (*shutdown)(void *data); > }; > > struct syscore { > const struct syscore_ops *ops; > struct list_head node; > /* NEW driver-specific data */ > void *data; > };
I like this more than the original, but I would do struct syscore_ops_ops { int (*suspend)(void *data); void (*resume)(void *data); void (*shutdown)(void *data); }; struct syscore_ops { struct list_head node; const struct syscore_ops_ops *ops; void *data; }; and change register_syscore_ops() to take three arguments, the struct syscore_ops pointer, the (constified) struct syscore_ops_ops one, and the (void *) data one. Note that it is not necessary to change the signature of unregister_syscore_ops() in this case. > It ends up increasing the syscore structure's size, about 33%, though > given that there aren't a lot of these that's probably negligible. That's not a problem IMV. > What I think is a bit more unnatural about it in this case is that we > embed the struct syscore into some driver-private data anyway so that > it becomes per instance, and then we have a circular reference: > > foo->syscore.ops = &foo_syscore_ops; > foo->syscore.data = foo; That depends because "data" need not be "foo" in all cases, but also see above. If the initialization of struct syscore_ops is all done by register_syscore_ops(), it doesn't look circular any more. > Which looks kind of weird. Alternatively I suppose we could completely > rework it and make register_syscore_ops() allocate struct syscore, and > hide the internals from drivers completely: > > err = register_syscore(&foo_syscore_ops, foo); > > With that it may be problematic that register_syscore() can now fail. Yes, that might be a problem.