kevent: core files.
Subsystems (8)
RFC 1/4 kevent: core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialisation
- notification state machines
It might also inlclude parts from other subsystem (like network related syscalls so it is possible that it will not compile without other patches applied).
arch/i386/kernel/syscall_table.S +4 -0
@@ -316,3 +316,7 @@ ENTRY(sys_call_table) .long sys_sync_file_range .long sys_tee /* 315 */ .long sys_vmsplice + .long sys_aio_recv + .long sys_aio_send + .long sys_aio_sendfile + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +4 -0
@@ -696,4 +696,8 @@ #endif .quad sys_sync_file_range .quad sys_tee .quad compat_sys_vmsplice + .quad sys_aio_recv + .quad sys_aio_send + .quad sys_aio_sendfile + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +5 -1
@@ -322,10 +322,14 @@ #define __NR_splice 313#define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316 +#define __NR_aio_recv 317 +#define __NR_aio_send 318 +#define __NR_aio_sendfile 319 +#define __NR_kevent_ctl 320#ifdef __KERNEL__ -#define NR_syscalls 317 +#define NR_syscalls 321/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/socket.h +2 -0
@@ -49,4 +49,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31 +#define SO_ASYNC_SOCK 34 +#endif /* _ASM_SOCKET_H */
include/asm-x86_64/unistd.h +9 -2
@@ -617,11 +617,18 @@ #define __NR_sync_file_range 277 __SYSCALL(__NR_sync_file_range, sys_sync_file_range)#define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice) +#define __NR_aio_recv 279 +__SYSCALL(__NR_aio_recv, sys_aio_recv) +#define __NR_aio_send 280 +__SYSCALL(__NR_aio_send, sys_aio_send) +#define __NR_aio_sendfile 281 +__SYSCALL(__NR_aio_sendfile, sys_aio_sendfile) +#define __NR_kevent_ctl 282 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_vmsplice - +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS/* user-visible error numbers are in the range -1 - -4095 */
include/linux/kevent.h new file +263 -0
263 lines not on this page · raw message
include/linux/kevent_storage.h new file +12 -0
@@ -0,0 +1,12 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +unsigned int qlen;/* Number of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +4 -0
@@ -587,4 +587,8 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_aio_recv (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_aio_send (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_aio_sendfile (int ctl_fd,int fd,int s,size_t size,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,void __user *buf);#endif
init/Kconfig +2 -0
@@ -185,6 +185,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -36,6 +36,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
kernel/kevent/Kconfig new file +57 -0
57 lines not on this page · raw message
kernel/kevent/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +obj-y := kevent.o kevent_user.o kevent_init.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
kernel/kevent/kevent.c new file +260 -0
260 lines not on this page · raw message
kernel/kevent/kevent_init.c new file +85 -0
85 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +728 -0
728 lines not on this page · raw message
kernel/sys_ni.c +5 -0
@@ -121,6 +121,11 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_aio_recv); +cond_syscall (sys_aio_send); +cond_syscall (sys_aio_sendfile); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
Signature · 1 line
Evgeniy Polyakov
kernel/kevent/kevent.c line 236
Quoted 11 lines · Evgeniy Polyakov
kernel/kevent/kevent.c new file +11
-0+struct kevent *kevent_alloc (gfp_t mask) +{ +struct kevent *k; + +if (kevent_cache) + k =kmem_cache_alloc (kevent_cache, mask); +else + k =kzalloc (sizeof (struct kevent ), mask); + +return k; +}
What's this for? Why would kevent_cache be NULL? Note that you can use kmem_cache_zalloc() for fixed size allocations that need to be zeroed.
kernel/kevent/kevent.c line 240
Quoted 4 lines · Evgeniy Polyakov
kernel/kevent/kevent.c new file +4
-0+ +void kevent_free (struct kevent *k) +{ +memset (k,0xab ,sizeof (struct kevent ));
Why is slab poisoning not sufficient?
Quoted 5 lines
kernel/kevent/kevent.c new file +5
-0+if (kevent_cache) +kmem_cache_free (kevent_cache, k); +else +kfree (k); +}
Quoted 15 lines · Pekka Enberg
kernel/kevent/kevent.c line 236
Quoted 11 lines · Evgeniy Polyakov
kernel/kevent/kevent.c new file +11
-0+struct kevent *kevent_alloc (gfp_t mask) +{ +struct kevent *k; + +if (kevent_cache) + k =kmem_cache_alloc (kevent_cache, mask); +else + k =kzalloc (sizeof (struct kevent ), mask); + +return k; +}What's this for? Why would
kevent_cachebe NULL? Note that you can usekmem_cache_zalloc()for fixed size allocations that need to be zeroed.
It can work without cache at all, i.e. if cache creation fails. Well, it can be removed of course, since it does not hurt anything.
Quoted 7 lines · Evgeniy Polyakov
kernel/kevent/kevent.c line 240
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+ +void kevent_free (struct kevent *k) +{ +memset (k,0xab ,sizeof (struct kevent ));Why is slab poisoning not sufficient?
Since that pointer is always known to be poisoned no matter if kernel debugging option is turned on or off.
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerFrom: Evgeniy Polyakov <[email protected]>
Date: Sun, 9 Jul 2006 17:24:46 +0400
Quoted 11 lines
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialisation
- notification state machinesIt might also inlclude parts from other subsystem (like network related syscalls so it is possible that it will not compile without other patches applied).
Signed-off-by Evgeniy Polyakov <[email protected]>
I like this work a lot, as I've stated before. The data structures look like they will scale well and it takes care of all the limitations that networking in particular seems to have in this area.
I have to say that the user API is not the nicest in the world. Yet, at the same time, I cannot think of a better one :)
Please, remove some grot such as this:
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+if (kevent_cache) + k =kmem_cache_alloc (kevent_cache, mask); +else + k =kzalloc (sizeof (struct kevent ), mask);
...
kernel/kevent/kevent.c line 245
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+if (kevent_cache) +kmem_cache_free (kevent_cache, k); +else +kfree (k);
Instead, make this:
kernel/kevent/kevent.c line 255
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+ kevent_cache =kmem_cache_create ("kevent_cache" , +sizeof (struct kevent ),0 ,0 ,NULL ,NULL ); +if (!kevent_cache) + err = -ENOMEM ;
panic(). This is consistent with how other core subsystems handle SLAB cache creation failures.
I also think that if we accept this work, it should be first class citizen with no config options and no ifdefs scattered all over. Either this is how we do network AIO or it is not.
I've looked only briefly at Ulrich Drepper's AIO proposal in his OLS slides, although the DMA bits do not initially strike me as such a hot idea. I haven't wrapped my brain much around this new stuff, so I'm not going to touch on it much more just yet.
The practical advantage kevent has over any new proposal is that 1) implementation exists :) and 2) several types of test applications and performance measurements have been made against it which usually flushes out the worst design issues.
Quoted 13 lines · David Miller
From: Evgeniy Polyakov <[email protected]>
Date: Sun, 9 Jul 2006 17:24:46 +0400Quoted 10 lines
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialisation
- notification state machinesIt might also inlclude parts from other subsystem (like network related syscalls so it is possible that it will not compile without other patches applied).
Quoted 8 lines
Quoted 1 line
Signed-off-by Evgeniy Polyakov <[email protected]>I like this work a lot, as I've stated before. The data structures look like they will scale well and it takes care of all the limitations that networking in particular seems to have in this area.
I have to say that the user API is not the nicest in the world. Yet, at the same time, I cannot think of a better one :)
Hi David. I see you have a day of backlog mails processing :)
Quoted 14 lines
Please, remove some grot such as this:
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+if (kevent_cache) + k =kmem_cache_alloc (kevent_cache, mask); +else + k =kzalloc (sizeof (struct kevent ), mask);...
kernel/kevent/kevent.c line 245
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+if (kevent_cache) +kmem_cache_free (kevent_cache, k); +else +kfree (k);Instead, make this:
Quoted 7 lines
kernel/kevent/kevent.c line 255
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+ kevent_cache =kmem_cache_create ("kevent_cache" , +sizeof (struct kevent ),0 ,0 ,NULL ,NULL ); +if (!kevent_cache) + err = -ENOMEM ;
panic(). This is consistent with how other core subsystems handle SLAB cache creation failures.
Ok.
Quoted 8 lines
I also think that if we accept this work, it should be first class citizen with no config options and no ifdefs scattered all over. Either this is how we do network AIO or it is not.
I've looked only briefly at Ulrich Drepper's AIO proposal in his OLS slides, although the DMA bits do not initially strike me as such a hot idea. I haven't wrapped my brain much around this new stuff, so I'm not going to touch on it much more just yet.
Yes, his idea of dma alloc is extremely good.
I manage it with quite big overhead in kevent unfortunately. All other topics are fully covered with kevent (except nice userspace API of course :) )
Quoted 4 lines
The practical advantage kevent has over any new proposal is that 1) implementation exists :) and 2) several types of test applications and performance measurements have been made against it which usually flushes out the worst design issues.
I will clean code up and resubmit today.
Thank you.
Signature · 1 line
Evgeniy Polyakov
Quoted 2 lines
I like this work a lot, as I've stated before.
Yeah, me too. I think we're very close to having a workable system here. A few weeks of some restructuring and we all might be very happy.
Quoted 6 lines
The data structures
look like they will scale well and it takes care of all the limitations that networking in particular seems to have in this area.I have to say that the user API is not the nicest in the world. Yet, at the same time, I cannot think of a better one :)
I want to first focus on the event collection side of the API because I think we can definitely do better there :). I hope we all agree that there is huge value in having one place where an application can wait for notification from many different sources. If we get the collection side right we can later worry about generating events down the pipe from subsystems in a way that works best for them.
I'll sort of ramble about my thoughts here.
The easy part is fixing up the somewhat obfuscated collection call. Instead of coming in through a multiplexer that magically treats a void
* as a struct kevent_user_control followed by N ukevents (as specified in the kevent_user_control!) we'd turn it into a more explicit collection syscall:
int kevent_getevents(int event_fd, struct ukevent *events,
int min_events, int max_events,
struct timeval *timeout);
This would look a lot less nutty in strace. It lets apps specify if there is some minimum number of events they'd like the opportunity to process rather than waiting for the timeout to expire before the max number arrives. (the latter is what kevent_user_wait() does today). We can have the usual argument about whether *timeout is updated on a partial wake-up :).
That'd be a fine syscall collection interface, but we should try hard to explore being able to collect events without hitting the kernel.
Say we have a ring of event structs. AIO has this today, but it sort of gets it wrong because each event element doesn't specify whether it is owned by the kernel or userspace. (It really gets it wrong because it doesn't flush_dcache_page() after updating the ring via kmap(), but never mind that! No one actually uses this mmap() AIO ring.) In AIO today there is also a control struct mapped along with the ring that has head and tail pointers. We don't want to bounce that cacheline around.
net/socket/af_packet.c gets this right with it's tp_status member oftpacket_hdr.
So as the kernel generates events in the ring it only produces an event if the ownership field says that userspace has consumed it and in doing so it sets the ownership field to tell userspace that an event is waiting. userspace and the kernel now each follow their index around the ring as the ownership field lets them produce or consume the event at their index. Can someone tell me if the cache coherence costs of this are extreme? I'm hoping they're not.
So, great, glibc can now find pending events very quickly if they're waiting in the ring and can fall back to the collection syscall if it wants to wait and the ring is empty. If it consumes events via the syscall it increases its ring index by the number the syscall returned.
There's two things we should address: level events and the notion of only submitting as much as fits in the ring.
epoll and kevent both have the notion of an event type that always creates an event at the time of the collection syscall while the event source is on a ready list. Think of epoll calling ->poll(POLLOUT) for an empty socket buffer at every sys_epoll_wait() call. We can't have some source constantly spewing into the ring :/. We could fix this by the API requiring that level events can *only* be collected through the syscall interface. userspace could call into the collection syscall every N events collected through the ring, say. N would be tuned to amortize the syscall cost and still provide fairness or latency for the level sources. I'd be fine with that, especially when it's hidden off in glibc.
Today AIO only allows submission of as many events as there are space in the ring. It mostly does this so its completion can drop an event in the ring from any context. If we back away from this so that we can have long-lived source registration generate multiple edge events (and I think we want to!), we have to be kind of careful. A source could generate an event while the ring is full. The event could go in a list but if userspace is collecting events in userspace the kernel won't be told when there's space. We'd first have to check this ready list when later events are generated so that pending events on the list aren't overlooked. Userspace would also want to use the collection syscall as the ring empties. Neither seem hard.
So how does this sound? It wouldn't take me long to build this off of the current kevent patches. We could see how it looks..
- z
Quoted 18 lines · Zach Brown
Quoted 2 lines
I have to say that the user API is not the nicest in the world. Yet, at the same time, I cannot think of a better one :)
I want to first focus on the event collection side of the API because I think we can definitely do better there :). I hope we all agree that there is huge value in having one place where an application can wait for notification from many different sources. If we get the collection side right we can later worry about generating events down the pipe from subsystems in a way that works best for them.
I'll sort of ramble about my thoughts here.
The easy part is fixing up the somewhat obfuscated collection call. Instead of coming in through a multiplexer that magically treats a void
* as a structkevent_user_controlfollowed by N ukevents (as specified in thekevent_user_control!) we'd turn it into a more explicit collection syscall:
Quoted 3 lines
int kevent_getevents(int
event_fd, struct ukevent *events,
intmin_events, intmax_events,
struct timeval *timeout);
I used only one syscall for all operations, above syscall is essentially what kevent_user_wait() does.
Quoted 6 lines
This would look a lot less nutty in strace. It lets apps specify if there is some minimum number of events they'd like the opportunity to process rather than waiting for the timeout to expire before the max number arrives. (the latter is what
kevent_user_wait()does today). We can have the usual argument about whether *timeout is updated on a partial wake-up :).
Sure it can be moved as a different syscall.
Quoted 12 lines
That'd be a fine syscall collection interface, but we should try hard to explore being able to collect events without hitting the kernel.
Say we have a ring of event structs. AIO has this today, but it sort of gets it wrong because each event element doesn't specify whether it is owned by the kernel or userspace. (It really gets it wrong because it doesn't
flush_dcache_page()after updating the ring viakmap(), but never mind that! No one actually uses thismmap()AIO ring.) In AIO today there is also a control struct mapped along with the ring that has head and tail pointers. We don't want to bounce that cacheline around.
net/socket/af_packet.cgets this right with it'stp_statusmember oftpacket_hdr.
When kevent is ready, it is supposed to be "moved" into the userspace, if it is ready or not is detected through it's list entry for given list(or just dequeue it).
So there is no need to have a ring of all kevents and select ready from them (actually kevent has a list of all kevents, and it is possible to scan it and select for ready ones, but special ready list was created to speed that process up).
Quoted 8 lines
So as the kernel generates events in the ring it only produces an event if the ownership field says that userspace has consumed it and in doing so it sets the ownership field to tell userspace that an event is waiting. userspace and the kernel now each follow their index around the ring as the ownership field lets them produce or consume the event at their index. Can someone tell me if the cache coherence costs of this are extreme? I'm hoping they're not.
Quoted 4 lines
So, great, glibc can now find pending events very quickly if they're waiting in the ring and can fall back to the collection syscall if it wants to wait and the ring is empty. If it consumes events via the syscall it increases its ring index by the number the syscall returned.
It can get pending event from ready list - no need to scan the whole ring. If one wants to wait on special kevent, it is possible to get it from "main" list and check it's state.
But do we really need that functionality? If user created several kevents, it was not done just for the sake of kevent creation - user wants them back, so why he will wait only on special one?
I had an idea about priorities for the kevents, and it can be implemented as several ready lists though.
Quoted 14 lines
There's two things we should address: level events and the notion of only submitting as much as fits in the ring.
epoll and kevent both have the notion of an event type that always creates an event at the time of the collection syscall while the event source is on a ready list. Think of epoll calling ->poll(POLLOUT) for an empty socket buffer at every
sys_epoll_wait()call. We can't have some source constantly spewing into the ring :/. We could fix this by the API requiring that level events can *only* be collected through the syscall interface. userspace could call into the collection syscall every N events collected through the ring, say. N would be tuned to amortize the syscall cost and still provide fairness or latency for the level sources. I'd be fine with that, especially when it's hidden off in glibc.
Hmm, it looks like I'm lost here...
Each kevent can be modified and even reused at any time, no matter if it is in the ready list or not.
Quoted 11 lines
Today AIO only allows submission of as many events as there are space in the ring. It mostly does this so its completion can drop an event in the ring from any context. If we back away from this so that we can have long-lived source registration generate multiple edge events (and I think we want to!), we have to be kind of careful. A source could generate an event while the ring is full. The event could go in a list but if userspace is collecting events in userspace the kernel won't be told when there's space. We'd first have to check this ready list when later events are generated so that pending events on the list aren't overlooked. Userspace would also want to use the collection syscall as the ring empties. Neither seem hard.
There is no problem with lists lengths - when kevent is added, and queue length is enough, it is autmatically means that all lists will accept that kevent, since all kevents live simultaneously in several rings. There are no event generators - only existing, i.e. requested kevents are confirmed to be ready or not, so system behaves axactly how user asked it to work with provided requests.
For example inotify will allocate and add to the ring new events each time they fire, but kevent works in a different way (it's inode notification) - it checks if there are events for inode creation/removal and that events are marked as ready - thus no allocations, no problems with queues -system scales well and does not eat resources, but it has a problem, that not very much info can be delivered to user when event is ready (for example no filenames, only inode number can be transferred).
Quoted 2 lines
So how does this sound? It wouldn't take me long to build this off of the current kevent patches. We could see how it looks..
I especially like idea about world happinness in a week or so :)
Quoted 1 line
- z
Signature · 1 line
Evgeniy Polyakov
Quoted 7 lines
Quoted 4 lines
int kevent_getevents(int
event_fd, struct ukevent *events,
intmin_events, intmax_events,
struct timeval *timeout);I used only one syscall for all operations, above syscall is essentially what
kevent_user_wait()does.
Essentially, yes, but the differences are important. It's important to have a clear syscall interface instead of nesting through multiplexers.
And we should get the batching/latency inputs right. (I'm for both
min/max elements and arguably timeouts, but I could understand not wanting to go *that* far.)
Quoted 1 line
Hmm, it looks like I'm lost here...
Yeah, it seems my description might not have sunk in :). We're giving userspace a way to collect events without performing a system call.
Quoted 1 line
I especially like idea about world happinness in a week or so :)
A few weeks! :)
- z
Quoted 5 lines · Zach Brown
Quoted 5 lines
Quoted 4 lines
int kevent_getevents(int
event_fd, struct ukevent *events,
intmin_events, intmax_events,
struct timeval *timeout);
Quoted 8 lines
Quoted 2 lines
I used only one syscall for all operations, above syscall is essentially what
kevent_user_wait()does.Essentially, yes, but the differences are important. It's important to have a clear syscall interface instead of nesting through multiplexers.
And we should get the batching/latency inputs right. (I'm for both
min/max elements and arguably timeouts, but I could understand not wanting to go *that* far.)
I completely agree that existing kevent interface is not the best, so I'm opened for any suggestions.
Should kevent creation/removing/modification be separated too?
Quoted 4 lines
Quoted 1 line
Hmm, it looks like I'm lost here...
Yeah, it seems my description might not have sunk in :). We're giving userspace a way to collect events without performing a system call.
And why do we want this?
How glibc is supposed to determine, that some events already fired and such requests will return immediately, or for example how timer events will be managed?
Quoted 3 lines
Quoted 1 line
I especially like idea about world happinness in a week or so :)
A few weeks! :)
No matter after couple of millions of years of human evolution :)
Quoted 1 line
- z
Signature · 1 line
Evgeniy Polyakov
Quoted 4 lines
I completely agree that existing kevent interface is not the best, so I'm opened for any suggestions.
Should kevent creation/removing/modification be separated too?
Yeah, I think so.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Hmm, it looks like I'm lost here...
Yeah, it seems my description might not have sunk in :). We're giving userspace a way to collect events without performing a system call.
And why do we want this?
So that event collection can be very efficient.
Quoted 3 lines
How glibc is supposed to determine, that some events already fired and such requests will return immediately, or for example how timer events will be managed?
...
That was what my previous mail was all about!
- z
Quoted 6 lines · Zach Brown
Quoted 4 lines
I completely agree that existing kevent interface is not the best, so I'm opened for any suggestions.
Should kevent creation/removing/modification be separated too?Yeah, I think so.
So, I'm going to create kevent_create/destroy/control and kevent_get_events() Or any better names?
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Hmm, it looks like I'm lost here...
Yeah, it seems my description might not have sunk in :). We're giving userspace a way to collect events without performing a system call.
And why do we want this?
So that event collection can be very efficient.
Quoted 7 lines
Quoted 3 lines
How glibc is supposed to determine, that some events already fired and such requests will return immediately, or for example how timer events will be managed?
...
That was what my previous mail was all about!
Some events are impossible to create in userspace (like timer notification, which requires timer start and check when timer completed).
Actually all events are part of the kernel, since glibc does not have any knowledge about in-kernel state machines which are bound to appropriate kevents, so each kevent takes at least two syscall (create and get ready), and I do not see how, for exmple, glibc can avoid them when user requested POLLIN or similar event for network dataflow?
According to syscall speed on Linux, last time I checked empty syscall took about 100ns on AMD Athlon 3500+.
Quoted 1 line
- z
Signature · 1 line
Evgeniy Polyakov
Quoted 3 lines
So, I'm going to create kevent_create/destroy/control and
kevent_get_events()Or any better names?
Yeah, that sounds good.
Quoted 3 lines
Some events are impossible to create in userspace (like timer notification, which requires timer start and check when timer completed).
We're not talking about *creating* events in userspace, we're talking about checking for their completion events in the ring.
Quoted 2 lines
and get ready), and I do not see how, for exmple, glibc can avoid them when user requested POLLIN or similar event for network dataflow?
There are events that can be generated by kernel code paths as the event completes. Network sockets have the hooks to do this with SIGIO, it's very natural for the storage completion paths, etc. So that kernel code would update the ring which userspace could check. AIO does this today.
Userspace would still have to use the syscall to sleep waiting for new
events when the ring is empty.
Quoted 2 lines
According to syscall speed on Linux, last time I checked empty syscall took about 100ns on AMD Athlon 3500+.
Oh, sure, but still nice to avoid.
I'm mostly pursuing this because Ulrich seemed so insistent on it in his paper and talk. I will be very sad if we don't have aggressive glibc support for this generic event collection interface and so I want very much to keep him engaged. Ulrich, would you be satisfied if we didn't have the userspace mapped ring on the first pass and only had a collection syscall?
- z
Quoted 3 lines · Zach Brown
Ulrich, would you be satisfied if we didn't
have the userspace mapped ring on the first pass and only had a collection syscall?
I'm not the one to make a call but why rush things? Let's do it right from the start. Later changes can only lead to problems with users of the earlier interface.
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Quoted 9 lines · Ulrich Drepper
Quoted 3 lines · Zach Brown
Ulrich, would you be satisfied if we didn't
have the userspace mapped ring on the first pass and only had a collection syscall?I'm not the one to make a call but why rush things? Let's do it right from the start. Later changes can only lead to problems with users of the earlier interface.
Speaking of API design choices, I saw your OLS paper and was wondering if you were familiar with the Solaris port APIs* and, if so, you could please comment on how your proposed event channels are different/better.
* http://docs.sun.com/app/docs/doc/816-5168/6mbb3hrir?a=view
Signature · 1 line
Nicholas Miell <[email protected]>
Quoted 3 lines · Nicholas Miell
Speaking of API design choices, I saw your OLS paper and was wondering if you were familiar with the Solaris port APIs* and, if so, you could please comment on how your proposed event channels are different/better.
As far as it concerns kevents - userspace "ports" are just usual users of kevents, like timer notifications. Add another syscall to "complete" requested kevents and you get exactly Solaris ports.
It is fairly simple to implement on top of kevents, I just do not see immediate benefits from that.
Quoted 2 lines
Signature · 1 line
Nicholas Miell <[email protected]>
Signature · 1 line
Evgeniy Polyakov
Quoted 11 lines · Evgeniy Polyakov
Quoted 3 lines · Nicholas Miell
Speaking of API design choices, I saw your OLS paper and was wondering if you were familiar with the Solaris port APIs* and, if so, you could please comment on how your proposed event channels are different/better.
As far as it concerns kevents - userspace "ports" are just usual users of kevents, like timer notifications. Add another syscall to "complete" requested kevents and you get exactly Solaris ports.
It is fairly simple to implement on top of kevents, I just do not see immediate benefits from that.
Sorry, I wasn't talking about kevent, I was talking about the interfaces described in "The Need for Asynchronous, Zero-Copy Network I/O" by Ulrich Drepper -- specifically the ec_t type and related functions and the modifications to struct sigevent.
Signature · 1 line
Nicholas Miell <[email protected]>
Quoted 2 lines · Nicholas Miell
[...] and was wondering
if you were familiar with the Solaris port APIs* and,
I wasn't.
Quoted 2 lines
if so, you could
please comment on how your proposed event channels are different/better.
There indeed is not much difference. The differences are in the details. The way those ports are specified doesn't allow much room for further optimizations. E.g., the userlevel ring buffer isn't possible.
But mostly it's the same semantics. The ec_t type in my text is also
better a file descriptor since otherwise it cannot be transported via Unix stream sockets.
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Quoted 8 lines · Ulrich Drepper
Quoted 3 lines · Zach Brown
Ulrich, would you be satisfied if we didn't
have the userspace mapped ring on the first pass and only had a collection syscall?I'm not the one to make a call but why rush things? Let's do it right from the start. Later changes can only lead to problems with users of the earlier interface.
Btw, why do we want mapped ring of ready events?
If user requestd some event, he definitely wants to get them back when they are ready, and not to check and then get them?
Could you please explain more on this issue?
Quoted 3 lines
Signature · 1 line
??? Ulrich Drepper ??? Red Hat, Inc. ??? 444 Castro St ??? Mountain View, CA ???
Signature · 1 line
Evgeniy Polyakov
Quoted 4 lines · Evgeniy Polyakov
Btw, why do we want mapped ring of ready events?
If user requestd some event, he definitely wants to get them back when they are ready, and not to check and then get them?
Could you please explain more on this issue?
If of course makes no sense to enter the kernel to actually get the event. This should be done by storing the event in the ring buffer. I.e., there are two ways to get an event:
- with a syscall. This can report as many events at once as the caller provides space for. And no event which is reported in the run buffer should be reported this way
- if there is space, report it in the ring buffer. Yes, the buffer can be optional, then all events are reported by the system call.
So the use case would be like this:
wait_and_get_event:
is buffer empty ?
yes -> make syscall
no -> get event from bufferTo avoid races, the syscall needs to take a parameter indicating the last event checked out from the buffer. If in the meantime the kernel put another event in the buffer the syscall immediately returns. Similar to what we do in the futex syscall.
The question is how to best represent the ring buffer. Zach and some others had some ready responses in Ottawa. The important thing is to avoid cache line ping pong when possible.
Is the ring buffer absolutely necessary? Probably not. But it has the potential to help quite a bit. Don't look at the problem to solve in the context of heavy I/O operations when another syscall here and there doesn't matter. With this single event mechanism for every possible event the kernel can generate programming can look quite different. E.g., every read() call can implicitly we changed into an async read call followed by a user-level reschedule. This rescheduling allows another thread of execution to run while the read request is processed.
I.e., it's basically a setjmp() followed by a goto into the inner loop
to get the next event. And now suddenly the event notification mechanism really should be as fast as possible. If we submit basically every request asynchronously and are not creating dedicated threads for specific tasks anymore we
a) have a lot more event notifications
b) the probability of an event being reported when we want the receive the next one if higher (i.e., the case where no syscall vs syscall makes a difference)
Yes, all this will require changes in the way programs a written but we shouldn't limit the way we can write programs unnecessarily. I think that given increasing discrepancies in relative speed/latency of the peripherals and the CPU this is one possible solution to keep the CPUs busy without resorting to a gazillion separate threads in each program.
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Quoted 30 lines · Ulrich Drepper
Quoted 4 lines · Evgeniy Polyakov
Btw, why do we want mapped ring of ready events?
If user requestd some event, he definitely wants to get them back when they are ready, and not to check and then get them?
Could you please explain more on this issue?If of course makes no sense to enter the kernel to actually get the event. This should be done by storing the event in the ring buffer. I.e., there are two ways to get an event:
- with a syscall. This can report as many events at once as the caller provides space for. And no event which is reported in the run buffer should be reported this way
- if there is space, report it in the ring buffer. Yes, the buffer can be optional, then all events are reported by the system call.
So the use case would be like this:
wait_and_get_event:is buffer empty ?
yes -> make syscall no -> get event from buffer
Quoted 4 lines
To avoid races, the syscall needs to take a parameter indicating the last event checked out from the buffer. If in the meantime the kernel put another event in the buffer the syscall immediately returns. Similar to what we do in the futex syscall.
Couldn't this be done in a general way: Given a fd that supports streaming input, map some user-mem as a ring buffer for input. Maybe the kernel should control the buffer in order to make resizing possible (i.e., TCP zero-copy and window scaling).
Hans Henrik
Quoted 10 lines · Ulrich Drepper
Quoted 4 lines · Evgeniy Polyakov
Btw, why do we want mapped ring of ready events?
If user requestd some event, he definitely wants to get them back when they are ready, and not to check and then get them?
Could you please explain more on this issue?If of course makes no sense to enter the kernel to actually get the event. This should be done by storing the event in the ring buffer. I.e., there are two ways to get an event:
Quoted 6 lines
- with a syscall. This can report as many events at once as the caller provides space for. And no event which is reported in the run buffer should be reported this way
- if there is space, report it in the ring buffer. Yes, the buffer can be optional, then all events are reported by the system call.
That requires a copy, which can neglect syscall overhead. Do we really want it to be done?
Quoted 12 lines
So the use case would be like this:
wait_and_get_event:is buffer empty ?
yes -> make syscall no -> get event from buffer
Quoted 4 lines
To avoid races, the syscall needs to take a parameter indicating the last event checked out from the buffer. If in the meantime the kernel put another event in the buffer the syscall immediately returns. Similar to what we do in the futex syscall.
And how "misordering" between queue and buffer is going to be managed? I.e. when buffer is full and events are placed into queue, so syscall could get them, and then syscall is called to get events from the queue but not from the buffer - we can endup taking events from buffer while old are placed in the queue.
And how waiting will be done without syscalls? Will glibc take care of it?
Quoted 24 lines
The question is how to best represent the ring buffer. Zach and some others had some ready responses in Ottawa. The important thing is to avoid cache line ping pong when possible.
Is the ring buffer absolutely necessary? Probably not. But it has the potential to help quite a bit. Don't look at the problem to solve in the context of heavy I/O operations when another syscall here and there doesn't matter. With this single event mechanism for every possible event the kernel can generate programming can look quite different. E.g., every
read()call can implicitly we changed into an async read call followed by a user-level reschedule. This rescheduling allows another thread of execution to run while the read request is processed.
I.e., it's basically asetjmp()followed by a goto into the inner loop
to get the next event. And now suddenly the event notification mechanism really should be as fast as possible. If we submit basically every request asynchronously and are not creating dedicated threads for specific tasks anymore wea) have a lot more event notifications
b) the probability of an event being reported when we want the receive the next one if higher (i.e., the case where no syscall vs syscall makes a difference)
Quoted 5 lines
Yes, all this will require changes in the way programs a written but we shouldn't limit the way we can write programs unnecessarily. I think that given increasing discrepancies in relative speed/latency of the peripherals and the CPU this is one possible solution to keep the CPUs busy without resorting to a gazillion separate threads in each program.
Ok, let's do it in the following way:
I present new version of kevent with new syscalls and fixed issues mentioned before, while people look at it we can end up with mapped buffer design. Is it ok?
Quoted 3 lines
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Signature · 1 line
Evgeniy Polyakov
Quoted 6 lines · Evgeniy Polyakov
Quoted 3 lines
- if there is space, report it in the ring buffer. Yes, the buffer can be optional, then all events are reported by the system call.
That requires a copy, which can neglect syscall overhead. Do we really want it to be done?
Please note that we're talking about events here, not actual data. So only the event is being copied, which is presumably rather small compared to the data.
Cheers,
Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txtQuoted 11 lines · Herbert Xu
Quoted 6 lines · Evgeniy Polyakov
Quoted 3 lines
- if there is space, report it in the ring buffer. Yes, the buffer can be optional, then all events are reported by the system call.
That requires a copy, which can neglect syscall overhead. Do we really want it to be done?
Please note that we're talking about events here, not actual data. So only the event is being copied, which is presumably rather small compared to the data.
In syscall time kevents copy 40bytes for each event + 12 bytes of header (number of events, timeout and command number). That's likely two cache lines if only one event is reported.
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerFrom: Evgeniy Polyakov <[email protected]>
Date: Mon, 31 Jul 2006 14:50:37 +0400
Quoted 3 lines
In syscall time kevents copy 40bytes for each event + 12 bytes of header (number of events, timeout and command number). That's likely two cache lines if only one event is reported.
Do you know how many cachelines are dirtied by system call entry and exit on typical system?
On sparc64 it is a minimum of 3 64-byte cachelines just to save and restore the system call time cpu register state. If application is deep in a call chain, register windows might spill and each such register window will dirty 2 more cachelines as they are dumped to the stack.
I am not even talking about the other basic necessities of doing a system call such as touching various task_struct and thread_info state to check for pending signals etc.
System call overhead is non-trivial especially when you are using it to move only a few small objects into and out of the kernel.
So I would say for up to 4 or 5 events, system call overhead alone touches as many cache lines as the events themselves.
Quoted 3 lines · David Miller
So I would say for up to 4 or 5 events, system call overhead alone touches as many cache lines as the events themselves.
Absolutely.
The other to consider is that events don't come from the hardware. Events are written by the kernel. So if user-space is just reading the events that we've written, then there are no cache misses at all.
Cheers,
Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txtQuoted 3 lines · Herbert Xu
The other to consider is that events don't come from the hardware. Events are written by the kernel. So if user-space is just reading the events that we've written, then there are no cache misses at all.
Not quite true. The ring buffer can be written to from another processor. The kernel thread responsible for generating the event (receiving data from network or disk, expired timer) can run independently on another CPU.
This is the case to keep in mind here. I thought Zach and the other involved in the discussions in Ottawa said this has been shown to be a problem and that a ring buffer implementation with something other than simple front and back pointers is preferable.
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
David Miller
maintainerFrom: Ulrich Drepper <[email protected]>
Date: Tue, 01 Aug 2006 00:53:10 -0700
Quoted 4 lines
This is the case to keep in mind here. I thought Zach and the other involved in the discussions in Ottawa said this has been shown to be a problem and that a ring buffer implementation with something other than simple front and back pointers is preferable.
This is part of why I suggested VJ style channel data structure. At worst, the cachelines for the entries get into shared modified state when the remove userland cpu reads the slot.
Quoted 4 lines · Evgeniy Polyakov
Ok, let's do it in the following way:
I present new version of kevent with new syscalls and fixed issues mentioned before, while people look at it we can end up with mapped buffer design. Is it ok?
Since kevents are never generated by kernel, but only marked as ready, length of the main queue performs as flow control, so we can create a mapped buffer which will have space equal to the main queue length multiplied by size of the copied to userspace structure plus 16 bits for the start index of the kernel writing side, i.e. it will store offset where the oldest event was placed.
Since queue length is a limited factor and thus no new events can be added when queue is full, that means that buffer is full too and userspace must read events. When syscall is called to add new kevent and provided there offset differs from what kernel stored, that means that all events from kernel to provided index have been read and new events can be added. Thus we can even allow read-only mapping. Kernel's index is incremented modulo queue length. If kevent was removed after it was marked as ready, it's copy stays in the mapped buffer, but special flag can be assigned to show that kevent is no longer valid.
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerFrom: Evgeniy Polyakov <[email protected]>
Date: Mon, 31 Jul 2006 23:41:43 +0400
Quoted 7 lines
Since kevents are never generated by kernel, but only marked as ready, length of the main queue performs as flow control, so we can create a mapped buffer which will have space equal to the main queue length multiplied by size of the copied to userspace structure plus 16 bits for the start index of the kernel writing side, i.e. it will store offset where the oldest event was placed.
Quoted 9 lines
Since queue length is a limited factor and thus no new events can be added when queue is full, that means that buffer is full too and userspace must read events. When syscall is called to add new kevent and provided there offset differs from what kernel stored, that means that all events from kernel to provided index have been read and new events can be added. Thus we can even allow read-only mapping. Kernel's index is incremented modulo queue length. If kevent was removed after it was marked as ready, it's copy stays in the mapped buffer, but special flag can be assigned to show that kevent is no longer valid.
This sounds reasonable.
However we must be mindful that the thread of control trying to add a new event might not be in a position to drain the queue of pending events when the queue is full. Usually he will be trying to add an event in response to handling another event.
So we'd have cases like this, assume we start with a full event queue:
thread A thread B
dequeue event
aha, new connection
accept()
register new kevent queue is now full again
add kevent on new
connection
At this point thread A doesn't have very many options when the kevent add fails. You cannot force this thread to read more events, since he may not be in a state where he is easily able to do so.
Quoted 14 lines · David Miller
So we'd have cases like this, assume we start with a full event queue:
thread A thread B
dequeue event
aha, new connection
accept()
register new kevent queue is now full again
add kevent on new
connection
Quoted 3 lines
At this point thread A doesn't have very many options when the kevent add fails. You cannot force this thread to read more events, since he may not be in a state where he is easily able to do so.
There has to be some thread that is responsible for reading events. Perhaps a reasonable thing for a blocked thread that cannot process events to do is to yield to one that can?
David Miller
maintainerFrom: Brent Cook <[email protected]>
Date: Mon, 31 Jul 2006 17:16:48 -0500
Quoted 3 lines
There has to be some thread that is responsible for reading events. Perhaps a reasonable thing for a blocked thread that cannot process events to do is to yield to one that can?
The reason one decentralizes event processing into threads is so that once they are tasked to process some event they need not be concerned with event state.
They are designed to process their event through to the end, then return to the top level and say "any more work for me?"
Quoted 40 lines · David Miller
From: Evgeniy Polyakov <[email protected]>
Date: Mon, 31 Jul 2006 23:41:43 +0400Quoted 7 lines
Since kevents are never generated by kernel, but only marked as ready, length of the main queue performs as flow control, so we can create a mapped buffer which will have space equal to the main queue length multiplied by size of the copied to userspace structure plus 16 bits for the start index of the kernel writing side, i.e. it will store offset where the oldest event was placed.
Quoted 9 lines
Since queue length is a limited factor and thus no new events can be added when queue is full, that means that buffer is full too and userspace must read events. When syscall is called to add new kevent and provided there offset differs from what kernel stored, that means that all events from kernel to provided index have been read and new events can be added. Thus we can even allow read-only mapping. Kernel's index is incremented modulo queue length. If kevent was removed after it was marked as ready, it's copy stays in the mapped buffer, but special flag can be assigned to show that kevent is no longer valid.
This sounds reasonable.
However we must be mindful that the thread of control trying to add a new event might not be in a position to drain the queue of pending events when the queue is full. Usually he will be trying to add an event in response to handling another event.
So we'd have cases like this, assume we start with a full event queue:
thread A thread B
dequeue event
aha, new connection
accept()
register new kevent queue is now full again
add kevent on new
connection
Quoted 3 lines
At this point thread A doesn't have very many options when the kevent add fails. You cannot force this thread to read more events, since he may not be in a state where he is easily able to do so.
By default all kevents are not removed from the queue, so accept events will be in the queue and thread B will fail to register new kevent. To remove kevent from the queue user should either set one-shot flag or do it by special command.
So if we are in position when queue is full and all events are not one-shot, control thread must think about what does it do, and remove some of them (and next time add them with one-shot flag).
Signature · 1 line
Evgeniy Polyakov
Quoted 5 lines
Ok, let's do it in the following way:
I present new version of kevent with new syscalls and fixed issues mentioned before, while people look at it we can end up with mapped buffer design. Is it ok?
Yeah, that sounds good. I'm looking forward to seeing the next set of patches :).
- z
cover kevent: introduction.
In reply to Evgeniy PolyakovI send this patchset for comments and review, it still contains AIO and aio_sendfile() implementation on top of get_block() abstraction, which was decided to postpone for a while (it is simpler right now to generate patchset as a whole, when kevent will be ready for merge, I will generate patchset without AIO stuff). It does not contain mapped buffer implementation, since it's design is not 100% completed, I will present that implementation in the third patchset.
Changes from previous patchset:
- rebased against 2.6.18-git tree
- removed ioctl controlling
- added new syscall kevent_get_events(int fd, unsigned int min_nr, unsigned int max_nr, unsigned int timeout, void __user *buf, unsigned flags)
- use old syscall kevent_ctl for creation/removing, modification and initial kevent initialization
- use mutuxes instead of semaphores
- added file descriptor check and return error if provided descriptor does not match kevent file operations
- various indent fixes
- removed aio_sendfile() declarations.
Thank you.
PATCH 1/4 kevent: core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
It might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
arch/i386/kernel/syscall_table.S +4 -0
@@ -317,3 +317,7 @@ ENTRY(sys_call_table) .long sys_tee /* 315 */ .long sys_vmsplice .long sys_move_pages + .long sys_aio_recv + .long sys_aio_send + .long sys_kevent_get_events + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +4 -0
@@ -713,4 +713,8 @@ #endif .quad sys_tee .quad compat_sys_vmsplice .quad compat_sys_move_pages + .quad sys_aio_recv + .quad sys_aio_send + .quad sys_kevent_get_events + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +5 -1
@@ -323,10 +323,14 @@ #define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316#define __NR_move_pages 317 +#define __NR_aio_recv 318 +#define __NR_aio_send 319 +#define __NR_kevent_get_events 320 +#define __NR_kevent_ctl 321#ifdef __KERNEL__ -#define NR_syscalls 318 +#define NR_syscalls 322/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/unistd.h +9 -1
@@ -619,10 +619,18 @@ #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice)#define __NR_move_pages 279 __SYSCALL(__NR_move_pages, sys_move_pages) +#define __NR_aio_recv 280 +__SYSCALL(__NR_aio_recv, sys_aio_recv) +#define __NR_aio_send 281 +__SYSCALL(__NR_aio_send, sys_aio_send) +#define __NR_aio_sendfile 282 +__SYSCALL(__NR_aio_sendfile, sys_kevent_get_events) +#define __NR_kevent_ctl 283 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_move_pages +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS
include/linux/kevent.h new file +259 -0
259 lines not on this page · raw message
include/linux/kevent_storage.h new file +12 -0
@@ -0,0 +1,12 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +unsigned int qlen;/* Number of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +5 -0
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_aio_recv (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_aio_send (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_kevent_get_events (int ctl_fd,unsigned int min,unsigned int max, +unsigned int timeout,void __user *buf,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,void __user *buf);#endif
init/Kconfig +2 -0
@@ -218,6 +218,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o
kernel/kevent/Kconfig new file +57 -0
57 lines not on this page · raw message
kernel/kevent/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +obj-y := kevent.o kevent_user.o kevent_init.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
kernel/kevent/kevent.c new file +248 -0
248 lines not on this page · raw message
kernel/kevent/kevent_init.c new file +85 -0
85 lines not on this page · raw message
kernel/kevent/kevent_inode.c new file +110 -0
110 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +713 -0
713 lines not on this page · raw message
kernel/sys_ni.c +5 -0
@@ -122,6 +122,11 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_aio_recv); +cond_syscall (sys_aio_send); +cond_syscall (sys_kevent_get_events); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
PATCH 2/4 kevent: network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/asm-i386/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/asm-x86_64/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/linux/skbuff.h +2 -0
@@ -1283,6 +1283,8 @@ extern struct sk_buff *skb_recv_datagram int noblock, int *err);extern unsigned int datagram_poll (struct file *file,struct socket *sock,struct poll_table_struct *wait); +extern int skb_copy_datagram (const struct sk_buff *from, +int offset,void *dst,int size);extern int skb_copy_datagram_iovec (const struct sk_buff *from,int offset,struct iovec *to,int size);
include/net/sock.h +25 -15
@@ -48,6 +48,7 @@ #include <linux/lockdep.h>#include <linux/netdevice.h> #include <linux/skbuff.h> /* struct sk_buff */ #include <linux/security.h> +#include <linux/kevent.h> #include <linux/filter.h>
@@ -391,6 +392,8 @@ enum sock_flags {SOCK_RCVTSTAMP ,/* %SO_TIMESTAMP setting */ SOCK_LOCALROUTE ,/* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK ,/* write queue has been shrunk recently */ +SOCK_ASYNC , + SOCK_ASYNC_INUSE, };static inline void sock_copy_flags (struct sock *nsk,struct sock *osk)
@@ -450,6 +453,21 @@ static inline int sk_stream_memory_free(extern void sk_stream_rfree (struct sk_buff *skb); +struct socket_alloc { +struct socket socket; +struct inode vfs_inode; +}; + +static inline struct socket *SOCKET_I (struct inode *inode) +{ + return &container_of(inode, struct socket_alloc, vfs_inode)->socket; +} + +static inline struct inode *SOCK_INODE (struct socket *socket) +{ + return &container_of(socket, struct socket_alloc, socket)->vfs_inode; +} +static inline void sk_stream_set_owner_r (struct sk_buff *skb,struct sock *sk) { skb->sk = sk;
@@ -477,6 +495,7 @@ static inline void sk_add_backlog(struct sk->sk_backlog.tail = skb; } skb->next =NULL ; +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); }#define sk_wait_event (__sk, __timeo, __condition) \
@@ -548,6 +567,12 @@ struct proto {int (*backlog_rcv) (struct sock *sk,struct sk_buff *skb); + +int (*async_recv) (struct sock *sk, +void *dst,size_t size); +int (*async_send) (struct sock *sk, +struct page **pages,unsigned int poffset, +size_t size);/* Keeping track of sk's, looking them up, and port selection methods. */ void (*hash)(struct sock *sk);
@@ -679,21 +704,6 @@ static inline struct kiocb *siocb_to_kioreturn si->kiocb; } -struct socket_alloc { -struct socket socket; -struct inode vfs_inode; -}; - -static inline struct socket *SOCKET_I (struct inode *inode) -{ - return &container_of(inode, struct socket_alloc, vfs_inode)->socket; -} - -static inline struct inode *SOCK_INODE (struct socket *socket) -{ - return &container_of(socket, struct socket_alloc, socket)->vfs_inode; -} -extern void __sk_stream_mem_reclaim (struct sock *sk);extern int sk_stream_mem_schedule (struct sock *sk,int size,int kind);
include/net/tcp.h +3 -0
@@ -364,6 +364,8 @@ extern int compat_tcp_setsockopt(struc int level, int optname, char __user *optval, int optlen);extern void tcp_set_keepalive (struct sock *sk,int val); +extern int tcp_async_recv (struct sock *sk,void *dst,size_t size); +extern int tcp_async_send (struct sock *sk,struct page **pages,unsigned int poffset,size_t size); extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
@@ -857,6 +859,7 @@ static inline int tcp_prequeue(struct so tp->ucopy.memory =0 ; }else if (skb_queue_len (&tp->ucopy.prequeue) ==1 ) {wake_up_interruptible (sk->sk_sleep); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND );if (!inet_csk_ack_scheduled (sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, (3 * TCP_RTO_MIN) / 4,
kernel/kevent/kevent_naio.c new file +239 -0
239 lines not on this page · raw message
kernel/kevent/kevent_socket.c new file +125 -0
125 lines not on this page · raw message
net/core/datagram.c +55 -0
67 lines not on this page · raw message
net/core/sock.c +17 -1
@@ -617,6 +617,16 @@ #endifspin_unlock_bh (&sk->sk_lock.slock); ret = -ENONET ;break ; +#ifdef CONFIG_KEVENT_SOCKET +case SO_ASYNC_SOCK : +spin_lock_bh (&sk->sk_lock.slock); +if (valbool) +sock_set_flag (sk,SOCK_ASYNC ); +else +sock_reset_flag (sk,SOCK_ASYNC ); +spin_unlock_bh (&sk->sk_lock.slock); +break ; +#endif case SO_PASSSEC :if (valbool)
@@ -1406,6 +1416,7 @@ static void sock_def_wakeup(struct sockif (sk->sk_sleep &&waitqueue_active (sk->sk_sleep))wake_up_interruptible_all (sk->sk_sleep);read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_error_report (struct sock *sk)
@@ -1415,6 +1426,7 @@ static void sock_def_error_report(structwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,0 ,POLL_ERR );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_readable (struct sock *sk,int len)
@@ -1424,6 +1436,7 @@ static void sock_def_readable(struct socwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,1 ,POLL_IN );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_write_space (struct sock *sk)
@@ -1443,6 +1456,7 @@ static void sock_def_write_space(struct }read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); }static void sock_def_destruct (struct sock *sk)
@@ -1559,8 +1573,10 @@ void fastcall release_sock(struct sock *if (sk->sk_backlog.tail)__release_sock (sk); sk->sk_lock.owner =NULL ; -if (waitqueue_active (&sk->sk_lock.wq)) +if (waitqueue_active (&sk->sk_lock.wq)) {wake_up (&sk->sk_lock.wq); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); + }spin_unlock_bh (&sk->sk_lock.slock); }EXPORT_SYMBOL (release_sock);
net/core/stream.c +1 -0
@@ -36,6 +36,7 @@ void sk_stream_write_space(struct sock *wake_up_interruptible (sk->sk_sleep);if (sock->fasync_list && !(sk->sk_shutdown &SEND_SHUTDOWN ))sock_wake_async (sock,2 ,POLL_OUT ); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); } }
net/ipv4/tcp.c +298 -0
316 lines not on this page · raw message
net/ipv4/tcp_input.c +3 -1
@@ -3112,6 +3112,7 @@ static void tcp_ofo_queue(struct sock *s__skb_unlink (skb, &tp->out_of_order_queue);__skb_queue_tail (&sk->sk_receive_queue, skb); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); tp->rcv_nxt =TCP_SKB_CB (skb)->end_seq;if (skb->h.th->fin)tcp_fin (skb, sk, skb->h.th);
@@ -3955,7 +3956,8 @@ int tcp_rcv_established(struct sock *sk,int copied_early =0 ;if (tp->copied_seq == tp->rcv_nxt && - len - tcp_header_len <= tp->ucopy.len) { + len - tcp_header_len <= tp->ucopy.len && + !sock_async (sk)) {#ifdef CONFIG_NET_DMA if (tcp_dma_try_early_copy (sk, skb, tcp_header_len)) { copied_early =1 ;
net/ipv4/tcp_ipv4.c +25 -15
@@ -61,6 +61,7 @@ #include <linux/cache.h>#include <linux/jhash.h> #include <linux/init.h> #include <linux/times.h> +#include <linux/kevent.h> #include <net/icmp.h> #include <net/inet_hashtables.h>
@@ -868,6 +869,7 @@ #endifreqsk_free (req); }else {inet_csk_reqsk_queue_hash_add (sk, req,TCP_TIMEOUT_INIT ); +kevent_socket_notify (sk,KEVENT_SOCKET_ACCEPT ); }return 0 ;
@@ -1108,24 +1110,30 @@ process: skb->dev =NULL ; -bh_lock_sock_nested (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock_nested (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan =get_softnet_dma (); -if (tp->ucopy.dma_chan) - ret =tcp_v4_do_rcv (sk, skb); - else +struct tcp_sock *tp =tcp_sk (sk); +if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan =get_softnet_dma (); +if (tp->ucopy.dma_chan) + ret =tcp_v4_do_rcv (sk, skb); + else#endif - { -if (!tcp_prequeue (sk, skb)) - ret =tcp_v4_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v4_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);
@@ -1849,6 +1857,8 @@ struct proto tcp_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v4_do_rcv, .hash = tcp_v4_hash, .unhash = tcp_unhash,
net/ipv6/tcp_ipv6.c +21 -13
@@ -1230,22 +1230,28 @@ process: skb->dev =NULL ; -bh_lock_sock (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (tp->ucopy.dma_chan) - ret =tcp_v6_do_rcv (sk, skb); - else -#endif - { -if (!tcp_prequeue (sk, skb)) +struct tcp_sock *tp =tcp_sk (sk); +if (tp->ucopy.dma_chan) ret =tcp_v6_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + else +#endif + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v6_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);return ret ?-1 :0 ;
@@ -1596,6 +1602,8 @@ struct proto tcpv6_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v6_do_rcv, .hash = tcp_v6_hash, .unhash = tcp_unhash,
PATCH 4/4 kevent: poll/select() notifications. Timer notifications.
This patch includes generic poll/select and timer notifications.
kevent_poll works simialr to epoll and has the same issues (callback is invoked not from internal state machine of the caller, but through process awake).
Timer notifications can be used for fine grained per-process time management, since iteractive timers are very inconveniently to use, and they are limited.
kernel/kevent/kevent_poll.c new file +223 -0
@@ -0,0 +1,223 @@ +/* +* kevent_poll.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/file.h> +#include <linux/kevent.h> +#include <linux/poll.h> +#include <linux/fs.h> + +static kmem_cache_t *kevent_poll_container_cache; +static kmem_cache_t *kevent_poll_priv_cache; + +struct kevent_poll_ctl +{ +struct poll_table_struct pt; +struct kevent *k; +}; + +struct kevent_poll_wait_container +{ +struct list_head container_entry; +wait_queue_head_t *whead; +wait_queue_t wait; +struct kevent *k; +}; + +struct kevent_poll_private +{ +struct list_head container_list; +spinlock_t container_lock; +}; + +static int kevent_poll_enqueue (struct kevent *k); +static int kevent_poll_dequeue (struct kevent *k); +static int kevent_poll_callback (struct kevent *k); + +static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ +struct kevent_poll_wait_container *cont = + container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +unsigned long flags; +u32 revents, event; + + revents = file->f_op->poll (file,NULL ); +spin_lock_irqsave (&k->lock, flags); + event = k->event.event; +spin_unlock_irqrestore (&k->lock, flags); + +kevent_storage_ready (k->st,NULL , revents); + +return 0 ; +} + +static void kevent_poll_qproc (struct file *file,wait_queue_head_t *whead, +struct poll_table_struct *poll_table) +{ +struct kevent *k = + container_of(poll_table, struct kevent_poll_ctl, pt)->k; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *cont; +unsigned long flags; + + cont =kmem_cache_alloc (kevent_poll_container_cache,SLAB_KERNEL ); +if (!cont) { +kevent_break (k); +return ; + } + + cont->k = k; +init_waitqueue_func_entry (&cont->wait, kevent_poll_wait_callback); + cont->whead = whead; + +spin_lock_irqsave (&priv->container_lock, flags); +list_add_tail (&cont->container_entry, &priv->container_list); +spin_unlock_irqrestore (&priv->container_lock, flags); + +add_wait_queue (whead, &cont->wait); +} + +static int kevent_poll_enqueue (struct kevent *k) +{ +struct file *file; +int err, ready =0 ; +unsigned int revents; +struct kevent_poll_ctl ctl; +struct kevent_poll_private *priv; + + file =fget (k->event.id.raw[0 ]); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_op || !file->f_op->poll) + goto err_out_fput; + + err = -ENOMEM ; + priv =kmem_cache_alloc (kevent_poll_priv_cache,SLAB_KERNEL ); +if (!priv) + goto err_out_fput; + +spin_lock_init (&priv->container_lock); +INIT_LIST_HEAD (&priv->container_list); + + k->priv = priv; + + ctl.k = k; +init_poll_funcptr (&ctl.pt, &kevent_poll_qproc); + + err =kevent_storage_enqueue (&file->st, k); +if (err) + goto err_out_free; + + revents = file->f_op->poll (file, &ctl.pt); +if (revents & k->event.event) { + ready =1 ; +kevent_poll_dequeue (k); + } + +return ready; + +err_out_free: +kmem_cache_free (kevent_poll_priv_cache, priv); +err_out_fput: +fput (file); +return err; +} + +static int kevent_poll_dequeue (struct kevent *k) +{ +struct file *file = k->st->origin; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *w, *n; +unsigned long flags; + +kevent_storage_dequeue (k->st, k); + +spin_lock_irqsave (&priv->container_lock, flags); +list_for_each_entry_safe (w, n, &priv->container_list, container_entry) { +list_del (&w->container_entry); +remove_wait_queue (w->whead, &w->wait); +kmem_cache_free (kevent_poll_container_cache, w); + } +spin_unlock_irqrestore (&priv->container_lock, flags); + +kmem_cache_free (kevent_poll_priv_cache, priv); + k->priv =NULL ; + +fput (file); + +return 0 ; +} + +static int kevent_poll_callback (struct kevent *k) +{ +struct file *file = k->st->origin; +unsigned int revents = file->f_op->poll (file,NULL ); +return (revents & k->event.event); +} + +int kevent_init_poll (struct kevent *k) +{ +if (!kevent_poll_container_cache || !kevent_poll_priv_cache) +return -ENOMEM ; + + k->enqueue = &kevent_poll_enqueue; + k->dequeue = &kevent_poll_dequeue; + k->callback = &kevent_poll_callback; +return 0 ; +} + + +static int __init kevent_poll_sys_init (void ) +{ + kevent_poll_container_cache =kmem_cache_create ("kevent_poll_container_cache" , +sizeof (struct kevent_poll_wait_container ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_container_cache) { +printk (KERN_ERR "Failed to create kevent poll container cache.\n" ); +return -ENOMEM ; + } + + kevent_poll_priv_cache =kmem_cache_create ("kevent_poll_priv_cache" , +sizeof (struct kevent_poll_private ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_priv_cache) { +printk (KERN_ERR "Failed to create kevent poll private data cache.\n" ); +kmem_cache_destroy (kevent_poll_container_cache); + kevent_poll_container_cache =NULL ; +return -ENOMEM ; + } + +printk (KERN_INFO "Kevent poll()/select() subsystem has been initialized.\n" ); +return 0 ; +} + +static void __exit kevent_poll_sys_fini(void) +{ +kmem_cache_destroy (kevent_poll_priv_cache); +kmem_cache_destroy (kevent_poll_container_cache); +} + +module_init (kevent_poll_sys_init); +module_exit (kevent_poll_sys_fini);
kernel/kevent/kevent_timer.c new file +112 -0
112 lines not on this page · raw message
PATCH 3/4 kevent: AIO, aio_sendfile() implementation.
This patch includes asynchronous propagation of file's data into VFS cache and aio_sendfile() implementation.
Network aio_sendfile() works lazily - it asynchronously populates pages into the VFS cache (which can be used for various tricks with adaptive readahead) and then uses usual ->sendfile() callback.
fs/bio.c +1 -1
@@ -119,7 +119,7 @@ void bio_free(struct bio *bio, struct bi/* * default destructor for a bio allocated with bio_alloc_bioset() */ -static void bio_fs_destructor (struct bio *bio) +void bio_fs_destructor (struct bio *bio) {bio_free (bio, fs_bio_set); }
fs/ext2/inode.c +1 -0
@@ -685,6 +685,7 @@ ext2_writepages(struct address_space *ma }const struct address_space_operations ext2_aops = { + .get_block = ext2_get_block, .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_writepage,
fs/ext3/inode.c +1 -0
@@ -1699,6 +1699,7 @@ static int ext3_journalled_set_page_dirt }static const struct address_space_operations ext3_ordered_aops = { + .get_block = ext3_get_block, .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_ordered_writepage,
fs/file_table.c +6 -0
@@ -112,6 +112,9 @@ struct file *get_empty_filp(void)if (security_file_alloc (f)) goto fail_sec; +#ifdef CONFIG_KEVENT_POLL +kevent_storage_init (f, &f->st); +#endif tsk = current;INIT_LIST_HEAD (&f->f_u.fu_list);atomic_set (&f->f_count,1 );
@@ -159,6 +162,9 @@ void fastcall __fput(struct file *file)might_sleep ();fsnotify_close (file); +#ifdef CONFIG_KEVENT_POLL +kevent_storage_fini (&file->st); +#endif /* * The function eventpoll_release() should be the first called * in the file cleanup chain.
fs/inode.c +7 -0
@@ -21,6 +21,7 @@ #include <linux/pagemap.h>#include <linux/cdev.h> #include <linux/bootmem.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/mount.h> /*
@@ -165,12 +166,18 @@ #endif }memset (&inode->u,0 ,sizeof (inode->u)); inode->i_mapping = mapping; +#if definedCONFIG_KEVENT +kevent_storage_init (inode, &inode->st); +#endif }return inode; }void destroy_inode (struct inode *inode) { +#if definedCONFIG_KEVENT_INODE || definedCONFIG_KEVENT_SOCKET +kevent_storage_fini (&inode->st); +#endif BUG_ON (inode_has_buffers (inode));security_inode_free (inode);if (inode->i_sb->s_op->destroy_inode)
fs/reiserfs/inode.c +1 -0
@@ -3001,6 +3001,7 @@ int reiserfs_setattr(struct dentry *dent }const struct address_space_operations reiserfs_address_space_operations = { + .get_block = reiserfs_get_block, .writepage = reiserfs_writepage, .readpage = reiserfs_readpage, .readpages = reiserfs_readpages,
include/linux/fs.h +12 -0
@@ -240,6 +240,9 @@ #include <linux/mutex.h>#include <asm/atomic.h> #include <asm/semaphore.h> #include <asm/byteorder.h> +#ifdef CONFIG_KEVENT +#include <linux/kevent_storage.h> +#endif struct hd_geometry ;struct iovec ;
@@ -352,6 +355,8 @@ struct address_space;struct writeback_control ;struct address_space_operations { +int (*get_block)(struct inode *inode,sector_t iblock, +struct buffer_head *bh_result,int create);int (*writepage)(struct page *page,struct writeback_control *wbc);int (*readpage)(struct file *,struct page *);void (*sync_page)(struct page *);
@@ -546,6 +551,10 @@ #ifdef CONFIG_INOTIFYstruct mutex inotify_mutex;/* protects the watches list */ #endif +#ifdef CONFIG_KEVENT_INODE +struct kevent_storage st; +#endif +unsigned long i_state;unsigned long dirtied_when;/* jiffies of first dirtying */
@@ -698,6 +707,9 @@ #ifdef CONFIG_EPOLLstruct list_head f_ep_links;spinlock_t f_ep_lock;#endif /* #ifdef CONFIG_EPOLL */ +#ifdef CONFIG_KEVENT_POLL +struct kevent_storage st; +#endif struct address_space *f_mapping; };extern spinlock_t files_lock;
include/linux/fsnotify.h +5 -0
@@ -15,6 +15,7 @@ #ifdef __KERNEL__#include <linux/dnotify.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/audit.h> /*
@@ -79,6 +80,7 @@ static inline void fsnotify_nameremove(s isdir =IN_ISDIR ;dnotify_parent (dentry,DN_DELETE );inotify_dentry_parent_queue_event (dentry,IN_DELETE |isdir,0 , dentry->d_name.name); +kevent_inode_notify_parent (dentry,KEVENT_INODE_REMOVE ); }/*
@@ -88,6 +90,7 @@ static inline void fsnotify_inoderemove( {inotify_inode_queue_event (inode,IN_DELETE_SELF ,0 ,NULL ,NULL );inotify_inode_is_dead (inode); +kevent_inode_remove (inode); }/*
@@ -96,6 +99,7 @@ static inline void fsnotify_inoderemove(static inline void fsnotify_create (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
@@ -107,6 +111,7 @@ static inline void fsnotify_create(strucstatic inline void fsnotify_mkdir (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE |IN_ISDIR ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
kernel/kevent/kevent_aio.c new file +580 -0
580 lines not on this page · raw message
kernel/kevent/kevent_user.c line 94
Quoted 4 lines · Evgeniy Polyakov
kernel/kevent/kevent_user.c new file +4
-0+ u->ready_num =0 ; +#ifdef CONFIG_KEVENT_USER_STAT + u->wait_num = u->im_num = u->total =0 ; +#endif
Generally, #ifdefs in the body of the kernel code are discouraged. Can you abstract these out as static inlines?
- James
Signature · 2 lines
James Morris <[email protected]>
Quoted 9 lines · James Morris
kernel/kevent/kevent_user.c line 94
Quoted 4 lines · Evgeniy Polyakov
kernel/kevent/kevent_user.c new file +4
-0+ u->ready_num =0 ; +#ifdef CONFIG_KEVENT_USER_STAT + u->wait_num = u->im_num = u->total =0 ; +#endif Generally, #ifdefs in the body of the kernel code are discouraged. Can you abstract these out as static inlines?
Yes, it is possible.
I would ask is it needed at all? It contains number of immediately fired events (i.e. those which were ready when event was added and thus syscall returned immediately showing that it is ready), total number of events, which were inserted in the given queue and number of events which were marked as ready after they were inserted. Currently it is compilation option which ends up in printk with above info when kevent queue is removed.
Quoted 4 lines
- James
Signature · 2 lines
James Morris <[email protected]>
Signature · 1 line
Evgeniy Polyakov
Quoted 11 lines · Evgeniy Polyakov
Quoted 9 lines · James Morris
kernel/kevent/kevent_user.c line 94
Quoted 4 lines · Evgeniy Polyakov
kernel/kevent/kevent_user.c new file +4
-0+ u->ready_num =0 ; +#ifdef CONFIG_KEVENT_USER_STAT + u->wait_num = u->im_num = u->total =0 ; +#endif Generally, #ifdefs in the body of the kernel code are discouraged. Can you abstract these out as static inlines?
Quoted 2 lines
Yes, it is possible.
I would ask is it needed at all?
Yes, please, it is standard kernel development practice.
Otherwise, the kernel will turn into an unmaintainable #ifdef jungle.
Quoted 7 lines
It contains number of immediately fired
events (i.e. those which were ready when event was added and thus syscall returned immediately showing that it is ready), total number of events, which were inserted in the given queue and number of events which were marked as ready after they were inserted. Currently it is compilation option which ends up in printk with above info when kevent queue is removed.
Fine, make
static inline void kevent_user_stat_reset(u);
etc.
which compile to nothing when it's not confifgured.
Signature · 2 lines
James Morris <[email protected]>
Quoted 8 lines · James Morris
Quoted 8 lines
Quoted 7 lines
kernel/kevent/kevent_user.c line 94
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ u->ready_num =0 ; +#ifdef CONFIG_KEVENT_USER_STAT + u->wait_num = u->im_num = u->total =0 ; +#endif Generally, #ifdefs in the body of the kernel code are discouraged. Can you abstract these out as static inlines?
Quoted 4 lines
Quoted 2 lines
Yes, it is possible.
I would ask is it needed at all?Yes, please, it is standard kernel development practice.
Will do.
Thanks, James.
Quoted 3 lines
Signature · 2 lines
James Morris <[email protected]>
Signature · 1 line
Evgeniy Polyakov
OK, here's some of my reactions to the core part.
include/linux/kevent.h line 45
Quoted 6 lines
include/linux/kevent.h new file +6
-0+#define KEVENT_SOCKET 0 +#define KEVENT_INODE 1 +#define KEVENT_TIMER 2 +#define KEVENT_POLL 3 +#define KEVENT_NAIO 4 +#define KEVENT_AIO 5
I guess we can't really avoid some form of centralized list of the constants in the API if we're going for a flat constant namespace. It'll be irritating to manage this list over time, just like it's irritating to manage syscall numbers now.
include/linux/kevent.h line 63
Quoted 6 lines
include/linux/kevent.h new file +6
-0+/* +* Socket/network asynchronous IO events. +*/ +#define KEVENT_SOCKET_RECV 0x1 +#define KEVENT_SOCKET_ACCEPT 0x2 +#define KEVENT_SOCKET_SEND 0x4
I wonder if these shouldn't live in the subsystems instead of in kevent.h.
Quoted 6 lines
include/linux/kevent.h new file +6
-0+/* +* Poll events. +*/ +#define KEVENT_POLL_POLLIN 0x0001 +#define KEVENT_POLL_POLLPRI 0x0002 +#define KEVENT_POLL_POLLOUT 0x0004
include/linux/kevent.h line 86
Quoted 10 lines
include/linux/kevent.h new file +10
-0+#define KEVENT_POLL_POLLERR 0x0008 +#define KEVENT_POLL_POLLHUP 0x0010 +#define KEVENT_POLL_POLLNVAL 0x0020 + +#define KEVENT_POLL_POLLRDNORM 0x0040 +#define KEVENT_POLL_POLLRDBAND 0x0080 +#define KEVENT_POLL_POLLWRNORM 0x0100 +#define KEVENT_POLL_POLLWRBAND 0x0200 +#define KEVENT_POLL_POLLMSG 0x0400 +#define KEVENT_POLL_POLLREMOVE 0x1000
And couldn't we just use the existing poll bit definitions for this?
include/linux/kevent.h line 99
Quoted 4 lines
include/linux/kevent.h new file +4
-0+struct kevent_id +{ +__u32 raw[2 ]; +};
Why not a simple u64? Users can play games with packing it into other types if they want.
include/linux/kevent.h line 112
Quoted 3 lines
include/linux/kevent.h new file +3
-0+__u32 user[2 ];/* User's data. It is not used, just copied to/from user. */ +void *ptr; + };
Again just a u64 seems like it would be simpler. userspace library wrappers can help massage it, but the kernel is just treating it as an opaque data blob.
include/linux/kevent.h line 125
Quoted 13 lines
include/linux/kevent.h new file +13
-0+}; + +#define KEVENT_CTL_ADD 0 +#define KEVENT_CTL_REMOVE 1 +#define KEVENT_CTL_MODIFY 2 +#define KEVENT_CTL_INIT 3 + +struct kevent_user_control +{ +unsigned int cmd;/* Control command, e.g. KEVENT_ADD, KEVENT_REMOVE... */ +unsigned int num;/* Number of ukevents this strucutre controls. */ +unsigned int timeout;/* Timeout in milliseconds waiting for "num" events to become ready. */ +};
Even if we only have one syscall with a cmd multiplexer (which I'm not thrilled with), we should at least make these arguments explicit in the system call. It's weird to hide them in a struct. We could also think about making them u32 or u64 so that we don't need compat wrappers, but maybe that's overkill.
Also, can we please use a struct timespec for the timeout? Then the kernel will have the luxury of using whatever mechanism it wants to satisfy the user's precision desires. Just like sys_nanosleep() uses timespec and so can be implemented with hrtimers.
Review
Quoted 2 lines
(quoted diff) +2
-0+struct kevent +{
(trivial nit, "struct kevent {" is the preferred form.)
include/linux/kevent.h line 147
Quoted 2 lines
include/linux/kevent.h new file +2
-0+struct ukevent event; +spinlock_t lock;/* This lock protects ukevent manipulations, e.g. ret_flags changes. */
It'd be great if these struct members could get a prefix (ala: inode -> i_, socket -> sk_) so that it's less painful getting tags helpers to look up instances for us. Asking for 'lock' is hilarious.
include/linux/kevent.h line 176
Quoted 9 lines
include/linux/kevent.h new file +9
-0+struct kevent_list +{ +struct list_head kevent_list;/* List of all kevents. */ +spinlock_t kevent_lock;/* Protects all manipulations with queue of kevents. */ +}; + +struct kevent_user +{ +struct kevent_list kqueue[KEVENT_HASH_MASK +1 ];
Hmm. I think the current preference is not to have a lock per bucket. It doesn't scale nearly as well as it seems like it should as the cache footprint is higher and as cacheline contention hits as there are multiple buckets per cacheline. For now I'd simplify the hash into a single lock and an array of struct hlist_head. In the future it could be another user of some kind of relatively-generic hash implementation based on rcu that has been talked about for a while.
Quoted 1 line
+#define
KEVENT_MAX_REQUESTSPAGE_SIZE/sizeof(struct kevent)
This is unused?
include/linux/kevent.h line 210
Quoted 5 lines
include/linux/kevent.h new file +5
-0+#define list_for_each_entry_reverse_safe (pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = n, n = list_entry(pos->member.prev, typeof(*pos), member))
If anyone was calling this they could use list_for_each_entry_safe_reverse() in list.h but nothing is calling it?
Either way, it should be removed :).
Quoted 1 line
+#define
sock_async(__sk)0
It's a minor complaint, but these kinds of ifdefs that drop arguments can cause unused argument warnings if they're the only user of the given argument. It'd be nicer to do something like ({ (void)_sk; 0; }) .
include/linux/kevent_storage.h line 6
Quoted 3 lines
include/linux/kevent_storage.h new file +3
-0+struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */
Do we really need this pointer? When the kevent_storage is embedded in the origin, like struct inode in your patch sets, you can use container_of() to get back to the inode. For sources that aren't built like that, like the timer_list, you could introduce a parent structure that has the timer_list and the _storage embedded in it.
kernel/kevent/Makefile line 7
Quoted 6 lines
kernel/kevent/Makefile new file +6
-0+obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
I suspect that we won't want this configurable if it gets merged, but I could be wrong and don't feel strongly about it.
kernel/kevent/kevent.c line 95
Quoted 7 lines
kernel/kevent/kevent.c new file +7
-0+switch (k->event.type) { +case KEVENT_NAIO : + err =kevent_init_naio (k); +break ; +case KEVENT_SOCKET : + err =kevent_init_socket (k); +break ;
I wonder if it wouldn't be less noisy to have something like
struct kevent_callbacks {
kevent_callback_t callback, enqueue, dequeue;
} kev_callbacks[] = { [ KEVENT_NAIO ] = &kevent_naio_callbacks,
};
k->callbacks = kev_callbacks[k->event.type];
Then you'd also have one pointer per kevent instead of three.
kernel/kevent/kevent.c line 145
Quoted 9 lines
kernel/kevent/kevent.c new file +9
-0+void kevent_storage_dequeue (struct kevent_storage *st,struct kevent *k) +{ +unsigned long flags; + +spin_lock_irqsave (&st->lock, flags); +if (k->storage_entry.next !=LIST_POISON1 ) { +list_del (&k->storage_entry); + st->qlen--; + }
Is this relying on list_del() having set LIST_POISON1? If so, please use list_del_init() and list_empty() instead.
Review
Quoted 2 lines
(quoted diff) +2
-0+static void __kevent_requeue(struct kevent *k, u32 event) +{
A few things here. First, the event argument isn't used?
kernel/kevent/kevent.c line 154
Quoted 1 line
kernel/kevent/kevent.c new file +1
-0+ err = k->callback (k);
This is being called while holding both the kevent_list->kevent_lock and the k->st->lock. So ->callback is being called with locks held and interrupts blocked which is going to greatly restrict what can be done there. If that's what we want to do we should document the heck out of it.
kernel/kevent/kevent.c line 174
Quoted 3 lines
kernel/kevent/kevent.c new file +2
-0+spin_lock_irqsave (&k->lock, flags); +spin_lock_irqsave (&k->user->ready_lock, flags);
It also now creates lock nesting three deep, which starts to feel uncomfortable. Have you run this under lockdep? It might be fine, but this kind of depth means those of us auditing have to stare very very closely at the locks that paths acquire. The fewer locks that are held at a time, the better.
kernel/kevent/kevent.c line 197
Quoted 2 lines
kernel/kevent/kevent.c new file +2
-0+void kevent_storage_ready (struct kevent_storage *st, +kevent_callback_t ready_callback,u32 event)
Hmm, the only caller that provides a callback is using it to call kevent_break() on each event. Could that not be done by the helper itself if the caller provides the right event? Is there some more complicated use of the callback on the horizon? Not a big deal, but there are those who prefer to avoid code paths that nest lots of callbacks in sequence.
Review
Quoted 1 line
(quoted diff) +1
-0+ struct kevent *k, *n;
In general, it's nicer to user longer names please. You'll notice throughout the kernel that we use things like dentry, inode, page, sock, etc, instead of d, i, p, and s.
kernel/kevent/kevent.c line 234
Quoted 9 lines
kernel/kevent/kevent.c new file +9
-0+struct kevent *kevent_alloc (gfp_t mask) +{ +return kmem_cache_alloc (kevent_cache, mask); +} + +void kevent_free (struct kevent *k) +{ +kmem_cache_free (kevent_cache, k); +}
We probably don't need these wrappers, just call the kmem_cache_* functions directly.
kernel/kevent/kevent_user.c line 75
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+static unsigned int kevent_user_poll (struct file *file,struct poll_table_struct *wait) +{ +struct kevent_user *u = file->private_data; +unsigned int mask; + +poll_wait (file, &u->wait, wait); + mask =0 ; + +if (u->ready_num) + mask |=POLLIN |POLLRDNORM ;
Shouldn't this be testing ready_num while holding the ready_lock?
kernel/kevent/kevent_user.c line 98
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+for (i=0 ; i<KEVENT_HASH_MASK +1 ; ++i) { +INIT_LIST_HEAD (&u->kqueue[i].kevent_list); +spin_lock_init (&u->kqueue[i].kevent_lock); + }
ARRAY_SIZE(u->kqueue) should probably be used instead of trying to keep (KEVENT_HASH_MASK + 1) in sync with the kqueue definition.
kernel/kevent/kevent_user.c line 134
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+static inline void kevent_user_put (struct kevent_user *u) +{ +if (atomic_dec_and_test (&u->refcnt)) { +#ifdef CONFIG_KEVENT_USER_STAT +printk ("%s: u=%p, wait=%lu, immediately=%lu, total=%lu.\n" , + __func__, u, u->wait_num, u->im_num, u->total); +#endif
printk() seems like a poor choice if this is meant to be a more formal functionality. If it's just a debugging aid then pr_debug() and DEBUG might be nicer.
kernel/kevent/kevent_user.c line 161
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+/* +* Remove kevent from user's list of all events, +* dequeue it from storage and decrease user's reference counter, +* since this kevent does not exist anymore. That is why it is freed here. +*/ +static void kevent_finish_user (struct kevent *k,int lock,int deq)
How about providing locked and unlocked prototypes instead of an argument that says whether to lock or not? You know, the usual:
void __thingy() {
doit();
}void thingy() {
lock();
__thingy();
unlock();
}Review
Quoted 2 lines
(quoted diff) +2
-0+ list_del(&k->kevent_entry); + u->kevent_num--;
I wonder if these shouldn't get micro helpers that then have BUG_ON()s to test bad conditions. like BUG_ON(list_empty() && kevent_num), that sort of thing.
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+static struct kevent *__kqueue_dequeue_one_ready (struct list_head *q, +unsigned int *qlen) +{ +struct kevent *k =NULL ;
kernel/kevent/kevent_user.c line 209
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+unsigned int len = *qlen; + +if (len && !list_empty (q)) { + k = list_entry(q->next, struct kevent, ready_entry); +list_del (&k->ready_entry); + *qlen = len -1 ; + } + +return k; +}
Hmm, this is only called in one place? I'd either make the list_head and lock into one struct (like struct sk_buff_head) or hoist the code into the caller.
kernel/kevent/kevent_user.c line 235
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+list_for_each_entry (k, &l->kevent_list, kevent_entry) { +spin_lock (&k->lock); +if (k->event.user[0 ] == uk->user[0 ] && k->event.user[1 ] == uk->user[1 ] && + k->event.id.raw[0 ] == uk->id.raw[0 ] && + k->event.id.raw[1 ] == uk->id.raw[1 ]) { + found =1 ; +spin_unlock (&k->lock);
Ahhh, it's fs/aio.c:lookup_kiocb() all over again :) :). I guess we'll get this in a hash, or something, before merging.
kernel/kevent/kevent_user.c line 324
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+mutex_lock (&u->ctl_mutex); + +for (i=0 ; i<ctl->num; ++i) { +if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + err = -EINVAL ; +break ; + } + +if (kevent_modify (&uk, u))
So there are a bunch of these. The internal list and kevents and such have their own locks. What are the mutexes serializing? Why can't we rely on the finger-grained object locking to make sure that concurrent operations behave resonably? One can imagine wanting to modify two kevents in a context that have nothing to do with each other and not wanting to serialize them at the context.
kernel/kevent/kevent_user.c line 384
Quoted 12 lines
kernel/kevent/kevent_user.c new file +12
-0+int kevent_user_add_ukevent (struct ukevent *uk,struct kevent_user *u) +{ +struct kevent *k; +int err; + + k =kevent_alloc (GFP_KERNEL ); +if (!k) { + err = -ENOMEM ; + goto err_out_exit; + } + +memcpy (&k->event, uk,sizeof (struct ukevent ));
This path is copying the ukevents twice. First from userspace back up in kevent_user_ctl_add() and then here into the kevent. We should rework things a bit so that we only copy it once.
kernel/kevent/kevent_user.c line 396
Quoted 3 lines
kernel/kevent/kevent_user.c new file +3
-0+#ifdef CONFIG_KEVENT_USER_STAT + u->total++; +#endif
FWIW, this could hide behind some kevent_user_stat_inc(u) that could be ifdefed away in the header.
kernel/kevent/kevent_user.c line 407
Quoted 11 lines
kernel/kevent/kevent_user.c new file +11
-0+ { +unsigned long flags; +unsigned int hash =kevent_user_hash (&k->event); +struct kevent_list *l = &u->kqueue[hash]; + +spin_lock_irqsave (&l->kevent_lock, flags); +list_add_tail (&k->kevent_entry, &l->kevent_list); + u->kevent_num++; +kevent_user_get (u); +spin_unlock_irqrestore (&l->kevent_lock, flags); + }
Hmm, please don't indent things like this. Add a little helper function or hoist the locals up into the main function and lose the braces.
kernel/kevent/kevent_user.c line 444
Quoted 6 lines
kernel/kevent/kevent_user.c new file +5
-0+static int kevent_user_ctl_add (struct kevent_user *u, +struct kevent_user_control *ctl,void __user *arg) +{ + orig = arg; + ctl_addr = arg -sizeof (struct kevent_user_control );
Ugh. This is more awkwardness that comes from packing the system call arguments in neighbouring structs behind a void *. We should really have explicit typed args.
kernel/kevent/kevent_user.c line 457
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+for (i=0 ; i<ctl->num; ++i) { +if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + } + arg +=sizeof (struct ukevent ); + + err =kevent_user_add_ukevent (&uk, u);
There are some users that will want to add thousands of events at a time. (Like, say, a certain database writing back lots of cached dirtied database blocks.) I wonder if we should arrange this so that we can get some batching done and reduce the amount of lock traffic per event added.
Quoted 5 lines
+/*
+ * In nonblocking mode it returns as many events as possible, but not more than @max_nr.
+ * In blocking mode it waits until timeout or if at least @min_nr events are ready,
+ * if timeout is zero, than it waits no more than 1 second or if at least one event
+ * is ready.
That's odd. Why not have a timeout of 0 mean a timeout of 0? Where did 1 second come from? :) It seems pretty crazy to require programmers to check that their timer math didn't just end up at 0 and magically tell the kernel to wait a second.
kernel/kevent/kevent_user.c line 516
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+mutex_lock (&u->ctl_mutex); +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + }
Again, this a great opportunity to copy more than one at a time with some refactoring.
kernel/kevent/kevent_user.c line 636
Quoted 11 lines
kernel/kevent/kevent_user.c new file +11
-0+asmlinkage long sys_kevent_ctl (int fd,void __user *arg) +{ +int err = -EINVAL , fput_needed; +struct kevent_user_control ctl; +struct file *file; + +if (copy_from_user (&ctl, arg,sizeof (struct kevent_user_control ))) +return -EINVAL ; + +if (ctl.cmd ==KEVENT_CTL_INIT ) +return kevent_ctl_init ();
Hmm. So we can get one of these fds both by opening the device file or by calling _CTL_INIT (which then magically ignores the fd argument?). That seems confusing.
Anyway, that's enough for now. I hope this helps.
- z
David Miller
maintainerFrom: Zach Brown <[email protected]>
Date: Tue, 01 Aug 2006 16:56:59 -0700
Quoted 5 lines
Even if we only have one syscall with a cmd multiplexer (which I'm not thrilled with), we should at least make these arguments explicit in the system call. It's weird to hide them in a struct. We could also think about making them u32 or u64 so that we don't need compat wrappers, but maybe that's overkill.
I think making the userspace data structure not require any compat handling is a must, thanks for pointing this out Zach.
Quoted 3 lines
It'd be great if these struct members could get a prefix (ala: inode -> i_, socket ->
sk_) so that it's less painful getting tags helpers to look up instances for us. Asking for 'lock' is hilarious.
Agreed.
Quoted 1 line
Hmm. I think the current preference is not to have a lock per bucket.
Yes, it loses badly, that's why we undid this in the routing cache and just have a fixed sized array of locks which is hashed into.
For kevents, I think a single spinlock initially is fine and if we hit performance problems on SMP we can fix it. We should not implement complexity we have no proof of needing yet :)
Quoted 3 lines
Quoted 1 line
+#define
KEVENT_MAX_REQUESTSPAGE_SIZE/sizeof(struct kevent)This is unused?
It is probably groundwork for the mmap() ring buffer... :)
Quoted 11 lines · David Miller
From: Zach Brown <[email protected]>
Date: Tue, 01 Aug 2006 16:56:59 -0700Quoted 5 lines
Even if we only have one syscall with a cmd multiplexer (which I'm not thrilled with), we should at least make these arguments explicit in the system call. It's weird to hide them in a struct. We could also think about making them u32 or u64 so that we don't need compat wrappers, but maybe that's overkill.
I think making the userspace data structure not require any compat handling is a must, thanks for pointing this out Zach.
It does not require compat macros, since unsigned int has the same size on all normal machines where Linux runs, although it can be different. Anyway, I will replace it with explicit syscall parameters.
Quoted 5 lines
Quoted 3 lines
It'd be great if these struct members could get a prefix (ala: inode -> i_, socket ->
sk_) so that it's less painful getting tags helpers to look up instances for us. Asking for 'lock' is hilarious.Agreed.
Heh, it was so much less typing...
Quoted 8 lines
Quoted 1 line
Hmm. I think the current preference is not to have a lock per bucket.
Yes, it loses badly, that's why we undid this in the routing cache and just have a fixed sized array of locks which is hashed into.
For kevents, I think a single spinlock initially is fine and if we hit performance problems on SMP we can fix it. We should not implement complexity we have no proof of needing yet :)
Ok, let's see how it will behave.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
+#define
KEVENT_MAX_REQUESTSPAGE_SIZE/sizeof(struct kevent)This is unused?
It is probably groundwork for the
mmap()ring buffer... :)
A lot of work, isn't it? :)
Signature · 1 line
Evgeniy Polyakov
Quoted 2 lines · Zach Brown
OK, here's some of my reactions to the core part.
Thanks.
Quoted 12 lines
include/linux/kevent.h line 45
Quoted 6 lines
include/linux/kevent.h new file +6
-0+#define KEVENT_SOCKET 0 +#define KEVENT_INODE 1 +#define KEVENT_TIMER 2 +#define KEVENT_POLL 3 +#define KEVENT_NAIO 4 +#define KEVENT_AIO 5I guess we can't really avoid some form of centralized list of the constants in the API if we're going for a flat constant namespace. It'll be irritating to manage this list over time, just like it's irritating to manage syscall numbers now.
Quoted 8 lines
include/linux/kevent.h line 63
Quoted 6 lines
include/linux/kevent.h new file +6
-0+/* +* Socket/network asynchronous IO events. +*/ +#define KEVENT_SOCKET_RECV 0x1 +#define KEVENT_SOCKET_ACCEPT 0x2 +#define KEVENT_SOCKET_SEND 0x4I wonder if these shouldn't live in the subsystems instead of in
kevent.h.
Yes it could, but it requires including those files in kevent.h, which is exported to userspace, and it is not always possible to publish included file there.
Quoted 10 lines
Quoted 10 lines
include/linux/kevent.h new file +10
-0+/* +* Poll events. +*/ +#define KEVENT_POLL_POLLIN 0x0001 +#define KEVENT_POLL_POLLPRI 0x0002 +#define KEVENT_POLL_POLLOUT 0x0004 +#define KEVENT_POLL_POLLERR 0x0008 +#define KEVENT_POLL_POLLHUP 0x0010 +#define KEVENT_POLL_POLLNVAL 0x0020 +
Quoted 8 lines
include/linux/kevent.h line 86
Quoted 6 lines
include/linux/kevent.h new file +6
-0+#define KEVENT_POLL_POLLRDNORM 0x0040 +#define KEVENT_POLL_POLLRDBAND 0x0080 +#define KEVENT_POLL_POLLWRNORM 0x0100 +#define KEVENT_POLL_POLLWRBAND 0x0200 +#define KEVENT_POLL_POLLMSG 0x0400 +#define KEVENT_POLL_POLLREMOVE 0x1000And couldn't we just use the existing poll bit definitions for this?
asm/poll.h I expect.linux/poll.h is too heavy or not?
Quoted 8 lines
include/linux/kevent.h line 99
Quoted 4 lines
include/linux/kevent.h new file +4
-0+struct kevent_id +{ +__u32 raw[2 ]; +};Why not a simple u64? Users can play games with packing it into other types if they want.
Quoted 7 lines
include/linux/kevent.h line 112
Quoted 3 lines
include/linux/kevent.h new file +3
-0+__u32 user[2 ];/* User's data. It is not used, just copied to/from user. */ +void *ptr; + };Again just a u64 seems like it would be simpler. userspace library wrappers can help massage it, but the kernel is just treating it as an opaque data blob.
u64 is not aligned, so I prefer to use u32 as much as possible.
Quoted 11 lines
Quoted 11 lines
include/linux/kevent.h new file +11
-0+}; + +#define KEVENT_CTL_ADD 0 +#define KEVENT_CTL_REMOVE 1 +#define KEVENT_CTL_MODIFY 2 +#define KEVENT_CTL_INIT 3 + +struct kevent_user_control +{ +unsigned int cmd;/* Control command, e.g. KEVENT_ADD, KEVENT_REMOVE... */ +unsigned int num;/* Number of ukevents this strucutre controls. */
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ unsigned int timeout; /* Timeout in milliseconds waiting for "num" events to become ready. */ +};Even if we only have one syscall with a cmd multiplexer (which I'm not thrilled with), we should at least make these arguments explicit in the system call. It's weird to hide them in a struct. We could also think about making them u32 or u64 so that we don't need compat wrappers, but maybe that's overkill.
Ok.
Quoted 4 lines
Also, can we please use a struct timespec for the timeout? Then the kernel will have the luxury of using whatever mechanism it wants to satisfy the user's precision desires. Just like
sys_nanosleep()uses timespec and so can be implemented with hrtimers.
It has variable size, I strongly against such things between kernel and userspace.
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+struct kevent +{(trivial nit, "struct kevent {" is the preferred form.)
Ok.
Quoted 7 lines
include/linux/kevent.h line 147
Quoted 2 lines
include/linux/kevent.h new file +2
-0+struct ukevent event; +spinlock_t lock;/* This lock protects ukevent manipulations, e.g. ret_flags changes. */ It'd be great if these struct members could get a prefix (ala: inode -> i_, socket ->
sk_) so that it's less painful getting tags helpers to look up instances for us. Asking for 'lock' is hilarious.
But it requires much less typing :)
Will update.
Quoted 10 lines
include/linux/kevent.h line 176
Quoted 9 lines
include/linux/kevent.h new file +9
-0+struct kevent_list +{ +struct list_head kevent_list;/* List of all kevents. */ +spinlock_t kevent_lock;/* Protects all manipulations with queue of kevents. */ +}; + +struct kevent_user +{ +struct kevent_list kqueue[KEVENT_HASH_MASK +1 ];
Quoted 7 lines
Hmm. I think the current preference is not to have a lock per bucket. It doesn't scale nearly as well as it seems like it should as the cache footprint is higher and as cacheline contention hits as there are multiple buckets per cacheline. For now I'd simplify the hash into a single lock and an array of struct
hlist_head. In the future it could be another user of some kind of relatively-generic hash implementation based on rcu that has been talked about for a while.
Well, it scales better than one lock per the whole queue, but we can see how it looks with one lock.
I used RCU hash table in kevents, but it scales very bad for things like inode removal, which can not be done (at least when kevent was initially created) in rcu callback context, so it required sycnhronize)rcu() which broke latencies to unacceptible level.
Quoted 3 lines
Quoted 1 line
+#define
KEVENT_MAX_REQUESTSPAGE_SIZE/sizeof(struct kevent)This is unused?
As David mentioned, I expect it to be base for mapped ring, although it should be PAGE_SIZE/sizeof(struct ukevent). I will remove for now.
Quoted 9 lines
include/linux/kevent.h line 210
Quoted 5 lines
include/linux/kevent.h new file +5
-0+#define list_for_each_entry_reverse_safe (pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = n, n = list_entry(pos->member.prev, typeof(*pos), member))If anyone was calling this they could use
list_for_each_entry_safe_reverse()inlist.hbut nothing is calling it?
Either way, it should be removed :).
It is from the past life, I will remove it.
Quoted 5 lines
Quoted 1 line
+#define
sock_async(__sk)0It's a minor complaint, but these kinds of ifdefs that drop arguments can cause unused argument warnings if they're the only user of the given argument. It'd be nicer to do something like ({ (void)_sk; 0; }) .
Ok.
Quoted 9 lines
include/linux/kevent_storage.h line 6
Quoted 3 lines
include/linux/kevent_storage.h new file +3
-0+struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ Do we really need this pointer? When the
kevent_storageis embedded in the origin, like struct inode in your patch sets, you can usecontainer_of()to get back to the inode. For sources that aren't built like that, like thetimer_list, you could introduce a parent structure that has thetimer_listand the_storageembedded in it.
Well, the idea was to be able not only to embed kevent_storage, but to have a pointer to it, so some users can allocate it as addon. If we strongly decide, that it will not used that way, this pointer can be removed.
Quoted 9 lines
kernel/kevent/Makefile line 7
Quoted 6 lines
kernel/kevent/Makefile new file +6
-0+obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.oI suspect that we won't want this configurable if it gets merged, but I could be wrong and don't feel strongly about it.
For example epoll is configurable for embedded systems, so it might be a good ide to be possible to remove something that will not be 100% in use.
Quoted 13 lines
kernel/kevent/kevent.c line 95
Quoted 7 lines
kernel/kevent/kevent.c new file +7
-0+switch (k->event.type) { +case KEVENT_NAIO : + err =kevent_init_naio (k); +break ; +case KEVENT_SOCKET : + err =kevent_init_socket (k); +break ;I wonder if it wouldn't be less noisy to have something like
struct kevent_callbacks { kevent_callback_t callback, enqueue, dequeue; } kev_callbacks[] = {
Quoted 6 lines
[
KEVENT_NAIO] =&kevent_naio_callbacks,
};k->callbacks = kev_callbacks[k->event.type];Then you'd also have one pointer per kevent instead of three.
Ok, I will create such table.
Quoted 4 lines
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+void kevent_storage_dequeue (struct kevent_storage *st,struct kevent *k) +{ +unsigned long flags; +
Quoted 8 lines
kernel/kevent/kevent.c line 145
Quoted 5 lines
kernel/kevent/kevent.c new file +5
-0+spin_lock_irqsave (&st->lock, flags); +if (k->storage_entry.next !=LIST_POISON1 ) { +list_del (&k->storage_entry); + st->qlen--; + }Is this relying on
list_del()having setLIST_POISON1? If so, please uselist_del_init()andlist_empty()instead.
Yes, POISON is a flag, that keven is or is not in the appropriate list. It could be done by wasting some bits, but I decided to not do it since list poison is always there.
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+static void __kevent_requeue(struct kevent *k, u32 event) +{A few things here. First, the event argument isn't used?
Tss, it was used for printk when it was there :)
Quoted 6 lines
kernel/kevent/kevent.c line 154
Quoted 1 line
kernel/kevent/kevent.c new file +1
-0+ err = k->callback (k);This is being called while holding both the
kevent_list->kevent_lockand thek->st->lock. So ->callback is being called with locks held and interrupts blocked which is going to greatly restrict what can be done there. If that's what we want to do we should document the heck out of it.
No, interrupts and bh a not blocked there, when it is called from origin's state machine.
locking is following:
storage_lock (interrupts and bh are in the state which was in a origin's state machine, for example socket code has bh disabled, but block layer has interrupts disabled here)
check if at aleast one event in storage queue is requested for
that event
call callback
It is possible to mark event as broken or done in callback, so we need to check event's flags. It does not actually require irqsave lock, since the same kevent can not live in several storages.
We hold a lock to protect against userspace which can change that flags. If it is marked as ready we queue that event into ready list under ready list lock. That requires irq disabling, since that queue can be accessed from any context.
So there are maximum 2 embedded locks.
When userspace modifies kevent, it must protect against access from the origin, so it disables interrupt.
Quoted 9 lines
kernel/kevent/kevent.c line 174
Quoted 3 lines
kernel/kevent/kevent.c new file +2
-0+spin_lock_irqsave (&k->lock, flags); +spin_lock_irqsave (&k->user->ready_lock, flags);It also now creates lock nesting three deep, which starts to feel uncomfortable. Have you run this under lockdep? It might be fine, but this kind of depth means those of us auditing have to stare very very closely at the locks that paths acquire. The fewer locks that are held at a time, the better.
Above sequence is only possible when userspace modifies kevent which was not fired yet, and that modification ends up in immediat fire. When it is called from origin's state machine irqs are disabled only when kevent is moved into the ready queue, since that queue can be accessed from different CPU or from irq on the same one.
Quoted 9 lines
kernel/kevent/kevent.c line 197
Quoted 2 lines
kernel/kevent/kevent.c new file +2
-0+void kevent_storage_ready (struct kevent_storage *st, +kevent_callback_t ready_callback,u32 event)Hmm, the only caller that provides a callback is using it to call
kevent_break()on each event. Could that not be done by the helper itself if the caller provides the right event? Is there some more complicated use of the callback on the horizon? Not a big deal, but there are those who prefer to avoid code paths that nest lots of callbacks in sequence.
kevent users can call any callback they want here, for example it can mark all events as ready, not only broken, or all mark as one-shot if origin is going to be removed and so on.
Quoted 5 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ struct kevent *k, *n;In general, it's nicer to user longer names please. You'll notice throughout the kernel that we use things like dentry, inode, page, sock, etc, instead of d, i, p, and s.
But we have 'sk' :)
Quoted 4 lines
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+struct kevent *kevent_alloc (gfp_t mask) +{ +return kmem_cache_alloc (kevent_cache, mask); +}
Quoted 8 lines
kernel/kevent/kevent.c line 234
Quoted 5 lines
kernel/kevent/kevent.c new file +5
-0+ +void kevent_free (struct kevent *k) +{ +kmem_cache_free (kevent_cache, k); +}We probably don't need these wrappers, just call the kmem_cache_* functions directly.
Ok.
Quoted 4 lines
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+static unsigned int kevent_user_poll (struct file *file,struct poll_table_struct *wait) +{ +struct kevent_user *u = file->private_data; +unsigned int mask;
Quoted 8 lines
kernel/kevent/kevent_user.c line 75
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+ +poll_wait (file, &u->wait, wait); + mask =0 ; + +if (u->ready_num) + mask |=POLLIN |POLLRDNORM ;Shouldn't this be testing
ready_numwhile holding theready_lock?
Integer read is atomic, so no need to wrap a lock around it.
Quoted 7 lines
kernel/kevent/kevent_user.c line 98
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+for (i=0 ; i<KEVENT_HASH_MASK +1 ; ++i) { +INIT_LIST_HEAD (&u->kqueue[i].kevent_list); +spin_lock_init (&u->kqueue[i].kevent_lock); + }
ARRAY_SIZE(u->kqueue)should probably be used instead of trying to keep (KEVENT_HASH_MASK+ 1) in sync with the kqueue definition.
Ok.
Quoted 11 lines
kernel/kevent/kevent_user.c line 134
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+static inline void kevent_user_put (struct kevent_user *u) +{ +if (atomic_dec_and_test (&u->refcnt)) { +#ifdef CONFIG_KEVENT_USER_STAT +printk ("%s: u=%p, wait=%lu, immediately=%lu, total=%lu.\n" , + __func__, u, u->wait_num, u->im_num, u->total); +#endif
printk()seems like a poor choice if this is meant to be a more formal functionality. If it's just a debugging aid thenpr_debug()and DEBUG might be nicer.
Yes, I will wrap it into some nice function.
Quoted 14 lines
kernel/kevent/kevent_user.c line 161
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+/* +* Remove kevent from user's list of all events, +* dequeue it from storage and decrease user's reference counter, +* since this kevent does not exist anymore. That is why it is freed here. +*/ +static void kevent_finish_user (struct kevent *k,int lock,int deq)How about providing locked and unlocked prototypes instead of an argument that says whether to lock or not? You know, the usual:
void __thingy() { doit(); }
Quoted 5 lines
void thingy() {
lock();
__thingy();
unlock();
}Ok.
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ list_del(&k->kevent_entry); + u->kevent_num--;I wonder if these shouldn't get micro helpers that then have BUG_ON()s to test bad conditions. like
BUG_ON(list_empty() && kevent_num), that sort of thing.
Well, if list is empty that means that kevent_entry has a broken links, which will will fire on list_del.
And having a lot of BUGs is not a good sign.
But I do not care much actually about it, let's have couple...
Quoted 10 lines
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+static struct kevent *__kqueue_dequeue_one_ready (struct list_head *q, +unsigned int *qlen) +{ +struct kevent *k =NULL ; +unsigned int len = *qlen; + +if (len && !list_empty (q)) { + k = list_entry(q->next, struct kevent, ready_entry); +list_del (&k->ready_entry); + *qlen = len -1 ;
Quoted 8 lines
kernel/kevent/kevent_user.c line 209
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ } + +return k; +}Hmm, this is only called in one place? I'd either make the
list_headand lock into one struct (like structsk_buff_head) or hoist the code into the caller.
No problem.
Quoted 10 lines
kernel/kevent/kevent_user.c line 235
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+list_for_each_entry (k, &l->kevent_list, kevent_entry) { +spin_lock (&k->lock); +if (k->event.user[0 ] == uk->user[0 ] && k->event.user[1 ] == uk->user[1 ] && + k->event.id.raw[0 ] == uk->id.raw[0 ] && + k->event.id.raw[1 ] == uk->id.raw[1 ]) { + found =1 ; +spin_unlock (&k->lock);Ahhh, it's fs/aio.c:lookup_kiocb() all over again :) :). I guess we'll get this in a hash, or something, before merging.
Please note that it is searching inside hash bucket :)
Quoted 8 lines
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+mutex_lock (&u->ctl_mutex); + +for (i=0 ; i<ctl->num; ++i) { +if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + err = -EINVAL ; +break ; + } +
Quoted 8 lines
kernel/kevent/kevent_user.c line 324
Quoted 1 line
kernel/kevent/kevent_user.c new file +1
-0+if (kevent_modify (&uk, u))So there are a bunch of these. The internal list and kevents and such have their own locks. What are the mutexes serializing? Why can't we rely on the finger-grained object locking to make sure that concurrent operations behave resonably? One can imagine wanting to modify two kevents in a context that have nothing to do with each other and not wanting to serialize them at the context.
Each lock is hosted inside a bucket, but kevent itself is protected by that mutex. lock is being held for relatively small operations, but mutex protects against the whole sequence of them (select bucket, search, hash and so on).
Quoted 8 lines
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+int kevent_user_add_ukevent (struct ukevent *uk,struct kevent_user *u) +{ +struct kevent *k; +int err; + + k =kevent_alloc (GFP_KERNEL ); +if (!k) { + err = -ENOMEM ;
Quoted 8 lines
kernel/kevent/kevent_user.c line 384
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ goto err_out_exit; + } + +memcpy (&k->event, uk,sizeof (struct ukevent ));This path is copying the ukevents twice. First from userspace back up in
kevent_user_ctl_add()and then here into the kevent. We should rework things a bit so that we only copy it once.
struct ukevent here can be allocated in stack and filled by naio or aio. I would not allow them to allocate and link kevents by itself, so I created this function.
Quoted 6 lines
kernel/kevent/kevent_user.c line 396
Quoted 3 lines
kernel/kevent/kevent_user.c new file +3
-0+#ifdef CONFIG_KEVENT_USER_STAT + u->total++; +#endif FWIW, this could hide behind some
kevent_user_stat_inc(u)that could be ifdefed away in the header.
Ok.
Quoted 6 lines
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+ { +unsigned long flags; +unsigned int hash =kevent_user_hash (&k->event); +struct kevent_list *l = &u->kqueue[hash]; + +spin_lock_irqsave (&l->kevent_lock, flags);
Quoted 8 lines
kernel/kevent/kevent_user.c line 407
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+list_add_tail (&k->kevent_entry, &l->kevent_list); + u->kevent_num++; +kevent_user_get (u); +spin_unlock_irqrestore (&l->kevent_lock, flags); + }Hmm, please don't indent things like this. Add a little helper function or hoist the locals up into the main function and lose the braces.
Ok.
Quoted 10 lines
kernel/kevent/kevent_user.c line 444
Quoted 6 lines
kernel/kevent/kevent_user.c new file +5
-0+static int kevent_user_ctl_add (struct kevent_user *u, +struct kevent_user_control *ctl,void __user *arg) +{ + orig = arg; + ctl_addr = arg -sizeof (struct kevent_user_control );Ugh. This is more awkwardness that comes from packing the system call arguments in neighbouring structs behind a void *. We should really have explicit typed args.
kevent_user_control will be replaced with explicit syscall parameters, so it will be removed.
Quoted 6 lines
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+for (i=0 ; i<ctl->num; ++i) { +if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + } + arg +=sizeof (struct ukevent );
Quoted 8 lines
kernel/kevent/kevent_user.c line 457
Quoted 2 lines
kernel/kevent/kevent_user.c new file +2
-0+ + err =kevent_user_add_ukevent (&uk, u);There are some users that will want to add thousands of events at a time. (Like, say, a certain database writing back lots of cached dirtied database blocks.) I wonder if we should arrange this so that we can get some batching done and reduce the amount of lock traffic per event added.
Well, it is possible with additional GPF_KERNEL allocation of the buffer, do we want that cost?
Quoted 10 lines
Quoted 5 lines
+/*
+ * In nonblocking mode it returns as many events as possible, but not more than @max_nr.
+ * In blocking mode it waits until timeout or if at least @min_nr events are ready,
+ * if timeout is zero, than it waits no more than 1 second or if at least one event
+ * is ready.That's odd. Why not have a timeout of 0 mean a timeout of 0? Where did 1 second come from? :) It seems pretty crazy to require programmers to check that their timer math didn't just end up at 0 and magically tell the kernel to wait a second.
Zero timeout means that we want as much as we have, but not less than one kevent. So we sleep one second for them :)
I will use min_nr for that, i.e. if it is zero, than it meanst at least onee and less than max_nr.
Quoted 10 lines
kernel/kevent/kevent_user.c line 516
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+mutex_lock (&u->ctl_mutex); +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + }Again, this a great opportunity to copy more than one at a time with some refactoring.
If we are going to remove ability to get events by ssycall it will be pure memcpy without additional overhead.
Quoted 7 lines
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+asmlinkage long sys_kevent_ctl (int fd,void __user *arg) +{ +int err = -EINVAL , fput_needed; +struct kevent_user_control ctl; +struct file *file; + +if (copy_from_user (&ctl, arg,sizeof (struct kevent_user_control )))
Quoted 8 lines
kernel/kevent/kevent_user.c line 636
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+return -EINVAL ; + +if (ctl.cmd ==KEVENT_CTL_INIT ) +return kevent_ctl_init ();Hmm. So we can get one of these fds both by opening the device file or by calling
_CTL_INIT(which then magically ignores the fd argument?). That seems confusing.
So we want additional syscall? :)
Quoted 1 line
Anyway, that's enough for now. I hope this helps.
Thanks Zach.
Quoted 1 line
- z
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerFrom: Evgeniy Polyakov <[email protected]>
Date: Wed, 2 Aug 2006 10:39:18 +0400
Quoted 1 line
u64 is not aligned, so I prefer to use u32 as much as possible.
We have aligned_u64 exactly for this purpose, netfilter makes use of it to avoid the x86_64 vs. x86 u64 alignment discrepency.
Quoted 7 lines · David Miller
From: Evgeniy Polyakov <[email protected]>
Date: Wed, 2 Aug 2006 10:39:18 +0400Quoted 1 line
u64 is not aligned, so I prefer to use u32 as much as possible.
We have
aligned_u64exactly for this purpose, netfilter makes use of it to avoid thex86_64vs. x86 u64 alignment discrepency.
Ok, I will use that type.
Signature · 1 line
Evgeniy Polyakov
cover kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovGeneric event handling mechanism.
I send this patchset for comments and review, it still contains AIO and aio_sendfile() implementation on top of get_block() abstraction, which was decided to postpone for a while (it is simpler right now to generate patchset as a whole, when kevent will be ready for merge, I will generate patchset without AIO stuff). It does not contain mapped buffer implementation, since it's design is not 100% completed, I will present that implementation in the third patchset.
Changes from 'take2' patchset:
* split kevent_finish_user() to locked and unlocked variants
* do not use KEVENT_STAT ifdefs, use inline functions instead
* use array of callbacks of each type instead of each kevent callback initialization
* changed name of ukevent guarding lock
* use only one kevent lock in kevent_user for all hash buckets instead of per-bucket locks
* do not use kevent_user_ctl structure instead provide needed arguments as syscall parameters
* various indent cleanups
* mapped buffer (initial) implementation (no userspace yet)
Changes from 'take1' patchset:
- rebased against 2.6.18-git tree
- removed ioctl controlling
- added new syscall kevent_get_events(int fd, unsigned int min_nr, unsigned int max_nr, unsigned int timeout, void __user *buf, unsigned flags)
- use old syscall kevent_ctl for creation/removing, modification and initial kevent initialization
- use mutuxes instead of semaphores
- added file descriptor check and return error if provided descriptor does not match kevent file operations
- various indent fixes
- removed aio_sendfile() declarations.
Thank you.
PATCH 1/4 kevent: Core files.
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
It might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
arch/i386/kernel/syscall_table.S +4 -0
@@ -317,3 +317,7 @@ ENTRY(sys_call_table) .long sys_tee /* 315 */ .long sys_vmsplice .long sys_move_pages + .long sys_aio_recv + .long sys_aio_send + .long sys_kevent_get_events + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +4 -0
@@ -713,4 +713,8 @@ #endif .quad sys_tee .quad compat_sys_vmsplice .quad compat_sys_move_pages + .quad sys_aio_recv + .quad sys_aio_send + .quad sys_kevent_get_events + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +5 -1
@@ -323,10 +323,14 @@ #define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316#define __NR_move_pages 317 +#define __NR_aio_recv 318 +#define __NR_aio_send 319 +#define __NR_kevent_get_events 320 +#define __NR_kevent_ctl 321#ifdef __KERNEL__ -#define NR_syscalls 318 +#define NR_syscalls 322/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/unistd.h +9 -1
@@ -619,10 +619,18 @@ #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice)#define __NR_move_pages 279 __SYSCALL(__NR_move_pages, sys_move_pages) +#define __NR_aio_recv 280 +__SYSCALL(__NR_aio_recv, sys_aio_recv) +#define __NR_aio_send 281 +__SYSCALL(__NR_aio_send, sys_aio_send) +#define __NR_kevent_get_events 282 +__SYSCALL(__NR_kevent_get_events, sys_kevent_get_events) +#define __NR_kevent_ctl 283 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_move_pages +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS
include/linux/kevent.h new file +277 -0
277 lines not on this page · raw message
include/linux/kevent_storage.h new file +12 -0
@@ -0,0 +1,12 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +unsigned int qlen;/* Number of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +5 -0
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_aio_recv (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_aio_send (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_kevent_get_events (int ctl_fd,unsigned int min,unsigned int max, +unsigned int timeout,void __user *buf,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,unsigned int cmd,unsigned int num,void __user *buf);#endif
init/Kconfig +2 -0
@@ -218,6 +218,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o
kernel/kevent/Kconfig new file +57 -0
57 lines not on this page · raw message
kernel/kevent/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +obj-y := kevent.o kevent_user.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
kernel/kevent/kevent.c new file +248 -0
248 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +876 -0
876 lines not on this page · raw message
kernel/sys_ni.c +5 -0
@@ -122,6 +122,11 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_aio_recv); +cond_syscall (sys_aio_send); +cond_syscall (sys_kevent_get_events); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
PATCH 2/4 kevent: AIO, aio_sendfile() implementation.
AIO, aio_sendfile() implementation.
This patch includes asynchronous propagation of file's data into VFS cache and aio_sendfile() implementation.
Network aio_sendfile() works lazily - it asynchronously populates pages into the VFS cache (which can be used for various tricks with adaptive readahead) and then uses usual ->sendfile() callback.
fs/bio.c +1 -1
@@ -119,7 +119,7 @@ void bio_free(struct bio *bio, struct bi/* * default destructor for a bio allocated with bio_alloc_bioset() */ -static void bio_fs_destructor (struct bio *bio) +void bio_fs_destructor (struct bio *bio) {bio_free (bio, fs_bio_set); }
fs/ext2/inode.c +1 -0
@@ -685,6 +685,7 @@ ext2_writepages(struct address_space *ma }const struct address_space_operations ext2_aops = { + .get_block = ext2_get_block, .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_writepage,
fs/ext3/inode.c +1 -0
@@ -1699,6 +1699,7 @@ static int ext3_journalled_set_page_dirt }static const struct address_space_operations ext3_ordered_aops = { + .get_block = ext3_get_block, .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_ordered_writepage,
fs/file_table.c +6 -0
@@ -112,6 +112,9 @@ struct file *get_empty_filp(void)if (security_file_alloc (f)) goto fail_sec; +#ifdef CONFIG_KEVENT_POLL +kevent_storage_init (f, &f->st); +#endif tsk = current;INIT_LIST_HEAD (&f->f_u.fu_list);atomic_set (&f->f_count,1 );
@@ -159,6 +162,9 @@ void fastcall __fput(struct file *file)might_sleep ();fsnotify_close (file); +#ifdef CONFIG_KEVENT_POLL +kevent_storage_fini (&file->st); +#endif /* * The function eventpoll_release() should be the first called * in the file cleanup chain.
fs/inode.c +7 -0
@@ -21,6 +21,7 @@ #include <linux/pagemap.h>#include <linux/cdev.h> #include <linux/bootmem.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/mount.h> /*
@@ -165,12 +166,18 @@ #endif }memset (&inode->u,0 ,sizeof (inode->u)); inode->i_mapping = mapping; +#if definedCONFIG_KEVENT +kevent_storage_init (inode, &inode->st); +#endif }return inode; }void destroy_inode (struct inode *inode) { +#if definedCONFIG_KEVENT_INODE || definedCONFIG_KEVENT_SOCKET +kevent_storage_fini (&inode->st); +#endif BUG_ON (inode_has_buffers (inode));security_inode_free (inode);if (inode->i_sb->s_op->destroy_inode)
fs/reiserfs/inode.c +1 -0
@@ -3001,6 +3001,7 @@ int reiserfs_setattr(struct dentry *dent }const struct address_space_operations reiserfs_address_space_operations = { + .get_block = reiserfs_get_block, .writepage = reiserfs_writepage, .readpage = reiserfs_readpage, .readpages = reiserfs_readpages,
include/linux/fs.h +12 -0
@@ -240,6 +240,9 @@ #include <linux/mutex.h>#include <asm/atomic.h> #include <asm/semaphore.h> #include <asm/byteorder.h> +#ifdef CONFIG_KEVENT +#include <linux/kevent_storage.h> +#endif struct hd_geometry ;struct iovec ;
@@ -352,6 +355,8 @@ struct address_space;struct writeback_control ;struct address_space_operations { +int (*get_block)(struct inode *inode,sector_t iblock, +struct buffer_head *bh_result,int create);int (*writepage)(struct page *page,struct writeback_control *wbc);int (*readpage)(struct file *,struct page *);void (*sync_page)(struct page *);
@@ -546,6 +551,10 @@ #ifdef CONFIG_INOTIFYstruct mutex inotify_mutex;/* protects the watches list */ #endif +#ifdef CONFIG_KEVENT_INODE +struct kevent_storage st; +#endif +unsigned long i_state;unsigned long dirtied_when;/* jiffies of first dirtying */
@@ -698,6 +707,9 @@ #ifdef CONFIG_EPOLLstruct list_head f_ep_links;spinlock_t f_ep_lock;#endif /* #ifdef CONFIG_EPOLL */ +#ifdef CONFIG_KEVENT_POLL +struct kevent_storage st; +#endif struct address_space *f_mapping; };extern spinlock_t files_lock;
include/linux/fsnotify.h +5 -0
@@ -15,6 +15,7 @@ #ifdef __KERNEL__#include <linux/dnotify.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/audit.h> /*
@@ -79,6 +80,7 @@ static inline void fsnotify_nameremove(s isdir =IN_ISDIR ;dnotify_parent (dentry,DN_DELETE );inotify_dentry_parent_queue_event (dentry,IN_DELETE |isdir,0 , dentry->d_name.name); +kevent_inode_notify_parent (dentry,KEVENT_INODE_REMOVE ); }/*
@@ -88,6 +90,7 @@ static inline void fsnotify_inoderemove( {inotify_inode_queue_event (inode,IN_DELETE_SELF ,0 ,NULL ,NULL );inotify_inode_is_dead (inode); +kevent_inode_remove (inode); }/*
@@ -96,6 +99,7 @@ static inline void fsnotify_inoderemove(static inline void fsnotify_create (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
@@ -107,6 +111,7 @@ static inline void fsnotify_create(strucstatic inline void fsnotify_mkdir (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE |IN_ISDIR ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
kernel/kevent/kevent_aio.c new file +584 -0
584 lines not on this page · raw message
kernel/kevent/kevent_inode.c new file +114 -0
114 lines not on this page · raw message
PATCH 3/4 kevent: Network AIO, socket notifications.
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/asm-i386/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/asm-x86_64/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/linux/skbuff.h +2 -0
@@ -1283,6 +1283,8 @@ extern struct sk_buff *skb_recv_datagram int noblock, int *err);extern unsigned int datagram_poll (struct file *file,struct socket *sock,struct poll_table_struct *wait); +extern int skb_copy_datagram (const struct sk_buff *from, +int offset,void *dst,int size);extern int skb_copy_datagram_iovec (const struct sk_buff *from,int offset,struct iovec *to,int size);
include/net/sock.h +25 -15
@@ -48,6 +48,7 @@ #include <linux/lockdep.h>#include <linux/netdevice.h> #include <linux/skbuff.h> /* struct sk_buff */ #include <linux/security.h> +#include <linux/kevent.h> #include <linux/filter.h>
@@ -391,6 +392,8 @@ enum sock_flags {SOCK_RCVTSTAMP ,/* %SO_TIMESTAMP setting */ SOCK_LOCALROUTE ,/* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK ,/* write queue has been shrunk recently */ +SOCK_ASYNC , + SOCK_ASYNC_INUSE, };static inline void sock_copy_flags (struct sock *nsk,struct sock *osk)
@@ -450,6 +453,21 @@ static inline int sk_stream_memory_free(extern void sk_stream_rfree (struct sk_buff *skb); +struct socket_alloc { +struct socket socket; +struct inode vfs_inode; +}; + +static inline struct socket *SOCKET_I (struct inode *inode) +{ + return &container_of(inode, struct socket_alloc, vfs_inode)->socket; +} + +static inline struct inode *SOCK_INODE (struct socket *socket) +{ + return &container_of(socket, struct socket_alloc, socket)->vfs_inode; +} +static inline void sk_stream_set_owner_r (struct sk_buff *skb,struct sock *sk) { skb->sk = sk;
@@ -477,6 +495,7 @@ static inline void sk_add_backlog(struct sk->sk_backlog.tail = skb; } skb->next =NULL ; +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); }#define sk_wait_event (__sk, __timeo, __condition) \
@@ -548,6 +567,12 @@ struct proto {int (*backlog_rcv) (struct sock *sk,struct sk_buff *skb); + +int (*async_recv) (struct sock *sk, +void *dst,size_t size); +int (*async_send) (struct sock *sk, +struct page **pages,unsigned int poffset, +size_t size);/* Keeping track of sk's, looking them up, and port selection methods. */ void (*hash)(struct sock *sk);
@@ -679,21 +704,6 @@ static inline struct kiocb *siocb_to_kioreturn si->kiocb; } -struct socket_alloc { -struct socket socket; -struct inode vfs_inode; -}; - -static inline struct socket *SOCKET_I (struct inode *inode) -{ - return &container_of(inode, struct socket_alloc, vfs_inode)->socket; -} - -static inline struct inode *SOCK_INODE (struct socket *socket) -{ - return &container_of(socket, struct socket_alloc, socket)->vfs_inode; -} -extern void __sk_stream_mem_reclaim (struct sock *sk);extern int sk_stream_mem_schedule (struct sock *sk,int size,int kind);
include/net/tcp.h +3 -0
@@ -364,6 +364,8 @@ extern int compat_tcp_setsockopt(struc int level, int optname, char __user *optval, int optlen);extern void tcp_set_keepalive (struct sock *sk,int val); +extern int tcp_async_recv (struct sock *sk,void *dst,size_t size); +extern int tcp_async_send (struct sock *sk,struct page **pages,unsigned int poffset,size_t size); extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
@@ -857,6 +859,7 @@ static inline int tcp_prequeue(struct so tp->ucopy.memory =0 ; }else if (skb_queue_len (&tp->ucopy.prequeue) ==1 ) {wake_up_interruptible (sk->sk_sleep); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND );if (!inet_csk_ack_scheduled (sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, (3 * TCP_RTO_MIN) / 4,
kernel/kevent/kevent_naio.c new file +242 -0
242 lines not on this page · raw message
kernel/kevent/kevent_socket.c new file +128 -0
128 lines not on this page · raw message
net/core/datagram.c +55 -0
67 lines not on this page · raw message
net/core/sock.c +17 -1
@@ -617,6 +617,16 @@ #endifspin_unlock_bh (&sk->sk_lock.slock); ret = -ENONET ;break ; +#ifdef CONFIG_KEVENT_SOCKET +case SO_ASYNC_SOCK : +spin_lock_bh (&sk->sk_lock.slock); +if (valbool) +sock_set_flag (sk,SOCK_ASYNC ); +else +sock_reset_flag (sk,SOCK_ASYNC ); +spin_unlock_bh (&sk->sk_lock.slock); +break ; +#endif case SO_PASSSEC :if (valbool)
@@ -1406,6 +1416,7 @@ static void sock_def_wakeup(struct sockif (sk->sk_sleep &&waitqueue_active (sk->sk_sleep))wake_up_interruptible_all (sk->sk_sleep);read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_error_report (struct sock *sk)
@@ -1415,6 +1426,7 @@ static void sock_def_error_report(structwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,0 ,POLL_ERR );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_readable (struct sock *sk,int len)
@@ -1424,6 +1436,7 @@ static void sock_def_readable(struct socwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,1 ,POLL_IN );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_write_space (struct sock *sk)
@@ -1443,6 +1456,7 @@ static void sock_def_write_space(struct }read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); }static void sock_def_destruct (struct sock *sk)
@@ -1559,8 +1573,10 @@ void fastcall release_sock(struct sock *if (sk->sk_backlog.tail)__release_sock (sk); sk->sk_lock.owner =NULL ; -if (waitqueue_active (&sk->sk_lock.wq)) +if (waitqueue_active (&sk->sk_lock.wq)) {wake_up (&sk->sk_lock.wq); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); + }spin_unlock_bh (&sk->sk_lock.slock); }EXPORT_SYMBOL (release_sock);
net/core/stream.c +1 -0
@@ -36,6 +36,7 @@ void sk_stream_write_space(struct sock *wake_up_interruptible (sk->sk_sleep);if (sock->fasync_list && !(sk->sk_shutdown &SEND_SHUTDOWN ))sock_wake_async (sock,2 ,POLL_OUT ); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); } }
net/ipv4/tcp.c +298 -0
316 lines not on this page · raw message
net/ipv4/tcp_input.c +3 -1
@@ -3112,6 +3112,7 @@ static void tcp_ofo_queue(struct sock *s__skb_unlink (skb, &tp->out_of_order_queue);__skb_queue_tail (&sk->sk_receive_queue, skb); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); tp->rcv_nxt =TCP_SKB_CB (skb)->end_seq;if (skb->h.th->fin)tcp_fin (skb, sk, skb->h.th);
@@ -3955,7 +3956,8 @@ int tcp_rcv_established(struct sock *sk,int copied_early =0 ;if (tp->copied_seq == tp->rcv_nxt && - len - tcp_header_len <= tp->ucopy.len) { + len - tcp_header_len <= tp->ucopy.len && + !sock_async (sk)) {#ifdef CONFIG_NET_DMA if (tcp_dma_try_early_copy (sk, skb, tcp_header_len)) { copied_early =1 ;
net/ipv4/tcp_ipv4.c +25 -15
@@ -61,6 +61,7 @@ #include <linux/cache.h>#include <linux/jhash.h> #include <linux/init.h> #include <linux/times.h> +#include <linux/kevent.h> #include <net/icmp.h> #include <net/inet_hashtables.h>
@@ -868,6 +869,7 @@ #endifreqsk_free (req); }else {inet_csk_reqsk_queue_hash_add (sk, req,TCP_TIMEOUT_INIT ); +kevent_socket_notify (sk,KEVENT_SOCKET_ACCEPT ); }return 0 ;
@@ -1108,24 +1110,30 @@ process: skb->dev =NULL ; -bh_lock_sock_nested (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock_nested (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan =get_softnet_dma (); -if (tp->ucopy.dma_chan) - ret =tcp_v4_do_rcv (sk, skb); - else +struct tcp_sock *tp =tcp_sk (sk); +if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan =get_softnet_dma (); +if (tp->ucopy.dma_chan) + ret =tcp_v4_do_rcv (sk, skb); + else#endif - { -if (!tcp_prequeue (sk, skb)) - ret =tcp_v4_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v4_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);
@@ -1849,6 +1857,8 @@ struct proto tcp_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v4_do_rcv, .hash = tcp_v4_hash, .unhash = tcp_unhash,
net/ipv6/tcp_ipv6.c +21 -13
@@ -1230,22 +1230,28 @@ process: skb->dev =NULL ; -bh_lock_sock (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (tp->ucopy.dma_chan) - ret =tcp_v6_do_rcv (sk, skb); - else -#endif - { -if (!tcp_prequeue (sk, skb)) +struct tcp_sock *tp =tcp_sk (sk); +if (tp->ucopy.dma_chan) ret =tcp_v6_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + else +#endif + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v6_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);return ret ?-1 :0 ;
@@ -1596,6 +1602,8 @@ struct proto tcpv6_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v6_do_rcv, .hash = tcp_v6_hash, .unhash = tcp_unhash,
PATCH 4/4 kevent: poll/select() notifications. Timer notifications.
poll/select() notifications. Timer notifications.
This patch includes generic poll/select and timer notifications.
kevent_poll works simialr to epoll and has the same issues (callback is invoked not from internal state machine of the caller, but through process awake).
Timer notifications can be used for fine grained per-process time management, since iteractive timers are very inconveniently to use, and they are limited.
kernel/kevent/kevent_poll.c new file +217 -0
@@ -0,0 +1,217 @@ +/* +* kevent_poll.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/file.h> +#include <linux/kevent.h> +#include <linux/poll.h> +#include <linux/fs.h> + +static kmem_cache_t *kevent_poll_container_cache; +static kmem_cache_t *kevent_poll_priv_cache; + +struct kevent_poll_ctl +{ +struct poll_table_struct pt; +struct kevent *k; +}; + +struct kevent_poll_wait_container +{ +struct list_head container_entry; +wait_queue_head_t *whead; +wait_queue_t wait; +struct kevent *k; +}; + +struct kevent_poll_private +{ +struct list_head container_list; +spinlock_t container_lock; +}; + +static int kevent_poll_enqueue (struct kevent *k); +static int kevent_poll_dequeue (struct kevent *k); +static int kevent_poll_callback (struct kevent *k); + +static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ +struct kevent_poll_wait_container *cont = + container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +unsigned long flags; +u32 revents, event; + + revents = file->f_op->poll (file,NULL ); +spin_lock_irqsave (&k->ulock, flags); + event = k->event.event; +spin_unlock_irqrestore (&k->ulock, flags); + +kevent_storage_ready (k->st,NULL , revents); + +return 0 ; +} + +static void kevent_poll_qproc (struct file *file,wait_queue_head_t *whead, +struct poll_table_struct *poll_table) +{ +struct kevent *k = + container_of(poll_table, struct kevent_poll_ctl, pt)->k; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *cont; +unsigned long flags; + + cont =kmem_cache_alloc (kevent_poll_container_cache,SLAB_KERNEL ); +if (!cont) { +kevent_break (k); +return ; + } + + cont->k = k; +init_waitqueue_func_entry (&cont->wait, kevent_poll_wait_callback); + cont->whead = whead; + +spin_lock_irqsave (&priv->container_lock, flags); +list_add_tail (&cont->container_entry, &priv->container_list); +spin_unlock_irqrestore (&priv->container_lock, flags); + +add_wait_queue (whead, &cont->wait); +} + +static int kevent_poll_enqueue (struct kevent *k) +{ +struct file *file; +int err, ready =0 ; +unsigned int revents; +struct kevent_poll_ctl ctl; +struct kevent_poll_private *priv; + + file =fget (k->event.id.raw[0 ]); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_op || !file->f_op->poll) + goto err_out_fput; + + err = -ENOMEM ; + priv =kmem_cache_alloc (kevent_poll_priv_cache,SLAB_KERNEL ); +if (!priv) + goto err_out_fput; + +spin_lock_init (&priv->container_lock); +INIT_LIST_HEAD (&priv->container_list); + + k->priv = priv; + + ctl.k = k; +init_poll_funcptr (&ctl.pt, &kevent_poll_qproc); + + err =kevent_storage_enqueue (&file->st, k); +if (err) + goto err_out_free; + + revents = file->f_op->poll (file, &ctl.pt); +if (revents & k->event.event) { + ready =1 ; +kevent_poll_dequeue (k); + } + +return ready; + +err_out_free: +kmem_cache_free (kevent_poll_priv_cache, priv); +err_out_fput: +fput (file); +return err; +} + +static int kevent_poll_dequeue (struct kevent *k) +{ +struct file *file = k->st->origin; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *w, *n; +unsigned long flags; + +kevent_storage_dequeue (k->st, k); + +spin_lock_irqsave (&priv->container_lock, flags); +list_for_each_entry_safe (w, n, &priv->container_list, container_entry) { +list_del (&w->container_entry); +remove_wait_queue (w->whead, &w->wait); +kmem_cache_free (kevent_poll_container_cache, w); + } +spin_unlock_irqrestore (&priv->container_lock, flags); + +kmem_cache_free (kevent_poll_priv_cache, priv); + k->priv =NULL ; + +fput (file); + +return 0 ; +} + +static int kevent_poll_callback (struct kevent *k) +{ +struct file *file = k->st->origin; +unsigned int revents = file->f_op->poll (file,NULL ); +return (revents & k->event.event); +} + +static int __init kevent_poll_sys_init (void ) +{ +struct kevent_callbacks *pc = &kevent_registered_callbacks[KEVENT_POLL ]; + + kevent_poll_container_cache =kmem_cache_create ("kevent_poll_container_cache" , +sizeof (struct kevent_poll_wait_container ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_container_cache) { +printk (KERN_ERR "Failed to create kevent poll container cache.\n" ); +return -ENOMEM ; + } + + kevent_poll_priv_cache =kmem_cache_create ("kevent_poll_priv_cache" , +sizeof (struct kevent_poll_private ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_priv_cache) { +printk (KERN_ERR "Failed to create kevent poll private data cache.\n" ); +kmem_cache_destroy (kevent_poll_container_cache); + kevent_poll_container_cache =NULL ; +return -ENOMEM ; + } + + pc->enqueue = &kevent_poll_enqueue; + pc->dequeue = &kevent_poll_dequeue; + pc->callback = &kevent_poll_callback; + +printk (KERN_INFO "Kevent poll()/select() subsystem has been initialized.\n" ); +return 0 ; +} + +static void __exit kevent_poll_sys_fini(void) +{ +kmem_cache_destroy (kevent_poll_priv_cache); +kmem_cache_destroy (kevent_poll_container_cache); +} + +module_init (kevent_poll_sys_init); +module_exit (kevent_poll_sys_fini);
kernel/kevent/kevent_timer.c new file +116 -0
116 lines not on this page · raw message
Re: [take3 4/4] kevent: poll/select() notifications. Timer notifications.
Quoted 8 lines · Evgeniy Polyakov
poll/select() notifications. Timer notifications.
+++
b/kernel/kevent/kevent_poll.ckernel/kevent/kevent_poll.c new file +4
-0+static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ + struct kevent_poll_wait_container *cont =
kernel/kevent/kevent_poll.c line 69
Quoted 10 lines
kernel/kevent/kevent_poll.c new file +10
-0+ container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +unsigned long flags; +u32 revents, event; + + revents = file->f_op->poll (file,NULL ); +spin_lock_irqsave (&k->ulock, flags); + event = k->event.event; +spin_unlock_irqrestore (&k->ulock, flags);
Not sure why you take a spinlock just to read a u32
Eric
Re: [take3 4/4] kevent: poll/select() notifications. Timer notifications.
Quoted 5 lines · Eric Dumazet
Quoted 3 lines · Evgeniy Polyakov
poll/select() notifications. Timer notifications.
+++
b/kernel/kevent/kevent_poll.c
Quoted 16 lines
Quoted 4 lines
kernel/kevent/kevent_poll.c new file +4
-0+static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ + struct kevent_poll_wait_container *cont =kernel/kevent/kevent_poll.c line 69
Quoted 10 lines
kernel/kevent/kevent_poll.c new file +10
-0+ container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +unsigned long flags; +u32 revents, event; + + revents = file->f_op->poll (file,NULL ); +spin_lock_irqsave (&k->ulock, flags); + event = k->event.event; +spin_unlock_irqrestore (&k->ulock, flags);Not sure why you take a spinlock just to read a u32
You are right, it is not needed there.
Thank you.
Quoted 1 line
Eric
Signature · 1 line
Evgeniy Polyakov
Re: [take3 3/4] kevent: Network AIO, socket notifications.
In reply to Evgeniy Polyakovkernel/kevent/kevent_naio.c
Quoted 7 lines · Evgeniy Polyakov
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/net/tcp.h +0
-0(3 * TCP_RTO_MIN) / 4,kernel/kevent/kevent_naio.c +0
-0
+
Quoted 8 lines
kernel/kevent/kevent_naio.c +8
-0+static int kevent_naio_enqueue (struct kevent *k) +{ +int err, i; +struct page **page; +void *addr; +unsigned int size = k->event.id.raw[1 ]; +int num = size/PAGE_SIZE ; +struct file *file;
kernel/kevent/kevent_naio.c line 99
Quoted 10 lines
kernel/kevent/kevent_naio.c +10
-0+struct sock *sk =NULL ; +int fput_needed; + + file =fget_light (k->event.id.raw[0 ], &fput_needed); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_dentry || !file->f_dentry->d_inode) + goto err_out_fput;
How can you be 100% sure this file is actually a socket here ? (Another thread could close the fd and this fd can now point to another file)
You should do
if (file->f_op != &socket_file_ops)
goto err_out_fput;
sk = file->private_data; /* set in sock_map_fd */
kernel/kevent/kevent_naio.c line 102
Quoted 3 lines
kernel/kevent/kevent_naio.c +3
-0+ + sk =SOCKET_I (file->f_dentry->d_inode)->sk; +
Eric
Re: [take3 3/4] kevent: Network AIO, socket notifications.
Quoted 18 lines · Eric Dumazet
kernel/kevent/kevent_naio.c line 99
Quoted 13 lines · Evgeniy Polyakov
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
kernel/kevent/kevent_naio.c new file +7
-0+ file =fget_light (k->event.id.raw[0 ], &fput_needed); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_dentry || !file->f_dentry->d_inode) + goto err_out_fput;How can you be 100% sure this file is actually a socket here ? (Another thread could close the fd and this fd can now point to another file)
Quoted 4 lines
You should do
if (file->f_op!=&socket_file_ops)goto err_out_fput;sk =
file->private_data; /* set insock_map_fd*/
That will be socket, not sock, but that check is definitely needed in both socket and network aio code.
Thanks Eric.
Quoted 1 line
Eric
Signature · 1 line
Evgeniy Polyakov
Re: [take3 2/4] kevent: AIO, aio_sendfile() implementation.
In reply to Evgeniy PolyakovQuoted 13 lines · Evgeniy Polyakov
AIO,
aio_sendfile()implementation.This patch includes asynchronous propagation of file's data into VFS cache and
aio_sendfile()implementation.
Networkaio_sendfile()works lazily - it asynchronously populates pages into the VFS cache (which can be used for various tricks with adaptive readahead) and then uses usual ->sendfile() callback....
kernel/kevent/kevent_aio.c line 3
Quoted 3 lines
kernel/kevent/kevent_aio.c new file +3
-0@@ -0,0 +1,584 @@ +/* +* kevent_aio.c +*
Since this is *almost* same as mpage.c code, wondering if its possible to make common generic/helper routines in mpage.c and use it here ?
Thanks,
Badari
Re: [take3 2/4] kevent: AIO, aio_sendfile() implementation.
Quoted 9 lines · Badari Pulavarty
Quoted 8 lines · Evgeniy Polyakov
AIO,
aio_sendfile()implementation.This patch includes asynchronous propagation of file's data into VFS cache and
aio_sendfile()implementation.
Networkaio_sendfile()works lazily - it asynchronously populates pages into the VFS cache (which can be used for various tricks with adaptive readahead) and then uses usual ->sendfile() callback.
Quoted 11 lines
kernel/kevent/kevent_aio.c line 3
Quoted 8 lines
...
kernel/kevent/kevent_aio.c new file +3
-0@@ -0,0 +1,584 @@ +/* +* kevent_aio.c +* Since this is *almost* same as
mpage.ccode, wondering if its possible to make common
generic/helper routines inmpage.cand use it here ?
Yes, as I mentioned in mail to Christoph, I did it just to separate kevent as much as possible (so I introduced ->get_block() based approach). It can be safely moved into mpage code and used from more clear callback like ->readpage().
Since this AIO code was decided to be postponed for a while, I'm not updating it (just make sure that it compiles with new changes), since overall design of AIO changes (if any) is not 100% completed.
Quoted 2 lines
Thanks,
Badari
Signature · 1 line
Evgeniy Polyakov
Re: [take3 1/4] kevent: Core files.
In reply to Evgeniy PolyakovQuoted 12 lines · Evgeniy Polyakov
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machineskernel/kevent/kevent_user.c new file +3
-0+static int kevent_user_wait (struct file *file,struct kevent_user *u, +unsigned int min_nr,unsigned int max_nr,unsigned int timeout, +void __user *buf)
kernel/kevent/kevent_user.c line 689
Quoted 10 lines
kernel/kevent/kevent_user.c new file +8
-0+{ +mutex_lock (&u->ctl_mutex); +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + }
It seems quite wrong to hold ctl_mutex while doing a copy_to_user() (of possibly a large amount of data) : A thread can sleep on a page fault and other threads cannot make progress.
Eric
Re: [take3 1/4] kevent: Core files.
Quoted 4 lines · Eric Dumazet
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+mutex_lock (&u->ctl_mutex); +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) {
Quoted 8 lines
kernel/kevent/kevent_user.c line 632
Quoted 3 lines
kernel/kevent/kevent_user.c new file +3
-0+ cerr = -EINVAL ; +break ; + }It seems quite wrong to hold
ctl_mutexwhile doing acopy_to_user()(of possibly a large amount of data) : A thread can sleep on a page fault and other threads cannot make progress.
I would not call that wrong - system prevents some threads from removing kevents which are counted to be transfered to the userspace, i.e. when dequeuing was awakened and it had seen some events it is possible, that when it will dequeue them part will be removed by other thread, so I prevent this.
Quoted 1 line
Eric
Signature · 1 line
Evgeniy Polyakov
Re: [take3 1/4] kevent: Core files.
Quoted 1 line · Evgeniy Polyakov
On Thu, Aug 03, 2006 at 04:40:34PM +0200, Eric Dumazet ([email protected])
Quoted 12 lines · wrote:
Quoted 11 lines
kernel/kevent/kevent_user.c line 689
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+mutex_lock (&u->ctl_mutex); +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + }It seems quite wrong to hold
ctl_mutexwhile doing acopy_to_user()(of possibly a large amount of data) : A thread can sleep on a page fault and other threads cannot make progress.
Quoted 5 lines
I would not call that wrong - system prevents some threads from removing kevents which are counted to be transfered to the userspace, i.e. when dequeuing was awakened and it had seen some events it is possible, that when it will dequeue them part will be removed by other thread, so I prevent this.
Hum, "wrong" was maybe not the good word.... but kqueue_dequeue_ready() uses a spinlock (ready_lock) to protect ready_list. One particular struct kevent is given to one thread, one at a time.
If you look at fs/eventpoll.c, you can see how carefull is ep_send_events() so that multiple threads can in the same time transfer different items to user memory.
In a model where several threads are servicing events collected by a single point (epoll, or kevent), this is important to not block all threads because of a single thread waiting a swapin (trigered by copy_to_user() )
Eric
Re: [take3 1/4] kevent: Core files.
Quoted 21 lines · Eric Dumazet
Quoted 1 line · Evgeniy Polyakov
On Thu, Aug 03, 2006 at 04:40:34PM +0200, Eric Dumazet ([email protected])
Quoted 12 lines · wrote:
Quoted 11 lines
kernel/kevent/kevent_user.c line 689
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+mutex_lock (&u->ctl_mutex); +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + }It seems quite wrong to hold
ctl_mutexwhile doing acopy_to_user()(of possibly a large amount of data) : A thread can sleep on a page fault and other threads cannot make progress.Quoted 5 lines
I would not call that wrong - system prevents some threads from removing kevents which are counted to be transfered to the userspace, i.e. when dequeuing was awakened and it had seen some events it is possible, that when it will dequeue them part will be removed by other thread, so I prevent this.
Quoted 3 lines
Hum, "wrong" was maybe not the good word.... but
kqueue_dequeue_ready()uses a spinlock (ready_lock) to protectready_list. One particular struct kevent is given to one thread, one at a time.
I mean that wait_event logic will see that there are requested number of events, and when it starts to get them, it is possible that there will be no events at all.
Quoted 3 lines
If you look at
fs/eventpoll.c, you can see how carefull isep_send_events()so that multiple threads can in the same time transfer different items to user memory.
It is done under the same logic under ep->sem semaphore, which is being held for del and read operations.
Or do you mean to have rw semahore instead of mutex here?
Quoted 5 lines
In a model where several threads are servicing events collected by a single point (epoll, or kevent), this is important to not block all threads because of a single thread waiting a swapin (trigered by
copy_to_user())Eric
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take3 1/4] kevent: Core files.
In reply to Evgeniy PolyakovFrom: Evgeniy Polyakov <[email protected]>
Date: Thu, 3 Aug 2006 18:55:57 +0400
Quoted 5 lines
I would not call that wrong - system prevents some threads from removing kevents which are counted to be transfered to the userspace, i.e. when dequeuing was awakened and it had seen some events it is possible, that when it will dequeue them part will be removed by other thread, so I prevent this.
Queue is all that matters to be synchronized, so it seems better to have a mutex on the queue rather than a global one. That way, user can only hurt himself.
Re: [take3 0/4] kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovQuoted 9 lines · Evgeniy Polyakov
Changes from 'take2' patchset:
* splitkevent_finish_user()to locked and unlocked variants
* do not useKEVENT_STATifdefs, use inline functions instead
* use array of callbacks of each type instead of each kevent callback initialization
* changed name of ukevent guarding lock
* use only one kevent lock inkevent_userfor all hash buckets instead of per-bucket locks
* do not usekevent_user_ctlstructure instead provide needed arguments as syscall parameters
* various indent cleanups
* mapped buffer (initial) implementation (no userspace yet)
Also added optimisation aimed to help when a lot of kevents are being copied from userspace in one syscall.
Signature · 1 line
Evgeniy Polyakov
cover kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovGeneric event handling mechanism.
I send this patchset for comments and review, it still contains AIO and aio_sendfile() implementation on top of get_block() abstraction, which was decided to postpone for a while (it is simpler right now to generate patchset as a whole, when kevent will be ready for merge, I will generate patchset without AIO stuff). It does not contain mapped buffer implementation, since it's design is not 100% completed, I will present that implementation in the third patchset.
Changes from 'take3' patchset:
* removed serializing mutex from kevent_user_wait()
* moved storage list processing to RCU
* removed lockdep screaming - all storage locks are initialized in the same function, so it was learned to differentiate between various cases
* remove kevent from storage if is marked as broken after callback
* fixed a typo in mmaped buffer implementation which would end up in wrong index calcualtion
Changes from 'take2' patchset:
* split kevent_finish_user() to locked and unlocked variants
* do not use KEVENT_STAT ifdefs, use inline functions instead
* use array of callbacks of each type instead of each kevent callback initialization
* changed name of ukevent guarding lock
* use only one kevent lock in kevent_user for all hash buckets instead of per-bucket locks
* do not use kevent_user_ctl structure instead provide needed arguments as syscall parameters
* various indent cleanups
* added optimisation, which is aimed to help when a lot of kevents are being copied from userspace
* mapped buffer (initial) implementation (no userspace yet)
Changes from 'take1' patchset:
- rebased against 2.6.18-git tree
- removed ioctl controlling
- added new syscall kevent_get_events(int fd, unsigned int min_nr, unsigned int max_nr, unsigned int timeout, void __user *buf, unsigned flags)
- use old syscall kevent_ctl for creation/removing, modification and initial kevent initialization
- use mutuxes instead of semaphores
- added file descriptor check and return error if provided descriptor does not match kevent file operations
- various indent fixes
- removed aio_sendfile() declarations.
Thank you.
PATCH 1/4 kevent: Core files.
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
It might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
arch/i386/kernel/syscall_table.S +4 -0
@@ -317,3 +317,7 @@ ENTRY(sys_call_table) .long sys_tee /* 315 */ .long sys_vmsplice .long sys_move_pages + .long sys_aio_recv + .long sys_aio_send + .long sys_kevent_get_events + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +4 -0
@@ -713,4 +713,8 @@ #endif .quad sys_tee .quad compat_sys_vmsplice .quad compat_sys_move_pages + .quad sys_aio_recv + .quad sys_aio_send + .quad sys_kevent_get_events + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +5 -1
@@ -323,10 +323,14 @@ #define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316#define __NR_move_pages 317 +#define __NR_aio_recv 318 +#define __NR_aio_send 319 +#define __NR_kevent_get_events 320 +#define __NR_kevent_ctl 321#ifdef __KERNEL__ -#define NR_syscalls 318 +#define NR_syscalls 322/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/unistd.h +9 -1
@@ -619,10 +619,18 @@ #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice)#define __NR_move_pages 279 __SYSCALL(__NR_move_pages, sys_move_pages) +#define __NR_aio_recv 280 +__SYSCALL(__NR_aio_recv, sys_aio_recv) +#define __NR_aio_send 281 +__SYSCALL(__NR_aio_send, sys_aio_send) +#define __NR_kevent_get_events 282 +__SYSCALL(__NR_kevent_get_events, sys_kevent_get_events) +#define __NR_kevent_ctl 283 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_move_pages +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS
include/linux/kevent.h new file +296 -0
296 lines not on this page · raw message
include/linux/kevent_storage.h new file +12 -0
@@ -0,0 +1,12 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +unsigned int qlen;/* Number of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +5 -0
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_aio_recv (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_aio_send (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_kevent_get_events (int ctl_fd,unsigned int min,unsigned int max, +unsigned int timeout,void __user *buf,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,unsigned int cmd,unsigned int num,void __user *buf);#endif
init/Kconfig +2 -0
@@ -218,6 +218,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o
kernel/kevent/Kconfig new file +57 -0
57 lines not on this page · raw message
kernel/kevent/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +obj-y := kevent.o kevent_user.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
kernel/kevent/kevent.c new file +238 -0
238 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +880 -0
880 lines not on this page · raw message
kernel/sys_ni.c +5 -0
@@ -122,6 +122,11 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_aio_recv); +cond_syscall (sys_aio_send); +cond_syscall (sys_kevent_get_events); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
PATCH 2/4 kevent: AIO, aio_sendfile() implementation.
AIO, aio_sendfile() implementation.
This patch includes asynchronous propagation of file's data into VFS cache and aio_sendfile() implementation.
Network aio_sendfile() works lazily - it asynchronously populates pages into the VFS cache (which can be used for various tricks with adaptive readahead) and then uses usual ->sendfile() callback.
fs/bio.c +1 -1
@@ -119,7 +119,7 @@ void bio_free(struct bio *bio, struct bi/* * default destructor for a bio allocated with bio_alloc_bioset() */ -static void bio_fs_destructor (struct bio *bio) +void bio_fs_destructor (struct bio *bio) {bio_free (bio, fs_bio_set); }
fs/ext2/inode.c +1 -0
@@ -685,6 +685,7 @@ ext2_writepages(struct address_space *ma }const struct address_space_operations ext2_aops = { + .get_block = ext2_get_block, .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_writepage,
fs/ext3/inode.c +1 -0
@@ -1699,6 +1699,7 @@ static int ext3_journalled_set_page_dirt }static const struct address_space_operations ext3_ordered_aops = { + .get_block = ext3_get_block, .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_ordered_writepage,
fs/file_table.c +7 -0
@@ -112,6 +112,10 @@ struct file *get_empty_filp(void)if (security_file_alloc (f)) goto fail_sec; +#ifdef CONFIG_KEVENT_POLL +kevent_storage_init (f, &f->st); +kevent_poll_reinit (f); +#endif tsk = current;INIT_LIST_HEAD (&f->f_u.fu_list);atomic_set (&f->f_count,1 );
@@ -159,6 +163,9 @@ void fastcall __fput(struct file *file)might_sleep ();fsnotify_close (file); +#ifdef CONFIG_KEVENT_POLL +kevent_storage_fini (&file->st); +#endif /* * The function eventpoll_release() should be the first called * in the file cleanup chain.
fs/inode.c +7 -0
@@ -21,6 +21,7 @@ #include <linux/pagemap.h>#include <linux/cdev.h> #include <linux/bootmem.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/mount.h> /*
@@ -165,12 +166,18 @@ #endif }memset (&inode->u,0 ,sizeof (inode->u)); inode->i_mapping = mapping; +#if definedCONFIG_KEVENT +kevent_storage_init (inode, &inode->st); +#endif }return inode; }void destroy_inode (struct inode *inode) { +#if definedCONFIG_KEVENT_INODE || definedCONFIG_KEVENT_SOCKET +kevent_storage_fini (&inode->st); +#endif BUG_ON (inode_has_buffers (inode));security_inode_free (inode);if (inode->i_sb->s_op->destroy_inode)
fs/reiserfs/inode.c +1 -0
@@ -3001,6 +3001,7 @@ int reiserfs_setattr(struct dentry *dent }const struct address_space_operations reiserfs_address_space_operations = { + .get_block = reiserfs_get_block, .writepage = reiserfs_writepage, .readpage = reiserfs_readpage, .readpages = reiserfs_readpages,
include/linux/fs.h +12 -0
@@ -240,6 +240,9 @@ #include <linux/mutex.h>#include <asm/atomic.h> #include <asm/semaphore.h> #include <asm/byteorder.h> +#ifdef CONFIG_KEVENT +#include <linux/kevent_storage.h> +#endif struct hd_geometry ;struct iovec ;
@@ -352,6 +355,8 @@ struct address_space;struct writeback_control ;struct address_space_operations { +int (*get_block)(struct inode *inode,sector_t iblock, +struct buffer_head *bh_result,int create);int (*writepage)(struct page *page,struct writeback_control *wbc);int (*readpage)(struct file *,struct page *);void (*sync_page)(struct page *);
@@ -546,6 +551,10 @@ #ifdef CONFIG_INOTIFYstruct mutex inotify_mutex;/* protects the watches list */ #endif +#ifdef CONFIG_KEVENT_INODE +struct kevent_storage st; +#endif +unsigned long i_state;unsigned long dirtied_when;/* jiffies of first dirtying */
@@ -698,6 +707,9 @@ #ifdef CONFIG_EPOLLstruct list_head f_ep_links;spinlock_t f_ep_lock;#endif /* #ifdef CONFIG_EPOLL */ +#ifdef CONFIG_KEVENT_POLL +struct kevent_storage st; +#endif struct address_space *f_mapping; };extern spinlock_t files_lock;
include/linux/fsnotify.h +5 -0
@@ -15,6 +15,7 @@ #ifdef __KERNEL__#include <linux/dnotify.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/audit.h> /*
@@ -79,6 +80,7 @@ static inline void fsnotify_nameremove(s isdir =IN_ISDIR ;dnotify_parent (dentry,DN_DELETE );inotify_dentry_parent_queue_event (dentry,IN_DELETE |isdir,0 , dentry->d_name.name); +kevent_inode_notify_parent (dentry,KEVENT_INODE_REMOVE ); }/*
@@ -88,6 +90,7 @@ static inline void fsnotify_inoderemove( {inotify_inode_queue_event (inode,IN_DELETE_SELF ,0 ,NULL ,NULL );inotify_inode_is_dead (inode); +kevent_inode_remove (inode); }/*
@@ -96,6 +99,7 @@ static inline void fsnotify_inoderemove(static inline void fsnotify_create (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
@@ -107,6 +111,7 @@ static inline void fsnotify_create(strucstatic inline void fsnotify_mkdir (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE |IN_ISDIR ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
kernel/kevent/kevent_aio.c new file +584 -0
584 lines not on this page · raw message
kernel/kevent/kevent_inode.c new file +114 -0
114 lines not on this page · raw message
PATCH 3/4 kevent: Network AIO, socket notifications.
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/asm-i386/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/asm-x86_64/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/linux/skbuff.h +2 -0
@@ -1283,6 +1283,8 @@ extern struct sk_buff *skb_recv_datagram int noblock, int *err);extern unsigned int datagram_poll (struct file *file,struct socket *sock,struct poll_table_struct *wait); +extern int skb_copy_datagram (const struct sk_buff *from, +int offset,void *dst,int size);extern int skb_copy_datagram_iovec (const struct sk_buff *from,int offset,struct iovec *to,int size);
include/net/sock.h +25 -15
@@ -48,6 +48,7 @@ #include <linux/lockdep.h>#include <linux/netdevice.h> #include <linux/skbuff.h> /* struct sk_buff */ #include <linux/security.h> +#include <linux/kevent.h> #include <linux/filter.h>
@@ -391,6 +392,8 @@ enum sock_flags {SOCK_RCVTSTAMP ,/* %SO_TIMESTAMP setting */ SOCK_LOCALROUTE ,/* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK ,/* write queue has been shrunk recently */ +SOCK_ASYNC , + SOCK_ASYNC_INUSE, };static inline void sock_copy_flags (struct sock *nsk,struct sock *osk)
@@ -450,6 +453,21 @@ static inline int sk_stream_memory_free(extern void sk_stream_rfree (struct sk_buff *skb); +struct socket_alloc { +struct socket socket; +struct inode vfs_inode; +}; + +static inline struct socket *SOCKET_I (struct inode *inode) +{ + return &container_of(inode, struct socket_alloc, vfs_inode)->socket; +} + +static inline struct inode *SOCK_INODE (struct socket *socket) +{ + return &container_of(socket, struct socket_alloc, socket)->vfs_inode; +} +static inline void sk_stream_set_owner_r (struct sk_buff *skb,struct sock *sk) { skb->sk = sk;
@@ -477,6 +495,7 @@ static inline void sk_add_backlog(struct sk->sk_backlog.tail = skb; } skb->next =NULL ; +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); }#define sk_wait_event (__sk, __timeo, __condition) \
@@ -548,6 +567,12 @@ struct proto {int (*backlog_rcv) (struct sock *sk,struct sk_buff *skb); + +int (*async_recv) (struct sock *sk, +void *dst,size_t size); +int (*async_send) (struct sock *sk, +struct page **pages,unsigned int poffset, +size_t size);/* Keeping track of sk's, looking them up, and port selection methods. */ void (*hash)(struct sock *sk);
@@ -679,21 +704,6 @@ static inline struct kiocb *siocb_to_kioreturn si->kiocb; } -struct socket_alloc { -struct socket socket; -struct inode vfs_inode; -}; - -static inline struct socket *SOCKET_I (struct inode *inode) -{ - return &container_of(inode, struct socket_alloc, vfs_inode)->socket; -} - -static inline struct inode *SOCK_INODE (struct socket *socket) -{ - return &container_of(socket, struct socket_alloc, socket)->vfs_inode; -} -extern void __sk_stream_mem_reclaim (struct sock *sk);extern int sk_stream_mem_schedule (struct sock *sk,int size,int kind);
include/net/tcp.h +3 -0
@@ -364,6 +364,8 @@ extern int compat_tcp_setsockopt(struc int level, int optname, char __user *optval, int optlen);extern void tcp_set_keepalive (struct sock *sk,int val); +extern int tcp_async_recv (struct sock *sk,void *dst,size_t size); +extern int tcp_async_send (struct sock *sk,struct page **pages,unsigned int poffset,size_t size); extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
@@ -857,6 +859,7 @@ static inline int tcp_prequeue(struct so tp->ucopy.memory =0 ; }else if (skb_queue_len (&tp->ucopy.prequeue) ==1 ) {wake_up_interruptible (sk->sk_sleep); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND );if (!inet_csk_ack_scheduled (sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, (3 * TCP_RTO_MIN) / 4,
kernel/kevent/kevent_naio.c new file +243 -0
243 lines not on this page · raw message
kernel/kevent/kevent_socket.c new file +149 -0
149 lines not on this page · raw message
net/core/datagram.c +55 -0
67 lines not on this page · raw message
net/core/sock.c +19 -1
@@ -617,6 +617,16 @@ #endifspin_unlock_bh (&sk->sk_lock.slock); ret = -ENONET ;break ; +#ifdef CONFIG_KEVENT_SOCKET +case SO_ASYNC_SOCK : +spin_lock_bh (&sk->sk_lock.slock); +if (valbool) +sock_set_flag (sk,SOCK_ASYNC ); +else +sock_reset_flag (sk,SOCK_ASYNC ); +spin_unlock_bh (&sk->sk_lock.slock); +break ; +#endif case SO_PASSSEC :if (valbool)
@@ -1406,6 +1416,7 @@ static void sock_def_wakeup(struct sockif (sk->sk_sleep &&waitqueue_active (sk->sk_sleep))wake_up_interruptible_all (sk->sk_sleep);read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_error_report (struct sock *sk)
@@ -1415,6 +1426,7 @@ static void sock_def_error_report(structwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,0 ,POLL_ERR );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_readable (struct sock *sk,int len)
@@ -1424,6 +1436,7 @@ static void sock_def_readable(struct socwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,1 ,POLL_IN );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_write_space (struct sock *sk)
@@ -1443,6 +1456,7 @@ static void sock_def_write_space(struct }read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); }static void sock_def_destruct (struct sock *sk)
@@ -1493,6 +1507,8 @@ #endif sk->sk_state =TCP_CLOSE ; sk->sk_socket = sock; +kevent_sk_reinit (sk); +sock_set_flag (sk,SOCK_ZAPPED );if (sock)
@@ -1559,8 +1575,10 @@ void fastcall release_sock(struct sock *if (sk->sk_backlog.tail)__release_sock (sk); sk->sk_lock.owner =NULL ; -if (waitqueue_active (&sk->sk_lock.wq)) +if (waitqueue_active (&sk->sk_lock.wq)) {wake_up (&sk->sk_lock.wq); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); + }spin_unlock_bh (&sk->sk_lock.slock); }EXPORT_SYMBOL (release_sock);
net/core/stream.c +1 -0
@@ -36,6 +36,7 @@ void sk_stream_write_space(struct sock *wake_up_interruptible (sk->sk_sleep);if (sock->fasync_list && !(sk->sk_shutdown &SEND_SHUTDOWN ))sock_wake_async (sock,2 ,POLL_OUT ); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); } }
net/ipv4/tcp.c +298 -0
316 lines not on this page · raw message
net/ipv4/tcp_input.c +3 -1
@@ -3112,6 +3112,7 @@ static void tcp_ofo_queue(struct sock *s__skb_unlink (skb, &tp->out_of_order_queue);__skb_queue_tail (&sk->sk_receive_queue, skb); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); tp->rcv_nxt =TCP_SKB_CB (skb)->end_seq;if (skb->h.th->fin)tcp_fin (skb, sk, skb->h.th);
@@ -3955,7 +3956,8 @@ int tcp_rcv_established(struct sock *sk,int copied_early =0 ;if (tp->copied_seq == tp->rcv_nxt && - len - tcp_header_len <= tp->ucopy.len) { + len - tcp_header_len <= tp->ucopy.len && + !sock_async (sk)) {#ifdef CONFIG_NET_DMA if (tcp_dma_try_early_copy (sk, skb, tcp_header_len)) { copied_early =1 ;
net/ipv4/tcp_ipv4.c +25 -15
@@ -61,6 +61,7 @@ #include <linux/cache.h>#include <linux/jhash.h> #include <linux/init.h> #include <linux/times.h> +#include <linux/kevent.h> #include <net/icmp.h> #include <net/inet_hashtables.h>
@@ -868,6 +869,7 @@ #endifreqsk_free (req); }else {inet_csk_reqsk_queue_hash_add (sk, req,TCP_TIMEOUT_INIT ); +kevent_socket_notify (sk,KEVENT_SOCKET_ACCEPT ); }return 0 ;
@@ -1108,24 +1110,30 @@ process: skb->dev =NULL ; -bh_lock_sock_nested (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock_nested (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan =get_softnet_dma (); -if (tp->ucopy.dma_chan) - ret =tcp_v4_do_rcv (sk, skb); - else +struct tcp_sock *tp =tcp_sk (sk); +if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan =get_softnet_dma (); +if (tp->ucopy.dma_chan) + ret =tcp_v4_do_rcv (sk, skb); + else#endif - { -if (!tcp_prequeue (sk, skb)) - ret =tcp_v4_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v4_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);
@@ -1849,6 +1857,8 @@ struct proto tcp_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v4_do_rcv, .hash = tcp_v4_hash, .unhash = tcp_unhash,
net/ipv6/tcp_ipv6.c +21 -13
@@ -1230,22 +1230,28 @@ process: skb->dev =NULL ; -bh_lock_sock (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (tp->ucopy.dma_chan) - ret =tcp_v6_do_rcv (sk, skb); - else -#endif - { -if (!tcp_prequeue (sk, skb)) +struct tcp_sock *tp =tcp_sk (sk); +if (tp->ucopy.dma_chan) ret =tcp_v6_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + else +#endif + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v6_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);return ret ?-1 :0 ;
@@ -1596,6 +1602,8 @@ struct proto tcpv6_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v6_do_rcv, .hash = tcp_v6_hash, .unhash = tcp_unhash,
PATCH 4/4 kevent: poll/select() notifications. Timer notifications.
poll/select() notifications. Timer notifications.
This patch includes generic poll/select and timer notifications.
kevent_poll works simialr to epoll and has the same issues (callback is invoked not from internal state machine of the caller, but through process awake).
Timer notifications can be used for fine grained per-process time management, since iteractive timers are very inconveniently to use, and they are limited.
kernel/kevent/kevent_poll.c new file +220 -0
@@ -0,0 +1,220 @@ +/* +* kevent_poll.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/file.h> +#include <linux/kevent.h> +#include <linux/poll.h> +#include <linux/fs.h> + +static kmem_cache_t *kevent_poll_container_cache; +static kmem_cache_t *kevent_poll_priv_cache; + +struct kevent_poll_ctl +{ +struct poll_table_struct pt; +struct kevent *k; +}; + +struct kevent_poll_wait_container +{ +struct list_head container_entry; +wait_queue_head_t *whead; +wait_queue_t wait; +struct kevent *k; +}; + +struct kevent_poll_private +{ +struct list_head container_list; +spinlock_t container_lock; +}; + +static int kevent_poll_enqueue (struct kevent *k); +static int kevent_poll_dequeue (struct kevent *k); +static int kevent_poll_callback (struct kevent *k); + +static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ +struct kevent_poll_wait_container *cont = + container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +u32 revents; + + revents = file->f_op->poll (file,NULL ); + +kevent_storage_ready (k->st,NULL , revents); + +return 0 ; +} + +static void kevent_poll_qproc (struct file *file,wait_queue_head_t *whead, +struct poll_table_struct *poll_table) +{ +struct kevent *k = + container_of(poll_table, struct kevent_poll_ctl, pt)->k; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *cont; +unsigned long flags; + + cont =kmem_cache_alloc (kevent_poll_container_cache,SLAB_KERNEL ); +if (!cont) { +kevent_break (k); +return ; + } + + cont->k = k; +init_waitqueue_func_entry (&cont->wait, kevent_poll_wait_callback); + cont->whead = whead; + +spin_lock_irqsave (&priv->container_lock, flags); +list_add_tail (&cont->container_entry, &priv->container_list); +spin_unlock_irqrestore (&priv->container_lock, flags); + +add_wait_queue (whead, &cont->wait); +} + +static int kevent_poll_enqueue (struct kevent *k) +{ +struct file *file; +int err, ready =0 ; +unsigned int revents; +struct kevent_poll_ctl ctl; +struct kevent_poll_private *priv; + + file =fget (k->event.id.raw[0 ]); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_op || !file->f_op->poll) + goto err_out_fput; + + err = -ENOMEM ; + priv =kmem_cache_alloc (kevent_poll_priv_cache,SLAB_KERNEL ); +if (!priv) + goto err_out_fput; + +spin_lock_init (&priv->container_lock); +INIT_LIST_HEAD (&priv->container_list); + + k->priv = priv; + + ctl.k = k; +init_poll_funcptr (&ctl.pt, &kevent_poll_qproc); + + err =kevent_storage_enqueue (&file->st, k); +if (err) + goto err_out_free; + + revents = file->f_op->poll (file, &ctl.pt); +if (revents & k->event.event) { + ready =1 ; +kevent_poll_dequeue (k); + } + +return ready; + +err_out_free: +kmem_cache_free (kevent_poll_priv_cache, priv); +err_out_fput: +fput (file); +return err; +} + +static int kevent_poll_dequeue (struct kevent *k) +{ +struct file *file = k->st->origin; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *w, *n; +unsigned long flags; + +kevent_storage_dequeue (k->st, k); + +spin_lock_irqsave (&priv->container_lock, flags); +list_for_each_entry_safe (w, n, &priv->container_list, container_entry) { +list_del (&w->container_entry); +remove_wait_queue (w->whead, &w->wait); +kmem_cache_free (kevent_poll_container_cache, w); + } +spin_unlock_irqrestore (&priv->container_lock, flags); + +kmem_cache_free (kevent_poll_priv_cache, priv); + k->priv =NULL ; + +fput (file); + +return 0 ; +} + +static int kevent_poll_callback (struct kevent *k) +{ +struct file *file = k->st->origin; +unsigned int revents = file->f_op->poll (file,NULL ); +return (revents & k->event.event); +} + +static int __init kevent_poll_sys_init (void ) +{ +struct kevent_callbacks *pc = &kevent_registered_callbacks[KEVENT_POLL ]; + + kevent_poll_container_cache =kmem_cache_create ("kevent_poll_container_cache" , +sizeof (struct kevent_poll_wait_container ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_container_cache) { +printk (KERN_ERR "Failed to create kevent poll container cache.\n" ); +return -ENOMEM ; + } + + kevent_poll_priv_cache =kmem_cache_create ("kevent_poll_priv_cache" , +sizeof (struct kevent_poll_private ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_priv_cache) { +printk (KERN_ERR "Failed to create kevent poll private data cache.\n" ); +kmem_cache_destroy (kevent_poll_container_cache); + kevent_poll_container_cache =NULL ; +return -ENOMEM ; + } + + pc->enqueue = &kevent_poll_enqueue; + pc->dequeue = &kevent_poll_dequeue; + pc->callback = &kevent_poll_callback; + +printk (KERN_INFO "Kevent poll()/select() subsystem has been initialized.\n" ); +return 0 ; +} + +static struct lock_class_key kevent_poll_key; + +void kevent_poll_reinit (struct file *file) +{ +lockdep_set_class (&file->st.lock, &kevent_poll_key); +} + +static void __exit kevent_poll_sys_fini(void) +{ +kmem_cache_destroy (kevent_poll_priv_cache); +kmem_cache_destroy (kevent_poll_container_cache); +} + +module_init (kevent_poll_sys_init); +module_exit (kevent_poll_sys_fini);
kernel/kevent/kevent_timer.c new file +119 -0
119 lines not on this page · raw message
Re: [take4 1/4] kevent: Core files.
In reply to Evgeniy PolyakovQuoted 28 lines · Evgeniy Polyakov
kernel/kevent/kevent_user.c new file +28
-0+static int __devinit kevent_user_init (void ) +{ +struct class_device *dev; +int err =0 ; + + err =register_filesystem (&kevent_fs_type); +if (err) +panic ("%s: failed to register filesystem: err=%d.\n" , + kevent_name, err); + + kevent_mnt =kern_mount (&kevent_fs_type); +if (IS_ERR (kevent_mnt)) +panic ("%s: failed to mount silesystem: err=%ld.\n" , + kevent_name,PTR_ERR (kevent_mnt)); + + kevent_user_major =register_chrdev (0 , kevent_name, &kevent_user_fops); +if (kevent_user_major <0 ) { +printk (KERN_ERR "Failed to register \"%s\" char device: err=%d.\n" , + kevent_name, kevent_user_major); +return -ENODEV ; + } + + kevent_user_class =class_create (THIS_MODULE ,"kevent" ); +if (IS_ERR (kevent_user_class)) { +printk (KERN_ERR "Failed to register \"%s\" class: err=%ld.\n" , + kevent_name,PTR_ERR (kevent_user_class)); + err =PTR_ERR (kevent_user_class); + goto err_out_unregister;
kernel/kevent/kevent_user.c line 856
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ } + + dev =class_device_create (kevent_user_class,NULL , +MKDEV (kevent_user_major,0 ),NULL , kevent_name); +if (IS_ERR (dev)) { +printk (KERN_ERR "Failed to create %d.%d class device in \"%s\" class: err=%ld.\n" , + kevent_user_major,0 , kevent_name,PTR_ERR (dev)); + err =PTR_ERR (dev); + goto err_out_class_destroy; + }
As you are only using 1 minor number in this code, why not just use a miscdevice instead? It saves a bit of overhead and makes the code a tiny bit smaller :)
thanks,
greg k-h
Re: [take4 1/4] kevent: Core files.
Quoted 4 lines · GregKH
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ dev =class_device_create (kevent_user_class,NULL , +MKDEV (kevent_user_major,0 ),NULL , kevent_name); +if (IS_ERR (dev)) { + printk(KERN_ERR "Failed to create %d.%d class device in \"%s\" class: err=%ld.\n",
Quoted 8 lines
kernel/kevent/kevent_user.c line 856
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ kevent_user_major, 0, kevent_name, PTR_ERR(dev)); + err =PTR_ERR (dev); + goto err_out_class_destroy; + }As you are only using 1 minor number in this code, why not just use a miscdevice instead? It saves a bit of overhead and makes the code a tiny bit smaller :)
No problem. I will move it to miscdevice instead of full chardev.
Quoted 3 lines
thanks,
greg k-h
Signature · 1 line
Evgeniy Polyakov
cover kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovGeneric event handling mechanism.
I send this patchset for comments and review, it still contains AIO and aio_sendfile() implementation on top of get_block() abstraction, which was decided to postpone for a while (it is simpler right now to generate patchset as a whole, when kevent will be ready for merge, I will generate patchset without AIO stuff).
Since number of suggested changes goes from 'several' to 'very small', I'm asking for inclusion or declining. Currently only AIO stuff requires some changes, and as sugested by Christoph Hellwig, will not supposed to be merged right now.
Should I prepare final patchset and what should it include? Thank you.
Changes from 'take4' patchset:
* use miscdevice instead of chardevice
* comments fixes
Changes from 'take3' patchset:
* removed serializing mutex from kevent_user_wait()
* moved storage list processing to RCU
* removed lockdep screaming - all storage locks are initialized in the same function, so it was learned to differentiate between various cases
* remove kevent from storage if is marked as broken after callback
* fixed a typo in mmaped buffer implementation which would end up in wrong index calcualtion
Changes from 'take2' patchset:
* split kevent_finish_user() to locked and unlocked variants
* do not use KEVENT_STAT ifdefs, use inline functions instead
* use array of callbacks of each type instead of each kevent callback initialization
* changed name of ukevent guarding lock
* use only one kevent lock in kevent_user for all hash buckets instead of per-bucket locks
* do not use kevent_user_ctl structure instead provide needed arguments as syscall parameters
* various indent cleanups
* added optimisation, which is aimed to help when a lot of kevents are being copied from userspace
* mapped buffer (initial) implementation (no userspace yet)
Changes from 'take1' patchset:
- rebased against 2.6.18-git tree
- removed ioctl controlling
- added new syscall kevent_get_events(int fd, unsigned int min_nr, unsigned int max_nr, unsigned int timeout, void __user *buf, unsigned flags)
- use old syscall kevent_ctl for creation/removing, modification and initial kevent initialization
- use mutuxes instead of semaphores
- added file descriptor check and return error if provided descriptor does not match kevent file operations
- various indent fixes
- removed aio_sendfile() declarations.
Thank you.
PATCH 1/4 kevent: Core files.
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
It might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
arch/i386/kernel/syscall_table.S +4 -0
@@ -317,3 +317,7 @@ ENTRY(sys_call_table) .long sys_tee /* 315 */ .long sys_vmsplice .long sys_move_pages + .long sys_aio_recv + .long sys_aio_send + .long sys_kevent_get_events + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +4 -0
@@ -713,4 +713,8 @@ #endif .quad sys_tee .quad compat_sys_vmsplice .quad compat_sys_move_pages + .quad sys_aio_recv + .quad sys_aio_send + .quad sys_kevent_get_events + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +5 -1
@@ -323,10 +323,14 @@ #define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316#define __NR_move_pages 317 +#define __NR_aio_recv 318 +#define __NR_aio_send 319 +#define __NR_kevent_get_events 320 +#define __NR_kevent_ctl 321#ifdef __KERNEL__ -#define NR_syscalls 318 +#define NR_syscalls 322/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/unistd.h +9 -1
@@ -619,10 +619,18 @@ #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice)#define __NR_move_pages 279 __SYSCALL(__NR_move_pages, sys_move_pages) +#define __NR_aio_recv 280 +__SYSCALL(__NR_aio_recv, sys_aio_recv) +#define __NR_aio_send 281 +__SYSCALL(__NR_aio_send, sys_aio_send) +#define __NR_kevent_get_events 282 +__SYSCALL(__NR_kevent_get_events, sys_kevent_get_events) +#define __NR_kevent_ctl 283 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_move_pages +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS
include/linux/kevent.h new file +296 -0
296 lines not on this page · raw message
include/linux/kevent_storage.h new file +12 -0
@@ -0,0 +1,12 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +unsigned int qlen;/* Number of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +5 -0
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_aio_recv (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_aio_send (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_kevent_get_events (int ctl_fd,unsigned int min,unsigned int max, +unsigned int timeout,void __user *buf,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,unsigned int cmd,unsigned int num,void __user *buf);#endif
init/Kconfig +2 -0
@@ -218,6 +218,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o
kernel/kevent/Kconfig new file +57 -0
57 lines not on this page · raw message
kernel/kevent/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +obj-y := kevent.o kevent_user.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
kernel/kevent/kevent.c new file +238 -0
238 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +863 -0
863 lines not on this page · raw message
kernel/sys_ni.c +5 -0
@@ -122,6 +122,11 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_aio_recv); +cond_syscall (sys_aio_send); +cond_syscall (sys_kevent_get_events); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
PATCH 2/4 kevent: AIO, aio_sendfile() implementation.
AIO, aio_sendfile() implementation.
This patch includes asynchronous propagation of file's data into VFS cache and aio_sendfile() implementation.
Network aio_sendfile() works lazily - it asynchronously populates pages into the VFS cache (which can be used for various tricks with adaptive readahead) and then uses usual ->sendfile() callback.
This patch contains AIO and aio_sendfile() implementation on top of get_block() abstraction, which was decided to postpone for a while.
More info can be found in the following thread at linux-kernel@ or netdev@:
http://thread.gmane.org/gmane.linux.kernel/425005
fs/bio.c +1 -1
@@ -119,7 +119,7 @@ void bio_free(struct bio *bio, struct bi/* * default destructor for a bio allocated with bio_alloc_bioset() */ -static void bio_fs_destructor (struct bio *bio) +void bio_fs_destructor (struct bio *bio) {bio_free (bio, fs_bio_set); }
fs/ext2/inode.c +1 -0
@@ -685,6 +685,7 @@ ext2_writepages(struct address_space *ma }const struct address_space_operations ext2_aops = { + .get_block = ext2_get_block, .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_writepage,
fs/ext3/inode.c +1 -0
@@ -1699,6 +1699,7 @@ static int ext3_journalled_set_page_dirt }static const struct address_space_operations ext3_ordered_aops = { + .get_block = ext3_get_block, .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_ordered_writepage,
fs/file_table.c +7 -0
@@ -112,6 +112,10 @@ struct file *get_empty_filp(void)if (security_file_alloc (f)) goto fail_sec; +#ifdef CONFIG_KEVENT_POLL +kevent_storage_init (f, &f->st); +kevent_poll_reinit (f); +#endif tsk = current;INIT_LIST_HEAD (&f->f_u.fu_list);atomic_set (&f->f_count,1 );
@@ -159,6 +163,9 @@ void fastcall __fput(struct file *file)might_sleep ();fsnotify_close (file); +#ifdef CONFIG_KEVENT_POLL +kevent_storage_fini (&file->st); +#endif /* * The function eventpoll_release() should be the first called * in the file cleanup chain.
fs/inode.c +7 -0
@@ -21,6 +21,7 @@ #include <linux/pagemap.h>#include <linux/cdev.h> #include <linux/bootmem.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/mount.h> /*
@@ -165,12 +166,18 @@ #endif }memset (&inode->u,0 ,sizeof (inode->u)); inode->i_mapping = mapping; +#if definedCONFIG_KEVENT +kevent_storage_init (inode, &inode->st); +#endif }return inode; }void destroy_inode (struct inode *inode) { +#if definedCONFIG_KEVENT_INODE || definedCONFIG_KEVENT_SOCKET +kevent_storage_fini (&inode->st); +#endif BUG_ON (inode_has_buffers (inode));security_inode_free (inode);if (inode->i_sb->s_op->destroy_inode)
fs/reiserfs/inode.c +1 -0
@@ -3001,6 +3001,7 @@ int reiserfs_setattr(struct dentry *dent }const struct address_space_operations reiserfs_address_space_operations = { + .get_block = reiserfs_get_block, .writepage = reiserfs_writepage, .readpage = reiserfs_readpage, .readpages = reiserfs_readpages,
include/linux/fs.h +12 -0
@@ -240,6 +240,9 @@ #include <linux/mutex.h>#include <asm/atomic.h> #include <asm/semaphore.h> #include <asm/byteorder.h> +#ifdef CONFIG_KEVENT +#include <linux/kevent_storage.h> +#endif struct hd_geometry ;struct iovec ;
@@ -352,6 +355,8 @@ struct address_space;struct writeback_control ;struct address_space_operations { +int (*get_block)(struct inode *inode,sector_t iblock, +struct buffer_head *bh_result,int create);int (*writepage)(struct page *page,struct writeback_control *wbc);int (*readpage)(struct file *,struct page *);void (*sync_page)(struct page *);
@@ -546,6 +551,10 @@ #ifdef CONFIG_INOTIFYstruct mutex inotify_mutex;/* protects the watches list */ #endif +#ifdef CONFIG_KEVENT_INODE +struct kevent_storage st; +#endif +unsigned long i_state;unsigned long dirtied_when;/* jiffies of first dirtying */
@@ -698,6 +707,9 @@ #ifdef CONFIG_EPOLLstruct list_head f_ep_links;spinlock_t f_ep_lock;#endif /* #ifdef CONFIG_EPOLL */ +#ifdef CONFIG_KEVENT_POLL +struct kevent_storage st; +#endif struct address_space *f_mapping; };extern spinlock_t files_lock;
include/linux/fsnotify.h +5 -0
@@ -15,6 +15,7 @@ #ifdef __KERNEL__#include <linux/dnotify.h> #include <linux/inotify.h> +#include <linux/kevent.h> #include <linux/audit.h> /*
@@ -79,6 +80,7 @@ static inline void fsnotify_nameremove(s isdir =IN_ISDIR ;dnotify_parent (dentry,DN_DELETE );inotify_dentry_parent_queue_event (dentry,IN_DELETE |isdir,0 , dentry->d_name.name); +kevent_inode_notify_parent (dentry,KEVENT_INODE_REMOVE ); }/*
@@ -88,6 +90,7 @@ static inline void fsnotify_inoderemove( {inotify_inode_queue_event (inode,IN_DELETE_SELF ,0 ,NULL ,NULL );inotify_inode_is_dead (inode); +kevent_inode_remove (inode); }/*
@@ -96,6 +99,7 @@ static inline void fsnotify_inoderemove(static inline void fsnotify_create (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
@@ -107,6 +111,7 @@ static inline void fsnotify_create(strucstatic inline void fsnotify_mkdir (struct inode *inode,struct dentry *dentry) {inode_dir_notify (inode,DN_CREATE ); +kevent_inode_notify (inode,KEVENT_INODE_CREATE );inotify_inode_queue_event (inode,IN_CREATE |IN_ISDIR ,0 , dentry->d_name.name, dentry->d_inode);audit_inode_child (dentry->d_name.name, dentry->d_inode, inode->i_ino);
kernel/kevent/kevent_aio.c new file +584 -0
584 lines not on this page · raw message
kernel/kevent/kevent_inode.c new file +114 -0
114 lines not on this page · raw message
PATCH 3/4 kevent: Network AIO, socket notifications.
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/asm-i386/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/asm-x86_64/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/linux/skbuff.h +2 -0
@@ -1283,6 +1283,8 @@ extern struct sk_buff *skb_recv_datagram int noblock, int *err);extern unsigned int datagram_poll (struct file *file,struct socket *sock,struct poll_table_struct *wait); +extern int skb_copy_datagram (const struct sk_buff *from, +int offset,void *dst,int size);extern int skb_copy_datagram_iovec (const struct sk_buff *from,int offset,struct iovec *to,int size);
include/net/sock.h +25 -15
@@ -48,6 +48,7 @@ #include <linux/lockdep.h>#include <linux/netdevice.h> #include <linux/skbuff.h> /* struct sk_buff */ #include <linux/security.h> +#include <linux/kevent.h> #include <linux/filter.h>
@@ -391,6 +392,8 @@ enum sock_flags {SOCK_RCVTSTAMP ,/* %SO_TIMESTAMP setting */ SOCK_LOCALROUTE ,/* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK ,/* write queue has been shrunk recently */ +SOCK_ASYNC , + SOCK_ASYNC_INUSE, };static inline void sock_copy_flags (struct sock *nsk,struct sock *osk)
@@ -450,6 +453,21 @@ static inline int sk_stream_memory_free(extern void sk_stream_rfree (struct sk_buff *skb); +struct socket_alloc { +struct socket socket; +struct inode vfs_inode; +}; + +static inline struct socket *SOCKET_I (struct inode *inode) +{ + return &container_of(inode, struct socket_alloc, vfs_inode)->socket; +} + +static inline struct inode *SOCK_INODE (struct socket *socket) +{ + return &container_of(socket, struct socket_alloc, socket)->vfs_inode; +} +static inline void sk_stream_set_owner_r (struct sk_buff *skb,struct sock *sk) { skb->sk = sk;
@@ -477,6 +495,7 @@ static inline void sk_add_backlog(struct sk->sk_backlog.tail = skb; } skb->next =NULL ; +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); }#define sk_wait_event (__sk, __timeo, __condition) \
@@ -548,6 +567,12 @@ struct proto {int (*backlog_rcv) (struct sock *sk,struct sk_buff *skb); + +int (*async_recv) (struct sock *sk, +void *dst,size_t size); +int (*async_send) (struct sock *sk, +struct page **pages,unsigned int poffset, +size_t size);/* Keeping track of sk's, looking them up, and port selection methods. */ void (*hash)(struct sock *sk);
@@ -679,21 +704,6 @@ static inline struct kiocb *siocb_to_kioreturn si->kiocb; } -struct socket_alloc { -struct socket socket; -struct inode vfs_inode; -}; - -static inline struct socket *SOCKET_I (struct inode *inode) -{ - return &container_of(inode, struct socket_alloc, vfs_inode)->socket; -} - -static inline struct inode *SOCK_INODE (struct socket *socket) -{ - return &container_of(socket, struct socket_alloc, socket)->vfs_inode; -} -extern void __sk_stream_mem_reclaim (struct sock *sk);extern int sk_stream_mem_schedule (struct sock *sk,int size,int kind);
include/net/tcp.h +3 -0
@@ -364,6 +364,8 @@ extern int compat_tcp_setsockopt(struc int level, int optname, char __user *optval, int optlen);extern void tcp_set_keepalive (struct sock *sk,int val); +extern int tcp_async_recv (struct sock *sk,void *dst,size_t size); +extern int tcp_async_send (struct sock *sk,struct page **pages,unsigned int poffset,size_t size); extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
@@ -857,6 +859,7 @@ static inline int tcp_prequeue(struct so tp->ucopy.memory =0 ; }else if (skb_queue_len (&tp->ucopy.prequeue) ==1 ) {wake_up_interruptible (sk->sk_sleep); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND );if (!inet_csk_ack_scheduled (sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, (3 * TCP_RTO_MIN) / 4,
kernel/kevent/kevent_naio.c new file +243 -0
243 lines not on this page · raw message
kernel/kevent/kevent_socket.c new file +149 -0
149 lines not on this page · raw message
net/core/datagram.c +55 -0
67 lines not on this page · raw message
net/core/sock.c +19 -1
@@ -617,6 +617,16 @@ #endifspin_unlock_bh (&sk->sk_lock.slock); ret = -ENONET ;break ; +#ifdef CONFIG_KEVENT_SOCKET +case SO_ASYNC_SOCK : +spin_lock_bh (&sk->sk_lock.slock); +if (valbool) +sock_set_flag (sk,SOCK_ASYNC ); +else +sock_reset_flag (sk,SOCK_ASYNC ); +spin_unlock_bh (&sk->sk_lock.slock); +break ; +#endif case SO_PASSSEC :if (valbool)
@@ -1406,6 +1416,7 @@ static void sock_def_wakeup(struct sockif (sk->sk_sleep &&waitqueue_active (sk->sk_sleep))wake_up_interruptible_all (sk->sk_sleep);read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_error_report (struct sock *sk)
@@ -1415,6 +1426,7 @@ static void sock_def_error_report(structwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,0 ,POLL_ERR );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_readable (struct sock *sk,int len)
@@ -1424,6 +1436,7 @@ static void sock_def_readable(struct socwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,1 ,POLL_IN );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_write_space (struct sock *sk)
@@ -1443,6 +1456,7 @@ static void sock_def_write_space(struct }read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); }static void sock_def_destruct (struct sock *sk)
@@ -1493,6 +1507,8 @@ #endif sk->sk_state =TCP_CLOSE ; sk->sk_socket = sock; +kevent_sk_reinit (sk); +sock_set_flag (sk,SOCK_ZAPPED );if (sock)
@@ -1559,8 +1575,10 @@ void fastcall release_sock(struct sock *if (sk->sk_backlog.tail)__release_sock (sk); sk->sk_lock.owner =NULL ; -if (waitqueue_active (&sk->sk_lock.wq)) +if (waitqueue_active (&sk->sk_lock.wq)) {wake_up (&sk->sk_lock.wq); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); + }spin_unlock_bh (&sk->sk_lock.slock); }EXPORT_SYMBOL (release_sock);
net/core/stream.c +1 -0
@@ -36,6 +36,7 @@ void sk_stream_write_space(struct sock *wake_up_interruptible (sk->sk_sleep);if (sock->fasync_list && !(sk->sk_shutdown &SEND_SHUTDOWN ))sock_wake_async (sock,2 ,POLL_OUT ); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); } }
net/ipv4/tcp.c +298 -0
316 lines not on this page · raw message
net/ipv4/tcp_input.c +3 -1
@@ -3112,6 +3112,7 @@ static void tcp_ofo_queue(struct sock *s__skb_unlink (skb, &tp->out_of_order_queue);__skb_queue_tail (&sk->sk_receive_queue, skb); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); tp->rcv_nxt =TCP_SKB_CB (skb)->end_seq;if (skb->h.th->fin)tcp_fin (skb, sk, skb->h.th);
@@ -3955,7 +3956,8 @@ int tcp_rcv_established(struct sock *sk,int copied_early =0 ;if (tp->copied_seq == tp->rcv_nxt && - len - tcp_header_len <= tp->ucopy.len) { + len - tcp_header_len <= tp->ucopy.len && + !sock_async (sk)) {#ifdef CONFIG_NET_DMA if (tcp_dma_try_early_copy (sk, skb, tcp_header_len)) { copied_early =1 ;
net/ipv4/tcp_ipv4.c +25 -15
@@ -61,6 +61,7 @@ #include <linux/cache.h>#include <linux/jhash.h> #include <linux/init.h> #include <linux/times.h> +#include <linux/kevent.h> #include <net/icmp.h> #include <net/inet_hashtables.h>
@@ -868,6 +869,7 @@ #endifreqsk_free (req); }else {inet_csk_reqsk_queue_hash_add (sk, req,TCP_TIMEOUT_INIT ); +kevent_socket_notify (sk,KEVENT_SOCKET_ACCEPT ); }return 0 ;
@@ -1108,24 +1110,30 @@ process: skb->dev =NULL ; -bh_lock_sock_nested (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock_nested (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan =get_softnet_dma (); -if (tp->ucopy.dma_chan) - ret =tcp_v4_do_rcv (sk, skb); - else +struct tcp_sock *tp =tcp_sk (sk); +if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan =get_softnet_dma (); +if (tp->ucopy.dma_chan) + ret =tcp_v4_do_rcv (sk, skb); + else#endif - { -if (!tcp_prequeue (sk, skb)) - ret =tcp_v4_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v4_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);
@@ -1849,6 +1857,8 @@ struct proto tcp_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v4_do_rcv, .hash = tcp_v4_hash, .unhash = tcp_unhash,
net/ipv6/tcp_ipv6.c +21 -13
@@ -1230,22 +1230,28 @@ process: skb->dev =NULL ; -bh_lock_sock (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (tp->ucopy.dma_chan) - ret =tcp_v6_do_rcv (sk, skb); - else -#endif - { -if (!tcp_prequeue (sk, skb)) +struct tcp_sock *tp =tcp_sk (sk); +if (tp->ucopy.dma_chan) ret =tcp_v6_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + else +#endif + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v6_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);return ret ?-1 :0 ;
@@ -1596,6 +1602,8 @@ struct proto tcpv6_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v6_do_rcv, .hash = tcp_v6_hash, .unhash = tcp_unhash,
PATCH 4/4 kevent: poll/select() notifications. Timer notifications.
poll/select() notifications. Timer notifications.
This patch includes generic poll/select and timer notifications.
kevent_poll works simialr to epoll and has the same issues (callback is invoked not from internal state machine of the caller, but through process awake).
Timer notifications can be used for fine grained per-process time management, since interval timers are very inconvenient to use, and they are limited.
kernel/kevent/kevent_poll.c new file +220 -0
@@ -0,0 +1,220 @@ +/* +* kevent_poll.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/file.h> +#include <linux/kevent.h> +#include <linux/poll.h> +#include <linux/fs.h> + +static kmem_cache_t *kevent_poll_container_cache; +static kmem_cache_t *kevent_poll_priv_cache; + +struct kevent_poll_ctl +{ +struct poll_table_struct pt; +struct kevent *k; +}; + +struct kevent_poll_wait_container +{ +struct list_head container_entry; +wait_queue_head_t *whead; +wait_queue_t wait; +struct kevent *k; +}; + +struct kevent_poll_private +{ +struct list_head container_list; +spinlock_t container_lock; +}; + +static int kevent_poll_enqueue (struct kevent *k); +static int kevent_poll_dequeue (struct kevent *k); +static int kevent_poll_callback (struct kevent *k); + +static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ +struct kevent_poll_wait_container *cont = + container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +u32 revents; + + revents = file->f_op->poll (file,NULL ); + +kevent_storage_ready (k->st,NULL , revents); + +return 0 ; +} + +static void kevent_poll_qproc (struct file *file,wait_queue_head_t *whead, +struct poll_table_struct *poll_table) +{ +struct kevent *k = + container_of(poll_table, struct kevent_poll_ctl, pt)->k; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *cont; +unsigned long flags; + + cont =kmem_cache_alloc (kevent_poll_container_cache,SLAB_KERNEL ); +if (!cont) { +kevent_break (k); +return ; + } + + cont->k = k; +init_waitqueue_func_entry (&cont->wait, kevent_poll_wait_callback); + cont->whead = whead; + +spin_lock_irqsave (&priv->container_lock, flags); +list_add_tail (&cont->container_entry, &priv->container_list); +spin_unlock_irqrestore (&priv->container_lock, flags); + +add_wait_queue (whead, &cont->wait); +} + +static int kevent_poll_enqueue (struct kevent *k) +{ +struct file *file; +int err, ready =0 ; +unsigned int revents; +struct kevent_poll_ctl ctl; +struct kevent_poll_private *priv; + + file =fget (k->event.id.raw[0 ]); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_op || !file->f_op->poll) + goto err_out_fput; + + err = -ENOMEM ; + priv =kmem_cache_alloc (kevent_poll_priv_cache,SLAB_KERNEL ); +if (!priv) + goto err_out_fput; + +spin_lock_init (&priv->container_lock); +INIT_LIST_HEAD (&priv->container_list); + + k->priv = priv; + + ctl.k = k; +init_poll_funcptr (&ctl.pt, &kevent_poll_qproc); + + err =kevent_storage_enqueue (&file->st, k); +if (err) + goto err_out_free; + + revents = file->f_op->poll (file, &ctl.pt); +if (revents & k->event.event) { + ready =1 ; +kevent_poll_dequeue (k); + } + +return ready; + +err_out_free: +kmem_cache_free (kevent_poll_priv_cache, priv); +err_out_fput: +fput (file); +return err; +} + +static int kevent_poll_dequeue (struct kevent *k) +{ +struct file *file = k->st->origin; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *w, *n; +unsigned long flags; + +kevent_storage_dequeue (k->st, k); + +spin_lock_irqsave (&priv->container_lock, flags); +list_for_each_entry_safe (w, n, &priv->container_list, container_entry) { +list_del (&w->container_entry); +remove_wait_queue (w->whead, &w->wait); +kmem_cache_free (kevent_poll_container_cache, w); + } +spin_unlock_irqrestore (&priv->container_lock, flags); + +kmem_cache_free (kevent_poll_priv_cache, priv); + k->priv =NULL ; + +fput (file); + +return 0 ; +} + +static int kevent_poll_callback (struct kevent *k) +{ +struct file *file = k->st->origin; +unsigned int revents = file->f_op->poll (file,NULL ); +return (revents & k->event.event); +} + +static int __init kevent_poll_sys_init (void ) +{ +struct kevent_callbacks *pc = &kevent_registered_callbacks[KEVENT_POLL ]; + + kevent_poll_container_cache =kmem_cache_create ("kevent_poll_container_cache" , +sizeof (struct kevent_poll_wait_container ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_container_cache) { +printk (KERN_ERR "Failed to create kevent poll container cache.\n" ); +return -ENOMEM ; + } + + kevent_poll_priv_cache =kmem_cache_create ("kevent_poll_priv_cache" , +sizeof (struct kevent_poll_private ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_priv_cache) { +printk (KERN_ERR "Failed to create kevent poll private data cache.\n" ); +kmem_cache_destroy (kevent_poll_container_cache); + kevent_poll_container_cache =NULL ; +return -ENOMEM ; + } + + pc->enqueue = &kevent_poll_enqueue; + pc->dequeue = &kevent_poll_dequeue; + pc->callback = &kevent_poll_callback; + +printk (KERN_INFO "Kevent poll()/select() subsystem has been initialized.\n" ); +return 0 ; +} + +static struct lock_class_key kevent_poll_key; + +void kevent_poll_reinit (struct file *file) +{ +lockdep_set_class (&file->st.lock, &kevent_poll_key); +} + +static void __exit kevent_poll_sys_fini(void) +{ +kmem_cache_destroy (kevent_poll_priv_cache); +kmem_cache_destroy (kevent_poll_container_cache); +} + +module_init (kevent_poll_sys_init); +module_exit (kevent_poll_sys_fini);
kernel/kevent/kevent_timer.c new file +119 -0
119 lines not on this page · raw message
Re: [take5 3/4] kevent: Network AIO, socket notifications.
In reply to Evgeniy PolyakovQuoted 5 lines · Evgeniy Polyakov
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/net/sock.h +0 -0
Why are you moving around SOCKET_I() and SOCK_INODE() ?
kernel/kevent/kevent_naio.c new file +0 -0
Review
Quoted 7 lines
(quoted diff) +7
-0+ if (!file->f_dentry || !file->f_dentry->d_inode) + goto err_out_fput; + if (file->f_op != &socket_file_ops) + goto err_out_fput; + + sk = SOCKET_I(file->f_dentry->d_inode)->sk; +
You dont need to acess the dentry to get the socket from the file pointer.
(cf net/socket.c , function sock_from_file())
if (file->f_op != &socket_file_ops)
goto err_out_fput;
sk = file->private_data;
(I repeat myself on this point, because I have a patch here so that only one common dentry is allocated for all the sockets to reduce ram usage on big servers : Everything but /proc/pid/fd is fine)
Eric
Re: [take5 3/4] kevent: Network AIO, socket notifications.
Quoted 12 lines · Eric Dumazet
Quoted 5 lines · Evgeniy Polyakov
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/net/sock.h +0
-0
Quoted 1 line
Why are you moving around
SOCKET_I()andSOCK_INODE()?
I used kevent notification above, which requires socket->inode transformation (to save some space inside struct sock/socket I use inode as storage both for socket and inode notifications)
Quoted 19 lines
kernel/kevent/kevent_naio.c new file +0
-0Review
Quoted 8 lines
(quoted diff) +7
-0+ if (!file->f_dentry || !file->f_dentry->d_inode) + goto err_out_fput; + if (file->f_op != &socket_file_ops) + goto err_out_fput; + + sk = SOCKET_I(file->f_dentry->d_inode)->sk; +You dont need to acess the dentry to get the socket from the file pointer.
(cf
net/socket.c, functionsock_from_file())
Quoted 7 lines
if (
file->f_op!=&socket_file_ops)goto err_out_fput;sk =
file->private_data;(I repeat myself on this point, because I have a patch here so that only one common dentry is allocated for all the sockets to reduce ram usage on big servers : Everything but
/proc/pid/fdis fine)
It will not be struct sock, but struct socket, i.e. only those sockets which have userspace process attached.
Quoted 2 lines
Eric
Signature · 1 line
Evgeniy Polyakov
Re: [take5 1/4] kevent: Core files.
In reply to Evgeniy PolyakovQuoted 2 lines
+++
b/include/linux/kevent.h
...
include/linux/kevent.h line 244
Quoted 3 lines
include/linux/kevent.h new file +3
-0+#ifdef CONFIG_KEVENT_SOCKET + +extern struct file_operations socket_file_ops;
This doesn't build because socket_file_ops was left static in net/socket.c.
In any case, kevent.h has no business exposing socket_file_ops to users of the kevent api just so the kevent core can test files as being backed by sockets. It'd be more appropriate to call into the socket layer with the filp and let it return -EINVAL or -ESOCKNOOPT instead of trying to do that in the kevent layer.
- z
Re: [take5 1/4] kevent: Core files.
Quoted 10 lines · Zach Brown
Quoted 2 lines
+++
b/include/linux/kevent.h...
include/linux/kevent.h line 244
Quoted 3 lines
include/linux/kevent.h new file +3
-0+#ifdef CONFIG_KEVENT_SOCKET + +extern struct file_operations socket_file_ops;This doesn't build because
socket_file_opswas left static innet/socket.c.
I exported it. It just sneaked out of patchset.
Quoted 5 lines
In any case,
kevent.hhas no business exposingsocket_file_opsto users of the kevent api just so the kevent core can test files as being backed by sockets. It'd be more appropriate to call into the socket layer with the filp and let it return-EINVALor-ESOCKNOOPTinstead of trying to do that in the kevent layer.
Ok, I will move to use some functions from socket code without exporting socket_file_ops.
Quoted 1 line
- z
Signature · 1 line
Evgeniy Polyakov
Re: [take5 0/4] kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovQuoted 6 lines · Evgeniy Polyakov
Generic event handling mechanism.
I send this patchset for comments and review, it still contains AIO and
aio_sendfile()implementation on top ofget_block()abstraction, which was decided to postpone for a while (it is simpler right now to generate patchset as a whole, when kevent will be ready for merge, I will generate patchset without AIO stuff).
I think that's the wrong order. Let's see the clean patch before deciding to merge it. Just remove the current aio_sendfile implementation. We can build up one that is suitable for merging once the kevent core is ready for merging.
Quoted 2 lines
Since number of suggested changes goes from 'several' to 'very small', I'm asking for inclusion or declining.
Well, I'm a little confused. It still seems like we have a long way to go before having something that we'll want to merge. There's still trivial things that need fixing like using LIST_POISON outside list.h and that nutty 1 second "default" timeout thing, both previously mentioned. Then there's much more fundamental stuff like still returning events from the _wait() syscall. I thought the notion was that the wait syscall would only wait and that events were always to be collected from the ring.
David, what do you think about the networking calls in here? Are they suitable for merging? I've been assuming that you'll want much more code re-use, but I don't remember seeing you say either way.
There's also something that's been bugging me that I want to ask the crowd. The kevent work is doing the same thing fs/aio.c did: for an API to support async operation we duplicate its existing system call API. In fs/aio.c it was IO_CMD_* and struct uiocb members, with kevent it's a system call like sys_aio_sendfile(). This seems like it might get to be a little much if we bring async support to much of networking, disk io, and say timers. That's a lot of interfaces to duplicate. Is this the direction we want to take? One could imagine doing a wrapper syscall that might allocate a kevent struct and hang it off task_struct for subsystems to recognize and work with, but that'd bring different trade-offs.
Quoted 1 line
Should I prepare final patchset and what should it include?
Can you address the things I mentioned last time I looked through the code and remove the parts of the patch that we know aren't going to be merged?
- z
Re: [take5 0/4] kevent: Generic event handling mechanism.
Quoted 8 lines · Zach Brown
Quoted 6 lines · Evgeniy Polyakov
Generic event handling mechanism.
I send this patchset for comments and review, it still contains AIO and
aio_sendfile()implementation on top ofget_block()abstraction, which was decided to postpone for a while (it is simpler right now to generate patchset as a whole, when kevent will be ready for merge, I will generate patchset without AIO stuff).
Quoted 4 lines
I think that's the wrong order. Let's see the clean patch before deciding to merge it. Just remove the current
aio_sendfileimplementation. We can build up one that is suitable for merging once the kevent core is ready for merging.
That's why I'm asking for what exactly should be moved into the patchset. I.e. do we need poll/select (it is the same as what epoll does), timer, network AIO? How they should be splitted?
Quoted 11 lines
Quoted 2 lines
Since number of suggested changes goes from 'several' to 'very small', I'm asking for inclusion or declining.
Well, I'm a little confused. It still seems like we have a long way to go before having something that we'll want to merge. There's still trivial things that need fixing like using
LIST_POISONoutsidelist.hand that nutty 1 second "default" timeout thing, both previously mentioned. Then there's much more fundamental stuff like still returning events from the_wait()syscall. I thought the notion was that the wait syscall would only wait and that events were always to be collected from the ring.
Using LIST_POISON is a flag that kevent is in appropriate queue or not, I can add some flag into the structure, but why, if it is clear just by looking into list's pointers.
Thanks for mentioning timeout issue, I forgot about it, that will be fixed.
I decided to allow to get events using syscall until glibc guys are happy with how things work (if any). Then it can be trivially removed.
Quoted 3 lines
David, what do you think about the networking calls in here? Are they suitable for merging? I've been assuming that you'll want much more code re-use, but I don't remember seeing you say either way.
The main problem with code reuse is that network AIO uses pinned pages (until there will be a method to allocate "dma" memory are) and thus uses memcpy() instead of copy_*_user(), so for full reuse we need some kind of extending iovec, which is exported to userspace, or add some flags into skbs, which is not the right thing to do, or heavily extend socket API to include pointer to "copying" function itself.
Quoted 12 lines
There's also something that's been bugging me that I want to ask the crowd. The kevent work is doing the same thing
fs/aio.cdid: for an API to support async operation we duplicate its existing system call API. Infs/aio.cit was IO_CMD_* and struct uiocb members, with kevent it's a system call likesys_aio_sendfile(). This seems like it might get to be a little much if we bring async support to much of networking, disk io, and say timers. That's a lot of interfaces to duplicate. Is this the direction we want to take? One could imagine doing a wrapper syscall that might allocate a kevent struct and hang it offtask_structfor subsystems to recognize and work with, but that'd bring different trade-offs.
Quoted 5 lines
Quoted 1 line
Should I prepare final patchset and what should it include?
Can you address the things I mentioned last time I looked through the code and remove the parts of the patch that we know aren't going to be merged?
I will remove AIO stuff from the next patchset.
Other things will be addressed as described.
Thank you.
Quoted 1 line
- z
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take5 0/4] kevent: Generic event handling mechanism.
From: Evgeniy Polyakov <[email protected]>
Date: Wed, 9 Aug 2006 09:31:14 +0400
I am burnt out on reviewing this endless kevent patch resubmissions, so I will only comment briefly.
Quoted 3 lines
That's why I'm asking for what exactly should be moved into the patchset. I.e. do we need poll/select (it is the same as what epoll does), timer, network AIO? How they should be splitted?
I would suggest removing all of the file I/O and network AIO since they are logically seperate from kevent.
Quoted 3 lines
Using
LIST_POISONis a flag that kevent is in appropriate queue or not, I can add some flag into the structure, but why, if it is clear just by looking into list's pointers.
What is wrong with using list_empty() as this indicator?
Re: [take5 0/4] kevent: Generic event handling mechanism.
Quoted 5 lines · David Miller
Quoted 3 lines
Using
LIST_POISONis a flag that kevent is in appropriate queue or not, I can add some flag into the structure, but why, if it is clear just by looking into list's pointers.What is wrong with using
list_empty()as this indicator?
RCU only replaces ->prev pointer, and list_empty(entry) checks for entry->next == head, but actually I do not see how kevent entry can point to itself, so I will try this.
Signature · 1 line
Evgeniy Polyakov
Re: [take5 0/4] kevent: Generic event handling mechanism.
Quoted 10 lines · Evgeniy Polyakov
Quoted 5 lines · David Miller
Quoted 3 lines
Using
LIST_POISONis a flag that kevent is in appropriate queue or not, I can add some flag into the structure, but why, if it is clear just by looking into list's pointers.What is wrong with using
list_empty()as this indicator?RCU only replaces ->prev pointer, and
list_empty(entry)checks forentry->next== head, but actually I do not see how kevent entry can point to itself, so I will try this.
Unfortunately it is impossible to use list_empty(), since due to RCU issues kevent can not use list_del_init(), so list_empty() will always show that list is not empty.
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take5 0/4] kevent: Generic event handling mechanism.
From: Evgeniy Polyakov <[email protected]>
Date: Wed, 9 Aug 2006 10:25:15 +0400
Quoted 3 lines
Unfortunately it is impossible to use
list_empty(), since due to RCU issues kevent can not uselist_del_init(), solist_empty()will always show that list is not empty.
I see, just use the LIST_POISON* scheme for now, it's something we can refine later.
Re: [take5 0/4] kevent: Generic event handling mechanism.
Quoted 9 lines · David Miller
From: Evgeniy Polyakov <[email protected]>
Date: Wed, 9 Aug 2006 10:25:15 +0400Quoted 3 lines
Unfortunately it is impossible to use
list_empty(), since due to RCU issues kevent can not uselist_del_init(), solist_empty()will always show that list is not empty.I see, just use the LIST_POISON* scheme for now, it's something we can refine later.
I've fixed timout issue Zach pointed, and will reend kevent 'take6' patchset without AIO stuff soon.
Question with kevents removal from syscall stays open until Ulrich accepts or declines mapped buffer implementation.
Signature · 1 line
Evgeniy Polyakov
Re: [take5 0/4] kevent: Generic event handling mechanism.
Quoted 2 lines · Evgeniy Polyakov
Question with kevents removal from syscall stays open until Ulrich accepts or declines mapped buffer implementation.
It was my idea in the first place to use the ring buffer. I'm sure others had the same idea but that's what I presented. So, I see no reason you should delay making this change because of me.
The only important thing is that we need to get a useful semantics for fork and exec. For fork, it must be possible to "dequeue" entries from the ring buffer in a thread-safe way. For exec (where a file descriptor might survive) we likely need a mechanism to mmap the ring buffer only based on the file descriptor. I'm not sure about this, though.
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
David Miller
maintainerRe: [take5 0/4] kevent: Generic event handling mechanism.
From: Ulrich Drepper <[email protected]>
Date: Tue, 08 Aug 2006 23:57:18 -0700
Quoted 3 lines
For exec (where a file descriptor might survive) we likely need a mechanism to mmap the ring buffer only based on the file descriptor. I'm not sure about this, though.
Unless we can come up with a serious legitimate use for this, I think we can forego it for now.
Re: [take5 0/4] kevent: Generic event handling mechanism.
In reply to Ulrich DrepperQuoted 7 lines · Ulrich Drepper
Quoted 2 lines · Evgeniy Polyakov
Question with kevents removal from syscall stays open until Ulrich accepts or declines mapped buffer implementation.
It was my idea in the first place to use the ring buffer. I'm sure others had the same idea but that's what I presented. So, I see no reason you should delay making this change because of me.
I did it already :)
So I'm waiting for you to decide if it is what you want, or it should be implemented in a different way.
Quoted 5 lines
The only important thing is that we need to get a useful semantics for fork and exec. For fork, it must be possible to "dequeue" entries from the ring buffer in a thread-safe way. For exec (where a file descriptor might survive) we likely need a mechanism to mmap the ring buffer only based on the file descriptor. I'm not sure about this, though.
Since reading of events from tha mapped ring can be unlimited and they are supposed to be removed only by syscall (but not copy them), and syscall itself is thread safe, there should be no problems with fork case.
Quoted 3 lines
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Signature · 1 line
Evgeniy Polyakov
Re: [take5 0/4] kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovQuoted 10 lines · Evgeniy Polyakov
Quoted 5 lines · David Miller
Quoted 3 lines
Using
LIST_POISONis a flag that kevent is in appropriate queue or not, I can add some flag into the structure, but why, if it is clear just by looking into list's pointers.What is wrong with using
list_empty()as this indicator?RCU only replaces ->prev pointer, and
list_empty(entry)checks forentry->next== head, but actually I do not see how kevent entry can point to itself, so I will try this.
Looking at LIST_POISON1 is still not acceptable at all. It's an internal implementation details that goes and comes back once in a while. We already had a past discussion on a use of this and it went away, maybe Andrew can remember it.
Re: [take5 0/4] kevent: Generic event handling mechanism.
Quoted 8 lines · On Wed, 9 Aug 2006 09:34:01 +0100 Christoph Hellwig <[email protected]> wrote:
Quoted 7 lines · Evgeniy Polyakov
Quoted 5 lines · David Miller
Quoted 3 lines
Using
LIST_POISONis a flag that kevent is in appropriate queue or not, I can add some flag into the structure, but why, if it is clear just by looking into list's pointers.What is wrong with using
list_empty()as this indicator?
Quoted 8 lines
Quoted 3 lines
RCU only replaces ->prev pointer, and
list_empty(entry)checks forentry->next== head, but actually I do not see how kevent entry can point to itself, so I will try this.Looking at
LIST_POISON1is still not acceptable at all. It's an internal implementation details that goes and comes back once in a while. We already had a past discussion on a use of this and it went away, maybe Andrew can remember it.
The list_del() poisoning will dirty the cacheline which holds the to-be-deleted list_head. Unnecesarily. So it's relatively expensive.
Hence it's quite legitimate to simply remove that poisoning code, and we might choose to do that at any time in the future, or we might move it under CONFIG_DEBUG_FOO.
So it isn't yours to play with ;)
cover kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovGeneric event handling mechanism.
Changes from 'take5' patchset:
* removed compilation warnings about unused wariables when lockdep is not turned on
* do not use internal socket structures, use appropriate (exported) wrappers instead
* removed default 1 second timeout
* removed AIO stuff from patchset
Changes from 'take4' patchset:
* use miscdevice instead of chardevice
* comments fixes
Changes from 'take3' patchset:
* removed serializing mutex from kevent_user_wait()
* moved storage list processing to RCU
* removed lockdep screaming - all storage locks are initialized in the same function, so it was learned to differentiate between various cases
* remove kevent from storage if is marked as broken after callback
* fixed a typo in mmaped buffer implementation which would end up in wrong index calcualtion
Changes from 'take2' patchset:
* split kevent_finish_user() to locked and unlocked variants
* do not use KEVENT_STAT ifdefs, use inline functions instead
* use array of callbacks of each type instead of each kevent callback initialization
* changed name of ukevent guarding lock
* use only one kevent lock in kevent_user for all hash buckets instead of per-bucket locks
* do not use kevent_user_ctl structure instead provide needed arguments as syscall parameters
* various indent cleanups
* added optimisation, which is aimed to help when a lot of kevents are being copied from userspace
* mapped buffer (initial) implementation (no userspace yet)
Changes from 'take1' patchset:
- rebased against 2.6.18-git tree
- removed ioctl controlling
- added new syscall kevent_get_events(int fd, unsigned int min_nr, unsigned int max_nr, unsigned int timeout, void __user *buf, unsigned flags)
- use old syscall kevent_ctl for creation/removing, modification and initial kevent initialization
- use mutuxes instead of semaphores
- added file descriptor check and return error if provided descriptor does not match kevent file operations
- various indent fixes
- removed aio_sendfile() declarations.
Thank you.
PATCH 1/3 kevent: Core files.
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
It might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
arch/i386/kernel/syscall_table.S +4 -0
@@ -317,3 +317,7 @@ ENTRY(sys_call_table) .long sys_tee /* 315 */ .long sys_vmsplice .long sys_move_pages + .long sys_aio_recv + .long sys_aio_send + .long sys_kevent_get_events + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +4 -0
@@ -713,4 +713,8 @@ #endif .quad sys_tee .quad compat_sys_vmsplice .quad compat_sys_move_pages + .quad sys_aio_recv + .quad sys_aio_send + .quad sys_kevent_get_events + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +5 -1
@@ -323,10 +323,14 @@ #define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316#define __NR_move_pages 317 +#define __NR_aio_recv 318 +#define __NR_aio_send 319 +#define __NR_kevent_get_events 320 +#define __NR_kevent_ctl 321#ifdef __KERNEL__ -#define NR_syscalls 318 +#define NR_syscalls 322/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/unistd.h +9 -1
@@ -619,10 +619,18 @@ #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice)#define __NR_move_pages 279 __SYSCALL(__NR_move_pages, sys_move_pages) +#define __NR_aio_recv 280 +__SYSCALL(__NR_aio_recv, sys_aio_recv) +#define __NR_aio_send 281 +__SYSCALL(__NR_aio_send, sys_aio_send) +#define __NR_kevent_get_events 282 +__SYSCALL(__NR_kevent_get_events, sys_kevent_get_events) +#define __NR_kevent_ctl 283 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_move_pages +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS
include/linux/kevent.h new file +296 -0
296 lines not on this page · raw message
include/linux/kevent_storage.h new file +12 -0
@@ -0,0 +1,12 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +unsigned int qlen;/* Number of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +5 -0
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_aio_recv (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_aio_send (int ctl_fd,int s,void __user *buf,size_t size,unsigned flags); +asmlinkage long sys_kevent_get_events (int ctl_fd,unsigned int min,unsigned int max, +unsigned int timeout,void __user *buf,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,unsigned int cmd,unsigned int num,void __user *buf);#endif
init/Kconfig +2 -0
@@ -218,6 +218,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o
kernel/kevent/Kconfig new file +50 -0
50 lines not on this page · raw message
kernel/kevent/Makefile new file +6 -0
@@ -0,0 +1,6 @@ +obj-y := kevent.o kevent_user.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o
kernel/kevent/kevent.c new file +238 -0
238 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +857 -0
857 lines not on this page · raw message
kernel/sys_ni.c +5 -0
@@ -122,6 +122,11 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_aio_recv); +cond_syscall (sys_aio_send); +cond_syscall (sys_kevent_get_events); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
PATCH 3/3 kevent: Network AIO, socket notifications.
Network AIO, socket notifications.
This patchset includes socket notifications and network asynchronous IO. Network AIO is based on kevent and works as usual kevent storage on top of inode.
include/asm-i386/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/asm-x86_64/socket.h +2 -0
@@ -50,4 +50,6 @@ #define SO_ACCEPTCONN 30#define SO_PEERSEC 31#define SO_PASSSEC 34 +#define SO_ASYNC_SOCK 35 +#endif /* _ASM_SOCKET_H */
include/linux/skbuff.h +2 -0
@@ -1283,6 +1283,8 @@ extern struct sk_buff *skb_recv_datagram int noblock, int *err);extern unsigned int datagram_poll (struct file *file,struct socket *sock,struct poll_table_struct *wait); +extern int skb_copy_datagram (const struct sk_buff *from, +int offset,void *dst,int size);extern int skb_copy_datagram_iovec (const struct sk_buff *from,int offset,struct iovec *to,int size);
include/net/sock.h +25 -15
@@ -48,6 +48,7 @@ #include <linux/lockdep.h>#include <linux/netdevice.h> #include <linux/skbuff.h> /* struct sk_buff */ #include <linux/security.h> +#include <linux/kevent.h> #include <linux/filter.h>
@@ -391,6 +392,8 @@ enum sock_flags {SOCK_RCVTSTAMP ,/* %SO_TIMESTAMP setting */ SOCK_LOCALROUTE ,/* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK ,/* write queue has been shrunk recently */ +SOCK_ASYNC , + SOCK_ASYNC_INUSE, };static inline void sock_copy_flags (struct sock *nsk,struct sock *osk)
@@ -450,6 +453,21 @@ static inline int sk_stream_memory_free(extern void sk_stream_rfree (struct sk_buff *skb); +struct socket_alloc { +struct socket socket; +struct inode vfs_inode; +}; + +static inline struct socket *SOCKET_I (struct inode *inode) +{ + return &container_of(inode, struct socket_alloc, vfs_inode)->socket; +} + +static inline struct inode *SOCK_INODE (struct socket *socket) +{ + return &container_of(socket, struct socket_alloc, socket)->vfs_inode; +} +static inline void sk_stream_set_owner_r (struct sk_buff *skb,struct sock *sk) { skb->sk = sk;
@@ -477,6 +495,7 @@ static inline void sk_add_backlog(struct sk->sk_backlog.tail = skb; } skb->next =NULL ; +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); }#define sk_wait_event (__sk, __timeo, __condition) \
@@ -548,6 +567,12 @@ struct proto {int (*backlog_rcv) (struct sock *sk,struct sk_buff *skb); + +int (*async_recv) (struct sock *sk, +void *dst,size_t size); +int (*async_send) (struct sock *sk, +struct page **pages,unsigned int poffset, +size_t size);/* Keeping track of sk's, looking them up, and port selection methods. */ void (*hash)(struct sock *sk);
@@ -679,21 +704,6 @@ static inline struct kiocb *siocb_to_kioreturn si->kiocb; } -struct socket_alloc { -struct socket socket; -struct inode vfs_inode; -}; - -static inline struct socket *SOCKET_I (struct inode *inode) -{ - return &container_of(inode, struct socket_alloc, vfs_inode)->socket; -} - -static inline struct inode *SOCK_INODE (struct socket *socket) -{ - return &container_of(socket, struct socket_alloc, socket)->vfs_inode; -} -extern void __sk_stream_mem_reclaim (struct sock *sk);extern int sk_stream_mem_schedule (struct sock *sk,int size,int kind);
include/net/tcp.h +3 -0
@@ -364,6 +364,8 @@ extern int compat_tcp_setsockopt(struc int level, int optname, char __user *optval, int optlen);extern void tcp_set_keepalive (struct sock *sk,int val); +extern int tcp_async_recv (struct sock *sk,void *dst,size_t size); +extern int tcp_async_send (struct sock *sk,struct page **pages,unsigned int poffset,size_t size); extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
@@ -857,6 +859,7 @@ static inline int tcp_prequeue(struct so tp->ucopy.memory =0 ; }else if (skb_queue_len (&tp->ucopy.prequeue) ==1 ) {wake_up_interruptible (sk->sk_sleep); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND );if (!inet_csk_ack_scheduled (sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, (3 * TCP_RTO_MIN) / 4,
kernel/kevent/kevent_naio.c new file +237 -0
237 lines not on this page · raw message
kernel/kevent/kevent_socket.c new file +144 -0
144 lines not on this page · raw message
net/core/datagram.c +55 -0
67 lines not on this page · raw message
net/core/sock.c +19 -1
@@ -617,6 +617,16 @@ #endifspin_unlock_bh (&sk->sk_lock.slock); ret = -ENONET ;break ; +#ifdef CONFIG_KEVENT_SOCKET +case SO_ASYNC_SOCK : +spin_lock_bh (&sk->sk_lock.slock); +if (valbool) +sock_set_flag (sk,SOCK_ASYNC ); +else +sock_reset_flag (sk,SOCK_ASYNC ); +spin_unlock_bh (&sk->sk_lock.slock); +break ; +#endif case SO_PASSSEC :if (valbool)
@@ -1406,6 +1416,7 @@ static void sock_def_wakeup(struct sockif (sk->sk_sleep &&waitqueue_active (sk->sk_sleep))wake_up_interruptible_all (sk->sk_sleep);read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_error_report (struct sock *sk)
@@ -1415,6 +1426,7 @@ static void sock_def_error_report(structwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,0 ,POLL_ERR );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_readable (struct sock *sk,int len)
@@ -1424,6 +1436,7 @@ static void sock_def_readable(struct socwake_up_interruptible (sk->sk_sleep);sk_wake_async (sk,1 ,POLL_IN );read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); }static void sock_def_write_space (struct sock *sk)
@@ -1443,6 +1456,7 @@ static void sock_def_write_space(struct }read_unlock (&sk->sk_callback_lock); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); }static void sock_def_destruct (struct sock *sk)
@@ -1493,6 +1507,8 @@ #endif sk->sk_state =TCP_CLOSE ; sk->sk_socket = sock; +kevent_sk_reinit (sk); +sock_set_flag (sk,SOCK_ZAPPED );if (sock)
@@ -1559,8 +1575,10 @@ void fastcall release_sock(struct sock *if (sk->sk_backlog.tail)__release_sock (sk); sk->sk_lock.owner =NULL ; -if (waitqueue_active (&sk->sk_lock.wq)) +if (waitqueue_active (&sk->sk_lock.wq)) {wake_up (&sk->sk_lock.wq); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV |KEVENT_SOCKET_SEND ); + }spin_unlock_bh (&sk->sk_lock.slock); }EXPORT_SYMBOL (release_sock);
net/core/stream.c +1 -0
@@ -36,6 +36,7 @@ void sk_stream_write_space(struct sock *wake_up_interruptible (sk->sk_sleep);if (sock->fasync_list && !(sk->sk_shutdown &SEND_SHUTDOWN ))sock_wake_async (sock,2 ,POLL_OUT ); +kevent_socket_notify (sk,KEVENT_SOCKET_SEND |KEVENT_SOCKET_RECV ); } }
net/ipv4/tcp.c +298 -0
316 lines not on this page · raw message
net/ipv4/tcp_input.c +3 -1
@@ -3112,6 +3112,7 @@ static void tcp_ofo_queue(struct sock *s__skb_unlink (skb, &tp->out_of_order_queue);__skb_queue_tail (&sk->sk_receive_queue, skb); +kevent_socket_notify (sk,KEVENT_SOCKET_RECV ); tp->rcv_nxt =TCP_SKB_CB (skb)->end_seq;if (skb->h.th->fin)tcp_fin (skb, sk, skb->h.th);
@@ -3955,7 +3956,8 @@ int tcp_rcv_established(struct sock *sk,int copied_early =0 ;if (tp->copied_seq == tp->rcv_nxt && - len - tcp_header_len <= tp->ucopy.len) { + len - tcp_header_len <= tp->ucopy.len && + !sock_async (sk)) {#ifdef CONFIG_NET_DMA if (tcp_dma_try_early_copy (sk, skb, tcp_header_len)) { copied_early =1 ;
net/ipv4/tcp_ipv4.c +25 -15
@@ -61,6 +61,7 @@ #include <linux/cache.h>#include <linux/jhash.h> #include <linux/init.h> #include <linux/times.h> +#include <linux/kevent.h> #include <net/icmp.h> #include <net/inet_hashtables.h>
@@ -868,6 +869,7 @@ #endifreqsk_free (req); }else {inet_csk_reqsk_queue_hash_add (sk, req,TCP_TIMEOUT_INIT ); +kevent_socket_notify (sk,KEVENT_SOCKET_ACCEPT ); }return 0 ;
@@ -1108,24 +1110,30 @@ process: skb->dev =NULL ; -bh_lock_sock_nested (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock_nested (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan =get_softnet_dma (); -if (tp->ucopy.dma_chan) - ret =tcp_v4_do_rcv (sk, skb); - else +struct tcp_sock *tp =tcp_sk (sk); +if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan =get_softnet_dma (); +if (tp->ucopy.dma_chan) + ret =tcp_v4_do_rcv (sk, skb); + else#endif - { -if (!tcp_prequeue (sk, skb)) - ret =tcp_v4_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v4_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);
@@ -1849,6 +1857,8 @@ struct proto tcp_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v4_do_rcv, .hash = tcp_v4_hash, .unhash = tcp_unhash,
net/ipv6/tcp_ipv6.c +21 -13
@@ -1230,22 +1230,28 @@ process: skb->dev =NULL ; -bh_lock_sock (sk); ret =0 ; -if (!sock_owned_by_user (sk)) { +if (sock_async (sk)) { +spin_lock_bh (&sk->sk_lock.slock); + ret =tcp_v4_do_rcv (sk, skb); +spin_unlock_bh (&sk->sk_lock.slock); + }else { +bh_lock_sock (sk); +if (!sock_owned_by_user (sk)) {#ifdef CONFIG_NET_DMA -struct tcp_sock *tp =tcp_sk (sk); -if (tp->ucopy.dma_chan) - ret =tcp_v6_do_rcv (sk, skb); - else -#endif - { -if (!tcp_prequeue (sk, skb)) +struct tcp_sock *tp =tcp_sk (sk); +if (tp->ucopy.dma_chan) ret =tcp_v6_do_rcv (sk, skb); - } - }else -sk_add_backlog (sk, skb); -bh_unlock_sock (sk); + else +#endif + { +if (!tcp_prequeue (sk, skb)) + ret =tcp_v6_do_rcv (sk, skb); + } + }else +sk_add_backlog (sk, skb); +bh_unlock_sock (sk); + }sock_put (sk);return ret ?-1 :0 ;
@@ -1596,6 +1602,8 @@ struct proto tcpv6_prot = { .getsockopt = tcp_getsockopt, .sendmsg = tcp_sendmsg, .recvmsg = tcp_recvmsg, + .async_recv = tcp_async_recv, + .async_send = tcp_async_send, .backlog_rcv = tcp_v6_do_rcv, .hash = tcp_v6_hash, .unhash = tcp_unhash,
PATCH 2/3 kevent: poll/select() notifications. Timer notifications.
poll/select() notifications. Timer notifications.
This patch includes generic poll/select and timer notifications.
kevent_poll works similar to epoll and has the same issues (callback is invoked not from internal state machine of the caller, but through process awake).
Timer notifications can be used for fine grained per-process time management, since interval timers are very inconvenient to use, and they are limited.
kernel/kevent/kevent_poll.c new file +220 -0
@@ -0,0 +1,220 @@ +/* +* kevent_poll.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/file.h> +#include <linux/kevent.h> +#include <linux/poll.h> +#include <linux/fs.h> + +static kmem_cache_t *kevent_poll_container_cache; +static kmem_cache_t *kevent_poll_priv_cache; + +struct kevent_poll_ctl +{ +struct poll_table_struct pt; +struct kevent *k; +}; + +struct kevent_poll_wait_container +{ +struct list_head container_entry; +wait_queue_head_t *whead; +wait_queue_t wait; +struct kevent *k; +}; + +struct kevent_poll_private +{ +struct list_head container_list; +spinlock_t container_lock; +}; + +static int kevent_poll_enqueue (struct kevent *k); +static int kevent_poll_dequeue (struct kevent *k); +static int kevent_poll_callback (struct kevent *k); + +static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ +struct kevent_poll_wait_container *cont = + container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +u32 revents; + + revents = file->f_op->poll (file,NULL ); + +kevent_storage_ready (k->st,NULL , revents); + +return 0 ; +} + +static void kevent_poll_qproc (struct file *file,wait_queue_head_t *whead, +struct poll_table_struct *poll_table) +{ +struct kevent *k = + container_of(poll_table, struct kevent_poll_ctl, pt)->k; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *cont; +unsigned long flags; + + cont =kmem_cache_alloc (kevent_poll_container_cache,SLAB_KERNEL ); +if (!cont) { +kevent_break (k); +return ; + } + + cont->k = k; +init_waitqueue_func_entry (&cont->wait, kevent_poll_wait_callback); + cont->whead = whead; + +spin_lock_irqsave (&priv->container_lock, flags); +list_add_tail (&cont->container_entry, &priv->container_list); +spin_unlock_irqrestore (&priv->container_lock, flags); + +add_wait_queue (whead, &cont->wait); +} + +static int kevent_poll_enqueue (struct kevent *k) +{ +struct file *file; +int err, ready =0 ; +unsigned int revents; +struct kevent_poll_ctl ctl; +struct kevent_poll_private *priv; + + file =fget (k->event.id.raw[0 ]); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_op || !file->f_op->poll) + goto err_out_fput; + + err = -ENOMEM ; + priv =kmem_cache_alloc (kevent_poll_priv_cache,SLAB_KERNEL ); +if (!priv) + goto err_out_fput; + +spin_lock_init (&priv->container_lock); +INIT_LIST_HEAD (&priv->container_list); + + k->priv = priv; + + ctl.k = k; +init_poll_funcptr (&ctl.pt, &kevent_poll_qproc); + + err =kevent_storage_enqueue (&file->st, k); +if (err) + goto err_out_free; + + revents = file->f_op->poll (file, &ctl.pt); +if (revents & k->event.event) { + ready =1 ; +kevent_poll_dequeue (k); + } + +return ready; + +err_out_free: +kmem_cache_free (kevent_poll_priv_cache, priv); +err_out_fput: +fput (file); +return err; +} + +static int kevent_poll_dequeue (struct kevent *k) +{ +struct file *file = k->st->origin; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *w, *n; +unsigned long flags; + +kevent_storage_dequeue (k->st, k); + +spin_lock_irqsave (&priv->container_lock, flags); +list_for_each_entry_safe (w, n, &priv->container_list, container_entry) { +list_del (&w->container_entry); +remove_wait_queue (w->whead, &w->wait); +kmem_cache_free (kevent_poll_container_cache, w); + } +spin_unlock_irqrestore (&priv->container_lock, flags); + +kmem_cache_free (kevent_poll_priv_cache, priv); + k->priv =NULL ; + +fput (file); + +return 0 ; +} + +static int kevent_poll_callback (struct kevent *k) +{ +struct file *file = k->st->origin; +unsigned int revents = file->f_op->poll (file,NULL ); +return (revents & k->event.event); +} + +static int __init kevent_poll_sys_init (void ) +{ +struct kevent_callbacks *pc = &kevent_registered_callbacks[KEVENT_POLL ]; + + kevent_poll_container_cache =kmem_cache_create ("kevent_poll_container_cache" , +sizeof (struct kevent_poll_wait_container ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_container_cache) { +printk (KERN_ERR "Failed to create kevent poll container cache.\n" ); +return -ENOMEM ; + } + + kevent_poll_priv_cache =kmem_cache_create ("kevent_poll_priv_cache" , +sizeof (struct kevent_poll_private ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_priv_cache) { +printk (KERN_ERR "Failed to create kevent poll private data cache.\n" ); +kmem_cache_destroy (kevent_poll_container_cache); + kevent_poll_container_cache =NULL ; +return -ENOMEM ; + } + + pc->enqueue = &kevent_poll_enqueue; + pc->dequeue = &kevent_poll_dequeue; + pc->callback = &kevent_poll_callback; + +printk (KERN_INFO "Kevent poll()/select() subsystem has been initialized.\n" ); +return 0 ; +} + +static struct lock_class_key kevent_poll_key; + +void kevent_poll_reinit (struct file *file) +{ +lockdep_set_class (&file->st.lock, &kevent_poll_key); +} + +static void __exit kevent_poll_sys_fini(void) +{ +kmem_cache_destroy (kevent_poll_priv_cache); +kmem_cache_destroy (kevent_poll_container_cache); +} + +module_init (kevent_poll_sys_init); +module_exit (kevent_poll_sys_fini);
kernel/kevent/kevent_timer.c new file +119 -0
119 lines not on this page · raw message
Re: [take6 1/3] kevent: Core files.
In reply to Evgeniy PolyakovQuoted 29 lines · On Wed, 9 Aug 2006 12:02:40 +0400 Evgeniy Polyakov <[email protected]> wrote:
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machinesIt might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
Signed-off-by Evgeniy Polyakov <[email protected]>include/linux/kevent.h new file +12
-0+#ifdef CONFIG_KEVENT_USER_STAT +static inline void kevent_user_stat_init (struct kevent_user *u) +{ + u->wait_num = u->im_num = u->total =0 ; +} +static inline void kevent_user_stat_print (struct kevent_user *u) +{ +pr_debug ("%s: u=%p, wait=%lu, immediately=%lu, total=%lu.\n" , + __func__, u, u->wait_num, u->im_num, u->total); +} +static inline void kevent_user_stat_increase_im (struct kevent_user *u) +{
include/linux/kevent.h line 286
Quoted 10 lines
include/linux/kevent.h new file +10
-0+ u->im_num++; +} +static inline void kevent_user_stat_increase_wait (struct kevent_user *u) +{ + u->wait_num++; +} +static inline void kevent_user_stat_increase_total (struct kevent_user *u) +{ + u->total++; +}
static wrapper_functions_with_execessive_long_names(struct i_really_hate *this)
{
suck();
}Re: [take6 1/3] kevent: Core files.
Quoted 10 lines · Stephen Hemminger
include/linux/kevent.h line 286
Quoted 5 lines
include/linux/kevent.h new file +4
-0+static inline void kevent_user_stat_increase_total (struct kevent_user *u) +{ + u->total++; +}static
wrapper_functions_with_execessive_long_names(struct i_really_hate *this){ suck(); }
Understood...
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take6 1/3] kevent: Core files.
In reply to Stephen HemmingerFrom: Stephen Hemminger <[email protected]>
Date: Wed, 9 Aug 2006 10:47:38 -0700
Quoted 4 lines
static
wrapper_functions_with_execessive_long_names(struct i_really_hate *this){ suck(); }
Yes, typing 50 characters just to bump a counter, it's beyond rediculious.
Go hack on the X server if you like long-winded function names that do trivial operations :-)
Re: [take6 1/3] kevent: Core files.
In reply to Evgeniy PolyakovQuoted 9 lines · On Wed, 9 Aug 2006 12:02:40 +0400 Evgeniy Polyakov <[email protected]> wrote:
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
Quoted 3 lines
It might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
Summary:
- has serious bugs which indicate that much better testing is needed.
- All -EFOO return values need to be reviewed for appropriateness
- needs much better commenting before I can do more than a local-level review.
Quoted 10 lines
include/linux/kevent.h new file +6
-0+/* +* Poll events. +*/ +#define KEVENT_POLL_POLLIN 0x0001 +#define KEVENT_POLL_POLLPRI 0x0002 +#define KEVENT_POLL_POLLOUT 0x0004
include/linux/kevent.h line 86
Quoted 10 lines
include/linux/kevent.h new file +10
-0+#define KEVENT_POLL_POLLERR 0x0008 +#define KEVENT_POLL_POLLHUP 0x0010 +#define KEVENT_POLL_POLLNVAL 0x0020 + +#define KEVENT_POLL_POLLRDNORM 0x0040 +#define KEVENT_POLL_POLLRDBAND 0x0080 +#define KEVENT_POLL_POLLWRNORM 0x0100 +#define KEVENT_POLL_POLLWRBAND 0x0200 +#define KEVENT_POLL_POLLMSG 0x0400 +#define KEVENT_POLL_POLLREMOVE 0x1000
0x0800 got lost.
include/linux/kevent.h line 113
Quoted 13 lines
include/linux/kevent.h new file +13
-0+struct ukevent +{ +struct kevent_id id;/* Id of this request, e.g. socket number, file descriptor and so on... */ +__u32 type;/* Event type, e.g. KEVENT_SOCK, KEVENT_INODE, KEVENT_TIMER and so on... */ +__u32 event;/* Event itself, e.g. SOCK_ACCEPT, INODE_CREATED, TIMER_FIRED... */ +__u32 req_flags;/* Per-event request flags */ +__u32 ret_flags;/* Per-event return flags */ +__u32 ret_data[2 ];/* Event return data. Event originator fills it with anything it likes. */ +union { +__u32 user[2 ];/* User's data. It is not used, just copied to/from user. */ +void *ptr; + }; +};
What is this union for?
`ptr' needs a __user tag, does it not?
`ptr' will be 64-bit in-kernel and 64-bit for 64-bit userspace, but 32-bit for 32-bit userspace. I guess that's why user[] is there.
On big-endian machines, this pointer will appear to be word-swapped as far as a 64-bit kernel is concerned. Or something.
IOW: What's going on here??
Quoted 21 lines
include/linux/kevent.h new file +21
-0+#ifdef CONFIG_KEVENT_INODE +void kevent_inode_notify (struct inode *inode,u32 event); +void kevent_inode_notify_parent (struct dentry *dentry,u32 event); +void kevent_inode_remove (struct inode *inode); +#else +static inline void kevent_inode_notify (struct inode *inode,u32 event) +{ +} +static inline void kevent_inode_notify_parent (struct dentry *dentry,u32 event) +{ +} +static inline void kevent_inode_remove (struct inode *inode) +{ +} +#endif /* CONFIG_KEVENT_INODE */ +#ifdef CONFIG_KEVENT_SOCKET +#ifdef CONFIG_LOCKDEP +void kevent_socket_reinit (struct socket *sock); +void kevent_sk_reinit (struct sock *sk); +#else +static inline void kevent_socket_reinit (struct socket *sock)
include/linux/kevent.h line 257
Quoted 10 lines
include/linux/kevent.h new file +10
-0+{ +} +static inline void kevent_sk_reinit (struct sock *sk) +{ +} +#endif +void kevent_socket_notify (struct sock *sock,u32 event); +int kevent_socket_dequeue (struct kevent *k); +int kevent_socket_enqueue (struct kevent *k); +#define sock_async (__sk) sock_flag(__sk, SOCK_ASYNC)
Is this header the correct place to be implementing sock_async()?
kernel/kevent/Kconfig line 6
Quoted 9 lines
kernel/kevent/Kconfig new file +6
-0@@ -0,0 +1,50 @@ +config KEVENT + bool "Kernel event notification mechanism" + help + This option enables event queue mechanism. + It can be used as replacement for poll()/select(), AIO callback invocations, + advanced timer notifications and other kernel object status changes.
Please squeeze all the help text into 80-columns. Or at least check that it looks OK in menuconfig in an 80-col xterm,
Quoted 36 lines
kernel/kevent/kevent.c new file +33
-0@@ -0,0 +1,238 @@ +/* +* kevent.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/mempool.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/kevent.h> + +kmem_cache_t *kevent_cache; +
kernel/kevent/kevent.c line 43
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+/* +* Attempts to add an event into appropriate origin's queue. +* Returns positive value if this event is ready immediately, +* negative value in case of error and zero if event has been queued. +* ->enqueue() callback must increase origin's reference counter. +*/ +int kevent_enqueue (struct kevent *k) +{ +if (k->event.type >=KEVENT_MAX ) +return -E2BIG ;
E2BIG is "Argument list too long". EINVAL is appropriate here.
Quoted 40 lines
kernel/kevent/kevent.c new file +40
-0+if (!k->callbacks.enqueue) { +kevent_break (k); +return -EINVAL ; + } + +return k->callbacks.enqueue (k); +} + +/* +* Remove event from the appropriate queue. +* ->dequeue() callback must decrease origin's reference counter. +*/ +int kevent_dequeue (struct kevent *k) +{ +if (k->event.type >=KEVENT_MAX ) +return -E2BIG ; + +if (!k->callbacks.dequeue) { +kevent_break (k); +return -EINVAL ; + } + +return k->callbacks.dequeue (k); +} + +int kevent_break (struct kevent *k) +{ +unsigned long flags; + +spin_lock_irqsave (&k->ulock, flags); + k->event.ret_flags |=KEVENT_RET_BROKEN ; +spin_unlock_irqrestore (&k->ulock, flags); +return 0 ; +} + +struct kevent_callbacks kevent_registered_callbacks[KEVENT_MAX ]; + +/* +* Must be called before event is going to be added into some origin's queue. +* Initializes ->enqueue(), ->dequeue() and ->callback() callbacks.
kernel/kevent/kevent.c line 94
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+* If failed, kevent should not be used or kevent_enqueue() will fail to add +* this kevent into origin's queue with setting +* KEVENT_RET_BROKEN flag in kevent->event.ret_flags. +*/ +int kevent_init (struct kevent *k) +{ +spin_lock_init (&k->ulock); + k->kevent_entry.next =LIST_POISON1 ; + k->storage_entry.prev =LIST_POISON2 ; + k->ready_entry.next =LIST_POISON1 ;
Nope ;)
Quoted 17 lines
kernel/kevent/kevent.c new file +17
-0+if (k->event.type >=KEVENT_MAX ) +return -E2BIG ; + + k->callbacks = kevent_registered_callbacks[k->event.type]; +if (!k->callbacks.callback) { +kevent_break (k); +return -EINVAL ; + } + +return 0 ; +} + +/* +* Called from ->enqueue() callback when reference counter for given +* origin (socket, inode...) has been increased. +*/ +int kevent_storage_enqueue (struct kevent_storage *st,struct kevent *k)
kernel/kevent/kevent.c line 122
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+{ +unsigned long flags; + + k->st = st; +spin_lock_irqsave (&st->lock, flags); +list_add_tail_rcu (&k->storage_entry, &st->list); + st->qlen++; +spin_unlock_irqrestore (&st->lock, flags); +return 0 ; +}
Is the _rcu variant needed here?
kernel/kevent/kevent.c line 135
Quoted 12 lines
kernel/kevent/kevent.c new file +12
-0+/* +* Dequeue kevent from origin's queue. +* It does not decrease origin's reference counter in any way +* and must be called before it, so storage itself must be valid. +* It is called from ->dequeue() callback. +*/ +void kevent_storage_dequeue (struct kevent_storage *st,struct kevent *k) +{ +unsigned long flags; + +spin_lock_irqsave (&st->lock, flags); +if (k->storage_entry.prev !=LIST_POISON2 ) {
Nope, as discussed earlier.
Quoted 14 lines
kernel/kevent/kevent.c new file +14
-0+list_del_rcu (&k->storage_entry); + st->qlen--; + } +spin_unlock_irqrestore (&st->lock, flags); +} + +static void __kevent_requeue (struct kevent *k,u32 event) +{ +int err, rem =0 ; +unsigned long flags; + + err = k->callbacks.callback (k); + +spin_lock_irqsave (&k->ulock, flags);
kernel/kevent/kevent.c line 159
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+if (err >0 ) { + k->event.ret_flags |=KEVENT_RET_DONE ; + }else if (err <0 ) { + k->event.ret_flags |=KEVENT_RET_BROKEN ; + k->event.ret_flags |=KEVENT_RET_DONE ; + } + rem = (k->event.req_flags &KEVENT_REQ_ONESHOT ); +if (!err) + err = (k->event.ret_flags & (KEVENT_RET_BROKEN |KEVENT_RET_DONE )); +spin_unlock_irqrestore (&k->ulock, flags);
Local variable `err' no longer actually indicates an error, does it?
If not, a differently-named local would be appropriate here.
kernel/kevent/kevent.c line 164
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+if (err) { +if ((rem || err <0 ) && k->storage_entry.prev !=LIST_POISON2 ) { +list_del_rcu (&k->storage_entry); + k->st->qlen--;
->qlen was previously modified under spinlock. Here it is not.
Quoted 24 lines
kernel/kevent/kevent.c new file +24
-0+ } + +spin_lock_irqsave (&k->user->ready_lock, flags); +if (k->ready_entry.next ==LIST_POISON1 ) { +kevent_user_ring_add_event (k); +list_add_tail (&k->ready_entry, &k->user->ready_list); + k->user->ready_num++; + } +spin_unlock_irqrestore (&k->user->ready_lock, flags); +wake_up (&k->user->wait); + } +} + +void kevent_requeue (struct kevent *k) +{ +unsigned long flags; + +spin_lock_irqsave (&k->st->lock, flags); +__kevent_requeue (k,0 ); +spin_unlock_irqrestore (&k->st->lock, flags); +} + +/* +* Called each time some activity in origin (socket, inode...) is noticed.
kernel/kevent/kevent.c line 198
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+*/ +void kevent_storage_ready (struct kevent_storage *st, +kevent_callback_t ready_callback,u32 event) +{ +struct kevent *k; + +rcu_read_lock (); +list_for_each_entry_rcu (k, &st->list, storage_entry) { +if (ready_callback) +ready_callback (k);
For readability reasons I prefer the old-style
(*ready_callback)(k);
so the reader knows not to go off hunting for the function "ready_callback". Minor point.
So the kevent_callback_t handlers are not allowed to sleep.
kernel/kevent/kevent.c line 202
Quoted 3 lines
kernel/kevent/kevent.c new file +3
-0+if (event & k->event.event) +__kevent_requeue (k, event); + }
Under what circumstances will `event' be zero??
Quoted 15 lines
kernel/kevent/kevent.c new file +15
-0+rcu_read_unlock (); +} + +int kevent_storage_init (void *origin,struct kevent_storage *st) +{ +spin_lock_init (&st->lock); + st->origin = origin; + st->qlen =0 ; +INIT_LIST_HEAD (&st->list); +return 0 ; +} + +void kevent_storage_fini (struct kevent_storage *st) +{ +kevent_storage_ready (st, kevent_break,KEVENT_MASK_ALL );
kernel/kevent/kevent.c line 227
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+} + +static int __init kevent_sys_init (void ) +{ +int i; + + kevent_cache =kmem_cache_create ("kevent_cache" , +sizeof (struct kevent ),0 ,0 ,NULL ,NULL ); +if (!kevent_cache) +panic ("kevent: Unable to create a cache.\n" );
Can use SLAB_PANIC (a silly thing I added to avoid code duplication).
kernel/kevent/kevent.c line 233
Quoted 5 lines
kernel/kevent/kevent.c new file +5
-0+for (i=0 ; i<ARRAY_SIZE (kevent_registered_callbacks); ++i) { +struct kevent_callbacks *c = &kevent_registered_callbacks[i]; + + c->callback = c->enqueue = c->dequeue =NULL ; + }
This zeroing is redundant.
kernel/kevent/kevent.c line 238
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+return 0 ; +} + +late_initcall (kevent_sys_init);
Why is it late_initcall? (A comment is needed)
Quoted 60 lines
kernel/kevent/kevent_user.c new file +54
-0@@ -0,0 +1,857 @@ +/* +* kevent_user.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/mount.h> +#include <linux/device.h> +#include <linux/poll.h> +#include <linux/kevent.h> +#include <linux/jhash.h> +#include <linux/miscdevice.h> +#include <asm/io.h> + +static char kevent_name[] ="kevent" ; + +static int kevent_user_open (struct inode *,struct file *); +static int kevent_user_release (struct inode *,struct file *); +static unsigned int kevent_user_poll (struct file *,struct poll_table_struct *); +static int kevnet_user_mmap (struct file *,struct vm_area_struct *); + +static struct file_operations kevent_user_fops = { + .mmap = kevnet_user_mmap, + .open = kevent_user_open, + .release = kevent_user_release, + .poll = kevent_user_poll, + .owner =THIS_MODULE , +}; + +static struct miscdevice kevent_miscdev = { + .minor =MISC_DYNAMIC_MINOR ,
kernel/kevent/kevent_user.c line 64
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ .name = kevent_name, + .fops = &kevent_user_fops, +}; + +static int kevent_get_sb (struct file_system_type *fs_type, +int flags,const char *dev_name,void *data,struct vfsmount *mnt) +{ +/* So original magic... */ +return get_sb_pseudo (fs_type, kevent_name,NULL ,0xabcdef , mnt); +}
That doesn't look like a well-chosen magic number...
Quoted 17 lines
kernel/kevent/kevent_user.c new file +17
-0+static struct file_system_type kevent_fs_type = { + .name = kevent_name, + .get_sb = kevent_get_sb, + .kill_sb = kill_anon_super, +}; + +static struct vfsmount *kevent_mnt; + +static unsigned int kevent_user_poll (struct file *file,struct poll_table_struct *wait) +{ +struct kevent_user *u = file->private_data; +unsigned int mask; + +poll_wait (file, &u->wait, wait); + mask =0 ; + +if (u->ready_num)
kernel/kevent/kevent_user.c line 92
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ mask |=POLLIN |POLLRDNORM ; + +return mask; +} + +static inline void kevent_user_ring_set (struct kevent_user *u,unsigned int num) +{ +unsigned int *idx; + + idx = (unsigned int *)u->pring[0 ];
This is a bit ugly.
kernel/kevent/kevent_user.c line 101
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+ idx[0 ] = num; +} + +/* +* Note that kevents does not exactly fill the page (each ukevent is 40 bytes), +* so we reuse 4 bytes at the begining of the first page to store index. +* Take that into account if you want to change size of struct ukevent. +*/ +#define KEVENTS_ON_PAGE (PAGE_SIZE/sizeof(struct ukevent))
How about doing
struct ukevent_ring {
unsigned int index;
struct ukevent[0];
}and removing all those nasty typeasting and offsetting games?
In fact you can even do
struct ukevent_ring {
struct ukevent[(PAGE_SIZE - sizeof(unsigned int)) /
sizeof(struct ukevent)];
unsigned int index;
};if you're careful ;)
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+/* +* Called under kevent_user->ready_lock, so updates are always protected. +*/ +void kevent_user_ring_add_event (struct kevent *k) +{ +unsigned int *idx_ptr, idx, pidx, off; +struct ukevent *ukev; +
kernel/kevent/kevent_user.c line 120
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ idx_ptr = (unsigned int *)k->user->pring[0 ]; + idx = idx_ptr[0 ]; + + pidx = idx/KEVENTS_ON_PAGE ; + off = idx%KEVENTS_ON_PAGE ; + +if (pidx ==0 ) + ukev = (struct ukevent *)(k->user->pring[pidx] +sizeof (unsigned int )); +else + ukev = (struct ukevent *)(k->user->pring[pidx]);
Such as these.
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+memcpy (&ukev[off], &k->event,sizeof (struct ukevent )); + + idx++; +if (idx >=KEVENT_MAX_EVENTS )
kernel/kevent/kevent_user.c line 135
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ idx =0 ; + + idx_ptr[0 ] = idx; +} + +static int kevent_user_ring_init (struct kevent_user *u) +{ +int i, pnum; + + pnum =ALIGN (KEVENT_MAX_EVENTS *sizeof (struct ukevent ) +sizeof (unsigned int ),PAGE_SIZE )/PAGE_SIZE ;
And these.
kernel/kevent/kevent_user.c line 143
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+ u->pring =kmalloc (pnum *sizeof (unsigned long ),GFP_KERNEL ); +if (!u->pring) +return -ENOMEM ; + +for (i=0 ; i<pnum; ++i) { + u->pring[i] =__get_free_page (GFP_KERNEL ); +if (!u->pring)
bug: this is testing the wrong thing.
kernel/kevent/kevent_user.c line 150
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+break ; + } + +if (i != pnum) { + pnum = i; + goto err_out_free; + }
Move this logic into the `if (!u->pring)' logic, above.
Quoted 36 lines
kernel/kevent/kevent_user.c new file +36
-0+kevent_user_ring_set (u,0 ); + +return 0 ; + +err_out_free: +for (i=0 ; i<pnum; ++i) +free_page (u->pring[i]); + +kfree (u->pring); + +return -ENOMEM ; +} + +static void kevent_user_ring_fini (struct kevent_user *u) +{ +int i, pnum; + + pnum =ALIGN (KEVENT_MAX_EVENTS *sizeof (struct ukevent ) +sizeof (unsigned int ),PAGE_SIZE )/PAGE_SIZE ; + +for (i=0 ; i<pnum; ++i) +free_page (u->pring[i]); + +kfree (u->pring); +} + +static struct kevent_user *kevent_user_alloc (void ) +{ +struct kevent_user *u; +int i; + + u =kzalloc (sizeof (struct kevent_user ),GFP_KERNEL ); +if (!u) +return NULL ; + +INIT_LIST_HEAD (&u->ready_list); +spin_lock_init (&u->ready_lock);
kernel/kevent/kevent_user.c line 197
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ u->ready_num =0 ; +kevent_user_stat_init (u); +spin_lock_init (&u->kevent_lock); +for (i=0 ; i<ARRAY_SIZE (u->kevent_list); ++i) +INIT_LIST_HEAD (&u->kevent_list[i]); + u->kevent_num =0 ; + +mutex_init (&u->ctl_mutex); +init_waitqueue_head (&u->wait); + u->max_ready_num =0 ;
The above zeroes out a bunch of known-to-already-be-zero things.
kernel/kevent/kevent_user.c line 235
Quoted 1 line
kernel/kevent/kevent_user.c new file +1
-0+static int kevnet_user_mmap (struct file *file,struct vm_area_struct *vma)
The function name is mistyped.
This code doesn't have many comments, does it? What are we mapping here, and why would an application want to map it?
And what are the portability implications? Does userspace need to know the 64-bitness of its kernel to be able to work out the alignment of things? If so, what happens if a later/different compiler aligns things differently?
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+{ +size_t size = vma->vm_end - vma->vm_start, psize; +int pnum = size/PAGE_SIZE , i; +unsigned long start = vma->vm_start; +struct kevent_user *u = file->private_data; + + psize =ALIGN (KEVENT_MAX_EVENTS *sizeof (struct ukevent ) +sizeof (unsigned int ),PAGE_SIZE ); + +if (size + vma->vm_pgoff*PAGE_SIZE != psize) +return -EINVAL ; + +if (vma->vm_flags &VM_WRITE ) +return -EPERM ; + + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot);
kernel/kevent/kevent_user.c line 260
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ +for (i=0 ; i<pnum; ++i) { +if (remap_pfn_range (vma, start,virt_to_phys ((void *)u->pring[i+vma->vm_pgoff]),PAGE_SIZE , + vma->vm_page_prot)) +return -EAGAIN ; + start +=PAGE_SIZE ; + } + +return 0 ; +}
Is EAGAIN an appropriate return value?
If this function had a decent comment we could ask Hugh to review it.
Quoted 24 lines
kernel/kevent/kevent_user.c new file +24
-0+#if 0 +static inline unsigned int kevent_user_hash (struct ukevent *uk) +{ +unsigned int h = (uk->user[0 ] ^ uk->user[1 ]) ^ (uk->id.raw[0 ] ^ uk->id.raw[1 ]); + + h = (((h >>16 ) &0xffff ) ^ (h &0xffff )) &0xffff ; + h = (((h >>8 ) &0xff ) ^ (h &0xff )) &KEVENT_HASH_MASK ; + +return h; +} +#else +static inline unsigned int kevent_user_hash (struct ukevent *uk) +{ +return jhash_1word (uk->id.raw[0 ],0 ) &KEVENT_HASH_MASK ; +} +#endif + +static void kevent_free_rcu (struct rcu_head *rcu) +{ + struct kevent *kevent = container_of(rcu, struct kevent, rcu_head); +kmem_cache_free (kevent_cache, kevent); +} + +static void kevent_finish_user_complete (struct kevent *k,int deq)
kernel/kevent/kevent_user.c line 295
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+{ +struct kevent_user *u = k->user; +unsigned long flags; + +if (deq) +kevent_dequeue (k); + +spin_lock_irqsave (&u->ready_lock, flags); +if (k->ready_entry.next !=LIST_POISON1 ) { +list_del (&k->ready_entry);
list_del_rcu()?
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+ u->ready_num--; + } +spin_unlock_irqrestore (&u->ready_lock, flags); + +kevent_user_put (u); +call_rcu (&k->rcu_head, kevent_free_rcu);
kernel/kevent/kevent_user.c line 311
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+} + +static void __kevent_finish_user (struct kevent *k,int deq) +{ +struct kevent_user *u = k->user; + +list_del (&k->kevent_entry); + u->kevent_num--; +kevent_finish_user_complete (k, deq); +}
No locking needed?
It's hard to review uncommented code. And the review is less useful if the reviewer cannot determine what the developer was attempting to do.
kernel/kevent/kevent_user.c line 317
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+/* +* Remove kevent from user's list of all events, +* dequeue it from storage and decrease user's reference counter, +* since this kevent does not exist anymore. That is why it is freed here. +*/
That's nice.
kernel/kevent/kevent_user.c line 324
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+static void kevent_finish_user (struct kevent *k,int deq) +{ +struct kevent_user *u = k->user; +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); +list_del (&k->kevent_entry);
list_del_rcu()?
Quoted 35 lines
kernel/kevent/kevent_user.c new file +35
-0+ u->kevent_num--; +spin_unlock_irqrestore (&u->kevent_lock, flags); +kevent_finish_user_complete (k, deq); +} + +/* +* Dequeue one entry from user's ready queue. +*/ + +static struct kevent *kqueue_dequeue_ready (struct kevent_user *u) +{ +unsigned long flags; +struct kevent *k =NULL ; + +spin_lock_irqsave (&u->ready_lock, flags); +if (u->ready_num && !list_empty (&u->ready_list)) { + k = list_entry(u->ready_list.next, struct kevent, ready_entry); +list_del (&k->ready_entry); + u->ready_num--; + } +spin_unlock_irqrestore (&u->ready_lock, flags); + +return k; +} + +static struct kevent *__kevent_search (struct list_head *head,struct ukevent *uk, +struct kevent_user *u) +{ +struct kevent *k; +int found =0 ; + +list_for_each_entry (k, head, kevent_entry) { +spin_lock (&k->ulock); + if (k->event.user[0] == uk->user[0] && k->event.user[1] == uk->user[1] && + k->event.id.raw[0] == uk->id.raw[0] &&
kernel/kevent/kevent_user.c line 369
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ k->event.id.raw[1] == uk->id.raw[1]) { + found =1 ; +spin_unlock (&k->ulock); +break ; + } +spin_unlock (&k->ulock); + } + +return (found)?k:NULL ; +}
Remove `found', do
struct kevent *ret = NULL; ... ret = k; break; ... return ret;
kernel/kevent/kevent_user.c line 371
Quoted 1 line
kernel/kevent/kevent_user.c new file +1
-0+static int kevent_modify (struct ukevent *uk,struct kevent_user *u)
<wonders what this function does>
Quoted 11 lines
kernel/kevent/kevent_user.c new file +11
-0+{ +struct kevent *k; +unsigned int hash =kevent_user_hash (uk); +int err = -ENODEV ; +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); + k =__kevent_search (&u->kevent_list[hash], uk, u); +if (k) { +spin_lock (&k->ulock); + k->event.event = uk->event;
kernel/kevent/kevent_user.c line 392
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ k->event.req_flags = uk->req_flags; + k->event.ret_flags =0 ; +spin_unlock (&k->ulock); +kevent_requeue (k); + err =0 ; + } +spin_unlock_irqrestore (&u->kevent_lock, flags); + +return err; +}
ENODEV: "No such device". Doesn't sound appropriate.
Quoted 19 lines
kernel/kevent/kevent_user.c new file +19
-0+static int kevent_remove (struct ukevent *uk,struct kevent_user *u) +{ +int err = -ENODEV ; +struct kevent *k; +unsigned int hash =kevent_user_hash (uk); +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); + k =__kevent_search (&u->kevent_list[hash], uk, u); +if (k) { +__kevent_finish_user (k,1 ); + err =0 ; + } +spin_unlock_irqrestore (&u->kevent_lock, flags); + +return err; +} + +/*
kernel/kevent/kevent_user.c line 422
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+* No new entry can be added or removed from any list at this point. +* It is not permitted to call ->ioctl() and ->release() in parallel. +*/ +static int kevent_user_release (struct inode *inode,struct file *file) +{ +struct kevent_user *u = file->private_data; +struct kevent *k, *n; +int i; + +for (i=0 ; i<KEVENT_HASH_MASK +1 ; ++i) {
ARRAY_SIZE
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+list_for_each_entry_safe (k, n, &u->kevent_list[i], kevent_entry) +kevent_finish_user (k,1 ); + } + +kevent_user_put (u); + file->private_data =NULL ; + +return 0 ; +} + +static struct ukevent *kevent_get_user (unsigned int num,void __user *arg) +{ +struct ukevent *ukev; + + ukev =kmalloc (sizeof (struct ukevent ) * num,GFP_KERNEL );
kernel/kevent/kevent_user.c line 447
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+if (!ukev) +return NULL ; + +if (copy_from_user (arg, ukev,sizeof (struct ukevent ) * num)) { +kfree (ukev); +return NULL ; + } + +return ukev; +}
The copy_fom_user() args are reversed.
This is serious breakage and raises concerns about the amount of testing which has been performed.
AFAICT there is no bounds checking on `num', so the user can force a deliberate multiplication overflow and cause havoc here.
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+static int kevent_user_ctl_modify (struct kevent_user *u,unsigned int num,void __user *arg) +{ +int err =0 , i; +struct ukevent uk; + +mutex_lock (&u->ctl_mutex); + +if (num >KEVENT_MIN_BUFFS_ALLOC ) { +struct ukevent *ukev;
kernel/kevent/kevent_user.c line 467
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ + ukev =kevent_get_user (num, arg); +if (ukev) { +for (i=0 ; i<num; ++i) { +if (kevent_modify (&ukev[i], u)) + ukev[i].ret_flags |=KEVENT_RET_BROKEN ; + ukev[i].ret_flags |=KEVENT_RET_DONE ; + } +if (copy_to_user (arg, ukev, num*sizeof (struct ukevent ))) + err = -EINVAL ;
EFAULT
kernel/kevent/kevent_user.c line 475
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+kfree (ukev); + goto out; + } + } + +for (i=0 ; i<num; ++i) { +if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + err = -EINVAL ;
EFAULT
kernel/kevent/kevent_user.c line 484
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+break ; + } + +if (kevent_modify (&uk, u)) + uk.ret_flags |=KEVENT_RET_BROKEN ; + uk.ret_flags |=KEVENT_RET_DONE ; + +if (copy_to_user (arg, &uk,sizeof (struct ukevent ))) { + err = -EINVAL ;
EFAULT.
kernel/kevent/kevent_user.c line 439
Quoted 2 lines
kernel/kevent/kevent_user.c new file +2
-0+if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + err = -EINVAL ;
EFAULT (all over the place).
kernel/kevent/kevent_user.c line 554
Quoted 11 lines
kernel/kevent/kevent_user.c new file +11
-0+static void kevent_user_enqueue (struct kevent_user *u,struct kevent *k) +{ +unsigned long flags; +unsigned int hash =kevent_user_hash (&k->event); + +spin_lock_irqsave (&u->kevent_lock, flags); +list_add_tail (&k->kevent_entry, &u->kevent_list[hash]); + u->kevent_num++; +kevent_user_get (u); +spin_unlock_irqrestore (&u->kevent_lock, flags); +}
kevent_user_get() can be moved outside the lock?
Quoted 11 lines
kernel/kevent/kevent_user.c new file +11
-0+/* +* Copy all ukevents from userspace, allocate kevent for each one +* and add them into appropriate kevent_storages, +* e.g. sockets, inodes and so on... +* If something goes wrong, all events will be dequeued and +* negative error will be returned. +* On success number of finished events is returned and +* Array of finished events (struct ukevent) will be placed behind +* kevent_user_control structure. User must run through that array and check +* ret_flags field of each ukevent structure to determine if it is fired or failed event. +*/
kernel/kevent/kevent_user.c line 614
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+static int kevent_user_ctl_add (struct kevent_user *u,unsigned int num,void __user *arg) +{ +int err, cerr =0 , knum =0 , rnum =0 , i; +void __user *orig = arg; +struct ukevent uk; + +mutex_lock (&u->ctl_mutex); + + err = -ENFILE ; +if (u->kevent_num + num >=KEVENT_MAX_EVENTS )
Can a malicious user force an arithmetic overflow here?
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ goto out_remove; + +if (num >KEVENT_MIN_BUFFS_ALLOC ) { +struct ukevent *ukev;
kernel/kevent/kevent_user.c line 628
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ + ukev =kevent_get_user (num, arg); +if (ukev) { +for (i=0 ; i<num; ++i) { + err =kevent_user_add_ukevent (&ukev[i], u); +if (err) { +kevent_user_stat_increase_im (u); +if (i != rnum) +memcpy (&ukev[rnum], &ukev[i],sizeof (struct ukevent )); + rnum++;
What's happening here? The games with rnum' and comparing it with i'??
Perhaps these are not the best-chosen identifiers..
Quoted 25 lines
kernel/kevent/kevent_user.c new file +25
-0+/* +* In nonblocking mode it returns as many events as possible, but not more than @max_nr. +* In blocking mode it waits until timeout or if at least @min_nr events are ready. +*/ +static int kevent_user_wait (struct file *file,struct kevent_user *u, +unsigned int min_nr,unsigned int max_nr,unsigned int timeout, +void __user *buf) +{ +struct kevent *k; +int cerr =0 , num =0 ; + +if (!(file->f_flags &O_NONBLOCK )) { +wait_event_interruptible_timeout (u->wait, + u->ready_num >= min_nr,msecs_to_jiffies (timeout)); + } + +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + } + +/* +* If it is one-shot kevent, it has been removed already from
kernel/kevent/kevent_user.c line 706
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+* origin's queue, so we can easily free it here. +*/ +if (k->event.req_flags &KEVENT_REQ_ONESHOT ) +kevent_finish_user (k,1 ); + ++num; +kevent_user_stat_increase_wait (u); + } + +return (cerr)?cerr:num; +}
So if this returns an error, the user doesn't know how many events were
actually completed? That doesn't seem good.
kernel/kevent/kevent_user.c line 796
Quoted 1 line
kernel/kevent/kevent_user.c new file +1
-0+asmlinkage long sys_kevent_ctl (int fd,unsigned int cmd,unsigned int num,void __user *arg)
At some point Michael will want to be writing the manpages for things like this. He'll start out by reading the comment block, poor guy.
Quoted 20 lines
kernel/kevent/kevent_user.c new file +20
-0+{ +int err = -EINVAL ; +struct file *file; + +if (cmd ==KEVENT_CTL_INIT ) +return kevent_ctl_init (); + + file =fget (fd); +if (!file) +return -ENODEV ; + +if (file->f_op != &kevent_user_fops) + goto out_fput; + + err =kevent_ctl_process (file, cmd, num, arg); + +out_fput: +fput (file); +return err; +}
Re: [take6 1/3] kevent: Core files.
Quoted 39 lines · Andrew Morton
Quoted 9 lines · On Wed, 9 Aug 2006 12:02:40 +0400 Evgeniy Polyakov <[email protected]> wrote:
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machinesQuoted 3 lines
It might also inlclude parts from other subsystem (like network related syscalls, so it is possible that it will not compile without other patches applied).
Summary:
- has serious bugs which indicate that much better testing is needed.
- All
-EFOOreturn values need to be reviewed for appropriateness- needs much better commenting before I can do more than a local-level review.
Quoted 14 lines
include/linux/kevent.h new file +10
-0+/* +* Poll events. +*/ +#define KEVENT_POLL_POLLIN 0x0001 +#define KEVENT_POLL_POLLPRI 0x0002 +#define KEVENT_POLL_POLLOUT 0x0004 +#define KEVENT_POLL_POLLERR 0x0008 +#define KEVENT_POLL_POLLHUP 0x0010 +#define KEVENT_POLL_POLLNVAL 0x0020 +
Quoted 8 lines
include/linux/kevent.h line 86
Quoted 6 lines
include/linux/kevent.h new file +6
-0+#define KEVENT_POLL_POLLRDNORM 0x0040 +#define KEVENT_POLL_POLLRDBAND 0x0080 +#define KEVENT_POLL_POLLWRNORM 0x0100 +#define KEVENT_POLL_POLLWRBAND 0x0200 +#define KEVENT_POLL_POLLMSG 0x0400 +#define KEVENT_POLL_POLLREMOVE 0x1000
0x0800got lost.
I will use usual poll definitions.
Quoted 16 lines
include/linux/kevent.h line 113
Quoted 13 lines
include/linux/kevent.h new file +13
-0+struct ukevent +{ +struct kevent_id id;/* Id of this request, e.g. socket number, file descriptor and so on... */ +__u32 type;/* Event type, e.g. KEVENT_SOCK, KEVENT_INODE, KEVENT_TIMER and so on... */ +__u32 event;/* Event itself, e.g. SOCK_ACCEPT, INODE_CREATED, TIMER_FIRED... */ +__u32 req_flags;/* Per-event request flags */ +__u32 ret_flags;/* Per-event return flags */ +__u32 ret_data[2 ];/* Event return data. Event originator fills it with anything it likes. */ +union { +__u32 user[2 ];/* User's data. It is not used, just copied to/from user. */ +void *ptr; + }; +};What is this union for?
Quoted 1 line
`ptr' needs a
__usertag, does it not?
Not, it is never touched by kernel.
Quoted 2 lines
`ptr' will be 64-bit in-kernel and 64-bit for 64-bit userspace, but 32-bit for 32-bit userspace. I guess that's why user[] is there.
Exactly.
Quoted 4 lines
On big-endian machines, this pointer will appear to be word-swapped as far as a 64-bit kernel is concerned. Or something.
IOW: What's going on here??
It is user data - I put there a union just to simplify userspace, so it sould not require some typecasting.
Quoted 25 lines
Quoted 25 lines
include/linux/kevent.h new file +25
-0+#ifdef CONFIG_KEVENT_INODE +void kevent_inode_notify (struct inode *inode,u32 event); +void kevent_inode_notify_parent (struct dentry *dentry,u32 event); +void kevent_inode_remove (struct inode *inode); +#else +static inline void kevent_inode_notify (struct inode *inode,u32 event) +{ +} +static inline void kevent_inode_notify_parent (struct dentry *dentry,u32 event) +{ +} +static inline void kevent_inode_remove (struct inode *inode) +{ +} +#endif /* CONFIG_KEVENT_INODE */ +#ifdef CONFIG_KEVENT_SOCKET +#ifdef CONFIG_LOCKDEP +void kevent_socket_reinit(struct socket *sock); +void kevent_sk_reinit(struct sock *sk); +#else +static inline void kevent_socket_reinit(struct socket *sock) +{ +} +static inline void kevent_sk_reinit(struct sock *sk) +{
Quoted 8 lines
include/linux/kevent.h line 257
Quoted 6 lines
include/linux/kevent.h new file +6
-0+} +#endif +void kevent_socket_notify (struct sock *sock,u32 event); +int kevent_socket_dequeue (struct kevent *k); +int kevent_socket_enqueue (struct kevent *k); +#define sock_async (__sk) sock_flag(__sk, SOCK_ASYNC)Is this header the correct place to be implementing
sock_async()?
I decided to have kevent as much separate as possible, so I put a lot there. When people decide that it is ok, than it can be moved into appropriate network header file - here it is much easier to review.
Quoted 4 lines
Quoted 4 lines
kernel/kevent/Kconfig new file +1
-0@@ -0,0 +1,50 @@ +config KEVENT
Quoted 8 lines
kernel/kevent/Kconfig line 6
Quoted 5 lines
kernel/kevent/Kconfig new file +5
-0+ bool "Kernel event notification mechanism" + help + This option enables event queue mechanism. + It can be used as replacement for poll()/select(), AIO callback invocations, + advanced timer notifications and other kernel object status changes.Please squeeze all the help text into 80-columns. Or at least check that it looks OK in menuconfig in an 80-col xterm,
Ok.
Quoted 40 lines
Quoted 40 lines
kernel/kevent/kevent.c new file +37
-0@@ -0,0 +1,238 @@ +/* +* kevent.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/mempool.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/kevent.h> + +kmem_cache_t *kevent_cache; + +/* +* Attempts to add an event into appropriate origin's queue. +* Returns positive value if this event is ready immediately, +* negative value in case of error and zero if event has been queued.
Quoted 8 lines
kernel/kevent/kevent.c line 43
Quoted 6 lines
kernel/kevent/kevent.c new file +6
-0+* ->enqueue() callback must increase origin's reference counter. +*/ +int kevent_enqueue (struct kevent *k) +{ +if (k->event.type >=KEVENT_MAX ) +return -E2BIG ;E2BIG is "Argument list too long". EINVAL is appropriate here.
No problem.
Quoted 44 lines
Quoted 44 lines
kernel/kevent/kevent.c new file +44
-0+if (!k->callbacks.enqueue) { +kevent_break (k); +return -EINVAL ; + } + +return k->callbacks.enqueue (k); +} + +/* +* Remove event from the appropriate queue. +* ->dequeue() callback must decrease origin's reference counter. +*/ +int kevent_dequeue (struct kevent *k) +{ +if (k->event.type >=KEVENT_MAX ) +return -E2BIG ; + +if (!k->callbacks.dequeue) { +kevent_break (k); +return -EINVAL ; + } + +return k->callbacks.dequeue (k); +} + +int kevent_break (struct kevent *k) +{ +unsigned long flags; + +spin_lock_irqsave (&k->ulock, flags); + k->event.ret_flags |=KEVENT_RET_BROKEN ; +spin_unlock_irqrestore (&k->ulock, flags); +return 0 ; +} + +struct kevent_callbacks kevent_registered_callbacks[KEVENT_MAX ]; + +/* +* Must be called before event is going to be added into some origin's queue. +* Initializes ->enqueue(), ->dequeue() and ->callback() callbacks. +* If failed, kevent should not be used or kevent_enqueue() will fail to add +* this kevent into origin's queue with setting +* KEVENT_RET_BROKEN flag in kevent->event.ret_flags. +*/
Quoted 8 lines
kernel/kevent/kevent.c line 94
Quoted 6 lines
kernel/kevent/kevent.c new file +6
-0+int kevent_init (struct kevent *k) +{ +spin_lock_init (&k->ulock); + k->kevent_entry.next =LIST_POISON1 ; + k->storage_entry.prev =LIST_POISON2 ; + k->ready_entry.next =LIST_POISON1 ;Nope ;)
I use pointer checks to determine if entry is in the list or not, why it is frowned upon here?
Please do not say about poisoning which takes a lot of cpu cycles to get new cachelines and so on - everything in that entry is in the cache, since entry was added/deleted/accessed through list walk macro.
Quoted 21 lines
Quoted 21 lines
kernel/kevent/kevent.c new file +21
-0+if (k->event.type >=KEVENT_MAX ) +return -E2BIG ; + + k->callbacks = kevent_registered_callbacks[k->event.type]; +if (!k->callbacks.callback) { +kevent_break (k); +return -EINVAL ; + } + +return 0 ; +} + +/* +* Called from ->enqueue() callback when reference counter for given +* origin (socket, inode...) has been increased. +*/ +int kevent_storage_enqueue (struct kevent_storage *st,struct kevent *k) +{ +unsigned long flags; + + k->st = st;
Quoted 8 lines
kernel/kevent/kevent.c line 122
Quoted 6 lines
kernel/kevent/kevent.c new file +6
-0+spin_lock_irqsave (&st->lock, flags); +list_add_tail_rcu (&k->storage_entry, &st->list); + st->qlen++; +spin_unlock_irqrestore (&st->lock, flags); +return 0 ; +}Is the
_rcuvariant needed here?
Yes, storage list is protected by RCU.st->lock is used to remove race between several "writers".
Quoted 6 lines
Quoted 6 lines
kernel/kevent/kevent.c new file +6
-0+/* +* Dequeue kevent from origin's queue. +* It does not decrease origin's reference counter in any way +* and must be called before it, so storage itself must be valid. +* It is called from ->dequeue() callback. +*/
Quoted 8 lines
kernel/kevent/kevent.c line 135
Quoted 6 lines
kernel/kevent/kevent.c new file +6
-0+void kevent_storage_dequeue (struct kevent_storage *st,struct kevent *k) +{ +unsigned long flags; + +spin_lock_irqsave (&st->lock, flags); +if (k->storage_entry.prev !=LIST_POISON2 ) {Nope, as discussed earlier.
Sorry, but I do not agree, that lsit poisoning costs something and I explained why. It can be wrong from some arcitechtural point of view, but I can crate a macros and place them into list.h which will do just the same.
The problem is how to determine if entry is in the list or not regardless kevent code.
Quoted 27 lines
Quoted 14 lines
kernel/kevent/kevent.c new file +14
-0+list_del_rcu (&k->storage_entry); + st->qlen--; + } +spin_unlock_irqrestore (&st->lock, flags); +} + +static void __kevent_requeue (struct kevent *k,u32 event) +{ +int err, rem =0 ; +unsigned long flags; + + err = k->callbacks.callback (k); + +spin_lock_irqsave (&k->ulock, flags);kernel/kevent/kevent.c line 159
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+if (err >0 ) { + k->event.ret_flags |=KEVENT_RET_DONE ; + }else if (err <0 ) { + k->event.ret_flags |=KEVENT_RET_BROKEN ; + k->event.ret_flags |=KEVENT_RET_DONE ; + } + rem = (k->event.req_flags &KEVENT_REQ_ONESHOT ); +if (!err) + err = (k->event.ret_flags & (KEVENT_RET_BROKEN |KEVENT_RET_DONE )); +spin_unlock_irqrestore (&k->ulock, flags);Local variable `err' no longer actually indicates an error, does it?
Quoted 1 line
If not, a differently-named local would be appropriate here.
Ok, I will rename it.
Quoted 6 lines
kernel/kevent/kevent.c line 164
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+if (err) { +if ((rem || err <0 ) && k->storage_entry.prev !=LIST_POISON2 ) { +list_del_rcu (&k->storage_entry); + k->st->qlen--;->qlen was previously modified under spinlock. Here it is not.
It is tricky part - this part of the code can not be reentered (by design of all storages which are used with kevents), and different CPUs can not access the same list due to RCU rules - i.e. it is perfectly ok, if different CPU sees old value of the queue length.
Quoted 37 lines
Quoted 24 lines
kernel/kevent/kevent.c new file +24
-0+ } + +spin_lock_irqsave (&k->user->ready_lock, flags); +if (k->ready_entry.next ==LIST_POISON1 ) { +kevent_user_ring_add_event (k); +list_add_tail (&k->ready_entry, &k->user->ready_list); + k->user->ready_num++; + } +spin_unlock_irqrestore (&k->user->ready_lock, flags); +wake_up (&k->user->wait); + } +} + +void kevent_requeue (struct kevent *k) +{ +unsigned long flags; + +spin_lock_irqsave (&k->st->lock, flags); +__kevent_requeue (k,0 ); +spin_unlock_irqrestore (&k->st->lock, flags); +} + +/* +* Called each time some activity in origin (socket, inode...) is noticed. kernel/kevent/kevent.c line 198
Quoted 10 lines
kernel/kevent/kevent.c new file +10
-0+*/ +void kevent_storage_ready (struct kevent_storage *st, +kevent_callback_t ready_callback,u32 event) +{ +struct kevent *k; + +rcu_read_lock (); +list_for_each_entry_rcu (k, &st->list, storage_entry) { +if (ready_callback) +ready_callback (k);For readability reasons I prefer the old-style
Quoted 6 lines
(*ready_callback)(k);so the reader knows not to go off hunting for the function "
ready_callback". Minor point.So the
kevent_callback_thandlers are not allowed to sleep.
No, since they are called from internals of state machines of the origins - it is either softirqs (network) or hard irqs (block layer).
Quoted 5 lines
kernel/kevent/kevent.c line 202
Quoted 3 lines
kernel/kevent/kevent.c new file +3
-0+if (event & k->event.event) +__kevent_requeue (k, event); + }Under what circumstances will `event' be zero??
It is a bit AND, requeing happens only when requested event matches at least one produced event.
Quoted 19 lines
Quoted 19 lines
kernel/kevent/kevent.c new file +19
-0+rcu_read_unlock (); +} + +int kevent_storage_init (void *origin,struct kevent_storage *st) +{ +spin_lock_init (&st->lock); + st->origin = origin; + st->qlen =0 ; +INIT_LIST_HEAD (&st->list); +return 0 ; +} + +void kevent_storage_fini (struct kevent_storage *st) +{ +kevent_storage_ready (st, kevent_break,KEVENT_MASK_ALL ); +} + +static int __init kevent_sys_init (void ) +{
Quoted 8 lines
kernel/kevent/kevent.c line 227
Quoted 6 lines
kernel/kevent/kevent.c new file +6
-0+int i; + + kevent_cache =kmem_cache_create ("kevent_cache" , +sizeof (struct kevent ),0 ,0 ,NULL ,NULL ); +if (!kevent_cache) +panic ("kevent: Unable to create a cache.\n" );Can use
SLAB_PANIC(a silly thing I added to avoid code duplication).
Ok.
Quoted 7 lines
kernel/kevent/kevent.c line 233
Quoted 5 lines
kernel/kevent/kevent.c new file +5
-0+for (i=0 ; i<ARRAY_SIZE (kevent_registered_callbacks); ++i) { +struct kevent_callbacks *c = &kevent_registered_callbacks[i]; + + c->callback = c->enqueue = c->dequeue =NULL ; + }This zeroing is redundant.
It is not static, I you sure it will be zeroed?
Quoted 6 lines
kernel/kevent/kevent.c line 238
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+return 0 ; +} + +late_initcall (kevent_sys_init);Why is it
late_initcall? (A comment is needed)
Why not?
It can be any initcall or __init.
Quoted 94 lines
Quoted 60 lines
kernel/kevent/kevent_user.c new file +54
-0@@ -0,0 +1,857 @@ +/* +* kevent_user.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/mount.h> +#include <linux/device.h> +#include <linux/poll.h> +#include <linux/kevent.h> +#include <linux/jhash.h> +#include <linux/miscdevice.h> +#include <asm/io.h> + +static char kevent_name[] ="kevent" ; + +static int kevent_user_open (struct inode *,struct file *); +static int kevent_user_release (struct inode *,struct file *); +static unsigned int kevent_user_poll (struct file *,struct poll_table_struct *); +static int kevnet_user_mmap (struct file *,struct vm_area_struct *); + +static struct file_operations kevent_user_fops = { + .mmap = kevnet_user_mmap, + .open = kevent_user_open, + .release = kevent_user_release, + .poll = kevent_user_poll, + .owner =THIS_MODULE , +}; + +static struct miscdevice kevent_miscdev = { + .minor =MISC_DYNAMIC_MINOR ,kernel/kevent/kevent_user.c line 64
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ .name = kevent_name, + .fops = &kevent_user_fops, +}; + +static int kevent_get_sb (struct file_system_type *fs_type, +int flags,const char *dev_name,void *data,struct vfsmount *mnt) +{ +/* So original magic... */ +return get_sb_pseudo (fs_type, kevent_name,NULL ,0xabcdef , mnt); +}That doesn't look like a well-chosen magic number...
Quoted 21 lines
kernel/kevent/kevent_user.c new file +21
-0+static struct file_system_type kevent_fs_type = { + .name = kevent_name, + .get_sb = kevent_get_sb, + .kill_sb = kill_anon_super, +}; + +static struct vfsmount *kevent_mnt; + +static unsigned int kevent_user_poll (struct file *file,struct poll_table_struct *wait) +{ +struct kevent_user *u = file->private_data; +unsigned int mask; + +poll_wait (file, &u->wait, wait); + mask =0 ; + +if (u->ready_num) + mask |=POLLIN |POLLRDNORM ; + +return mask; +}
Quoted 8 lines
kernel/kevent/kevent_user.c line 92
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+ +static inline void kevent_user_ring_set (struct kevent_user *u,unsigned int num) +{ +unsigned int *idx; + + idx = (unsigned int *)u->pring[0 ];This is a bit ugly.
I specially use first 4 bytes in the first page to store index there, since it must be accessed from userspace and kernelspace.
Quoted 21 lines
kernel/kevent/kevent_user.c line 101
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+ idx[0 ] = num; +} + +/* +* Note that kevents does not exactly fill the page (each ukevent is 40 bytes), +* so we reuse 4 bytes at the begining of the first page to store index. +* Take that into account if you want to change size of struct ukevent. +*/ +#define KEVENTS_ON_PAGE (PAGE_SIZE/sizeof(struct ukevent))How about doing
struct ukevent_ring { unsigned int index; struct ukevent[0]; }and removing all those nasty typeasting and offsetting games?
In fact you can even do
Quoted 7 lines
struct ukevent_ring { struct ukevent[(PAGE_SIZE - sizeof(unsigned int)) / sizeof(struct ukevent)]; unsigned int index; };if you're careful ;)
Ring takes more than one page, so it will be struct ukevent_ring_0 and struct ukevent_ring_other. Does it really needed?
Not a big problem, if you do thing it worse it.
Quoted 38 lines
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+/* +* Called under kevent_user->ready_lock, so updates are always protected. +*/ +void kevent_user_ring_add_event (struct kevent *k) +{ +unsigned int *idx_ptr, idx, pidx, off; +struct ukevent *ukev; +kernel/kevent/kevent_user.c line 120
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ idx_ptr = (unsigned int *)k->user->pring[0 ]; + idx = idx_ptr[0 ]; + + pidx = idx/KEVENTS_ON_PAGE ; + off = idx%KEVENTS_ON_PAGE ; + +if (pidx ==0 ) + ukev = (struct ukevent *)(k->user->pring[pidx] +sizeof (unsigned int )); +else + ukev = (struct ukevent *)(k->user->pring[pidx]);Such as these.
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+memcpy (&ukev[off], &k->event,sizeof (struct ukevent )); + + idx++; +if (idx >=KEVENT_MAX_EVENTS )kernel/kevent/kevent_user.c line 135
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ idx =0 ; + + idx_ptr[0 ] = idx; +} + +static int kevent_user_ring_init (struct kevent_user *u) +{ +int i, pnum; + + pnum =ALIGN (KEVENT_MAX_EVENTS *sizeof (struct ukevent ) +sizeof (unsigned int ),PAGE_SIZE )/PAGE_SIZE ;And these.
Quoted 9 lines
kernel/kevent/kevent_user.c line 143
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+ u->pring =kmalloc (pnum *sizeof (unsigned long ),GFP_KERNEL ); +if (!u->pring) +return -ENOMEM ; + +for (i=0 ; i<pnum; ++i) { + u->pring[i] =__get_free_page (GFP_KERNEL ); +if (!u->pring)bug: this is testing the wrong thing.
HOw come?__get_free_page() can return 0 if page was not allocated.
Quoted 9 lines
kernel/kevent/kevent_user.c line 150
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+break ; + } + +if (i != pnum) { + pnum = i; + goto err_out_free; + }Move this logic into the `if (!u->pring)' logic, above.
Ok.
Quoted 40 lines
Quoted 40 lines
kernel/kevent/kevent_user.c new file +40
-0+kevent_user_ring_set (u,0 ); + +return 0 ; + +err_out_free: +for (i=0 ; i<pnum; ++i) +free_page (u->pring[i]); + +kfree (u->pring); + +return -ENOMEM ; +} + +static void kevent_user_ring_fini (struct kevent_user *u) +{ +int i, pnum; + + pnum =ALIGN (KEVENT_MAX_EVENTS *sizeof (struct ukevent ) +sizeof (unsigned int ),PAGE_SIZE )/PAGE_SIZE ; + +for (i=0 ; i<pnum; ++i) +free_page (u->pring[i]); + +kfree (u->pring); +} + +static struct kevent_user *kevent_user_alloc (void ) +{ +struct kevent_user *u; +int i; + + u =kzalloc (sizeof (struct kevent_user ),GFP_KERNEL ); +if (!u) +return NULL ; + +INIT_LIST_HEAD (&u->ready_list); +spin_lock_init (&u->ready_lock); + u->ready_num =0 ; +kevent_user_stat_init (u); +spin_lock_init (&u->kevent_lock); +for (i=0 ; i<ARRAY_SIZE (u->kevent_list); ++i)
Quoted 8 lines
kernel/kevent/kevent_user.c line 197
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+INIT_LIST_HEAD (&u->kevent_list[i]); + u->kevent_num =0 ; + +mutex_init (&u->ctl_mutex); +init_waitqueue_head (&u->wait); + u->max_ready_num =0 ;The above zeroes out a bunch of known-to-already-be-zero things.
Ok, I will remove redundant settings.
Quoted 6 lines
kernel/kevent/kevent_user.c line 235
Quoted 1 line
kernel/kevent/kevent_user.c new file +1
-0+static int kevnet_user_mmap (struct file *file,struct vm_area_struct *vma)The function name is mistyped.
This code doesn't have many comments, does it? What are we mapping here, and why would an application want to map it?
That code waits comments from people who requested it.
It is ring of the ready events, which can be read by userspace instead of calling syscall, so syscall just becomes "wait until there is a place" or something like that.
Quoted 4 lines
And what are the portability implications? Does userspace need to know the 64-bitness of its kernel to be able to work out the alignment of things? If so, what happens if a later/different compiler aligns things differently?
There are no alignment issues - I use 32bit values anywhere.
Quoted 28 lines
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+{ +size_t size = vma->vm_end - vma->vm_start, psize; +int pnum = size/PAGE_SIZE , i; +unsigned long start = vma->vm_start; +struct kevent_user *u = file->private_data; + + psize =ALIGN (KEVENT_MAX_EVENTS *sizeof (struct ukevent ) +sizeof (unsigned int ),PAGE_SIZE ); + +if (size + vma->vm_pgoff*PAGE_SIZE != psize) +return -EINVAL ; + +if (vma->vm_flags &VM_WRITE ) +return -EPERM ; + + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot);kernel/kevent/kevent_user.c line 260
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ +for (i=0 ; i<pnum; ++i) { +if (remap_pfn_range (vma, start,virt_to_phys ((void *)u->pring[i+vma->vm_pgoff]),PAGE_SIZE , + vma->vm_page_prot)) +return -EAGAIN ; + start +=PAGE_SIZE ; + } + +return 0 ; +}Is EAGAIN an appropriate return value?
Quoted 1 line
If this function had a decent comment we could ask Hugh to review it.
It is trivial ->mmap() implementation - ring buffer, which contains of several pages (allocated through __get_free_page()) is mapped into userspace.vm_pgoff shows offset inside that ring.
Quoted 28 lines
Quoted 28 lines
kernel/kevent/kevent_user.c new file +28
-0+#if 0 +static inline unsigned int kevent_user_hash (struct ukevent *uk) +{ +unsigned int h = (uk->user[0 ] ^ uk->user[1 ]) ^ (uk->id.raw[0 ] ^ uk->id.raw[1 ]); + + h = (((h >>16 ) &0xffff ) ^ (h &0xffff )) &0xffff ; + h = (((h >>8 ) &0xff ) ^ (h &0xff )) &KEVENT_HASH_MASK ; + +return h; +} +#else +static inline unsigned int kevent_user_hash (struct ukevent *uk) +{ +return jhash_1word (uk->id.raw[0 ],0 ) &KEVENT_HASH_MASK ; +} +#endif + +static void kevent_free_rcu (struct rcu_head *rcu) +{ + struct kevent *kevent = container_of(rcu, struct kevent, rcu_head); +kmem_cache_free (kevent_cache, kevent); +} + +static void kevent_finish_user_complete (struct kevent *k,int deq) +{ +struct kevent_user *u = k->user; +unsigned long flags; +
Quoted 8 lines
kernel/kevent/kevent_user.c line 295
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+if (deq) +kevent_dequeue (k); + +spin_lock_irqsave (&u->ready_lock, flags); +if (k->ready_entry.next !=LIST_POISON1 ) { +list_del (&k->ready_entry);
list_del_rcu()?
No, ready list does not need RCU protection.
Quoted 10 lines
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ u->ready_num--; + } +spin_unlock_irqrestore (&u->ready_lock, flags); + +kevent_user_put (u); +call_rcu (&k->rcu_head, kevent_free_rcu); +} + +static void __kevent_finish_user (struct kevent *k,int deq) +{
Quoted 8 lines
kernel/kevent/kevent_user.c line 311
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+struct kevent_user *u = k->user; + +list_del (&k->kevent_entry); + u->kevent_num--; +kevent_finish_user_complete (k, deq); +}No locking needed?
It is special function which is called without lock, function without __ prefix holds appropriate lock.
Quoted 2 lines
It's hard to review uncommented code. And the review is less useful if the reviewer cannot determine what the developer was attempting to do.
Comment is 5 lines below, where that function is called wrapped with appropriate lock.
Quoted 7 lines
kernel/kevent/kevent_user.c line 317
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+/* +* Remove kevent from user's list of all events, +* dequeue it from storage and decrease user's reference counter, +* since this kevent does not exist anymore. That is why it is freed here. +*/ That's nice.
Here it is.
Quoted 9 lines
kernel/kevent/kevent_user.c line 324
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+static void kevent_finish_user (struct kevent *k,int deq) +{ +struct kevent_user *u = k->user; +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); +list_del (&k->kevent_entry);
list_del_rcu()?
No, this list does not require RCU protection, only storage_list (storage_entry) requires that.
Quoted 48 lines
Quoted 35 lines
kernel/kevent/kevent_user.c new file +35
-0+ u->kevent_num--; +spin_unlock_irqrestore (&u->kevent_lock, flags); +kevent_finish_user_complete (k, deq); +} + +/* +* Dequeue one entry from user's ready queue. +*/ + +static struct kevent *kqueue_dequeue_ready (struct kevent_user *u) +{ +unsigned long flags; +struct kevent *k =NULL ; + +spin_lock_irqsave (&u->ready_lock, flags); +if (u->ready_num && !list_empty (&u->ready_list)) { + k = list_entry(u->ready_list.next, struct kevent, ready_entry); +list_del (&k->ready_entry); + u->ready_num--; + } +spin_unlock_irqrestore (&u->ready_lock, flags); + +return k; +} + +static struct kevent *__kevent_search (struct list_head *head,struct ukevent *uk, +struct kevent_user *u) +{ +struct kevent *k; +int found =0 ; + +list_for_each_entry (k, head, kevent_entry) { +spin_lock (&k->ulock); + if (k->event.user[0] == uk->user[0] && k->event.user[1] == uk->user[1] && + k->event.id.raw[0] == uk->id.raw[0] &&kernel/kevent/kevent_user.c line 369
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ k->event.id.raw[1] == uk->id.raw[1]) { + found =1 ; +spin_unlock (&k->ulock); +break ; + } +spin_unlock (&k->ulock); + } + +return (found)?k:NULL ; +}Remove `found', do
Quoted 7 lines
struct kevent *ret = NULL; ... ret = k; break; ... return ret;
Ok.
Quoted 3 lines
kernel/kevent/kevent_user.c line 371
Quoted 1 line
kernel/kevent/kevent_user.c new file +1
-0+static int kevent_modify (struct ukevent *uk,struct kevent_user *u)<wonders what this function does>
Let me guess... It modifies kevent? :)
I will add comments.
Quoted 15 lines
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+{ +struct kevent *k; +unsigned int hash =kevent_user_hash (uk); +int err = -ENODEV ; +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); + k =__kevent_search (&u->kevent_list[hash], uk, u); +if (k) { +spin_lock (&k->ulock); + k->event.event = uk->event; + k->event.req_flags = uk->req_flags; + k->event.ret_flags =0 ; +spin_unlock (&k->ulock); +kevent_requeue (k);
Quoted 8 lines
kernel/kevent/kevent_user.c line 392
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+ err =0 ; + } +spin_unlock_irqrestore (&u->kevent_lock, flags); + +return err; +}ENODEV: "No such device". Doesn't sound appropriate.
ENOKEVENT? I expect ENODEV means "there is no requested thing".
Quoted 23 lines
Quoted 23 lines
kernel/kevent/kevent_user.c new file +23
-0+static int kevent_remove (struct ukevent *uk,struct kevent_user *u) +{ +int err = -ENODEV ; +struct kevent *k; +unsigned int hash =kevent_user_hash (uk); +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); + k =__kevent_search (&u->kevent_list[hash], uk, u); +if (k) { +__kevent_finish_user (k,1 ); + err =0 ; + } +spin_unlock_irqrestore (&u->kevent_lock, flags); + +return err; +} + +/* +* No new entry can be added or removed from any list at this point. +* It is not permitted to call ->ioctl() and ->release() in parallel. +*/ +static int kevent_user_release (struct inode *inode,struct file *file)
Quoted 8 lines
kernel/kevent/kevent_user.c line 422
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+{ +struct kevent_user *u = file->private_data; +struct kevent *k, *n; +int i; + +for (i=0 ; i<KEVENT_HASH_MASK +1 ; ++i) {
ARRAY_SIZE
Ok.
Quoted 28 lines
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+list_for_each_entry_safe (k, n, &u->kevent_list[i], kevent_entry) +kevent_finish_user (k,1 ); + } + +kevent_user_put (u); + file->private_data =NULL ; + +return 0 ; +} + +static struct ukevent *kevent_get_user (unsigned int num,void __user *arg) +{ +struct ukevent *ukev; + + ukev =kmalloc (sizeof (struct ukevent ) * num,GFP_KERNEL );kernel/kevent/kevent_user.c line 447
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+if (!ukev) +return NULL ; + +if (copy_from_user (arg, ukev,sizeof (struct ukevent ) * num)) { +kfree (ukev); +return NULL ; + } + +return ukev; +}The
copy_fom_user()args are reversed.
Quoted 2 lines
This is serious breakage and raises concerns about the amount of testing which has been performed.
It is typo in the new code, which was added by request in this thread.
Quoted 2 lines
AFAICT there is no bounds checking on `num', so the user can force a deliberate multiplication overflow and cause havoc here.
It is checked when it is added into the ring, if kevent was not added and is going to be removed or modified, it will be just thrown with appropriate return code.
It should be checked against u->kevent_num here.
Quoted 13 lines
Quoted 13 lines
kernel/kevent/kevent_user.c new file +13
-0+static int kevent_user_ctl_modify (struct kevent_user *u,unsigned int num,void __user *arg) +{ +int err =0 , i; +struct ukevent uk; + +mutex_lock (&u->ctl_mutex); + +if (num >KEVENT_MIN_BUFFS_ALLOC ) { +struct ukevent *ukev; + + ukev =kevent_get_user (num, arg); +if (ukev) { +for (i=0 ; i<num; ++i) {
Quoted 8 lines
kernel/kevent/kevent_user.c line 467
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+if (kevent_modify (&ukev[i], u)) + ukev[i].ret_flags |=KEVENT_RET_BROKEN ; + ukev[i].ret_flags |=KEVENT_RET_DONE ; + } +if (copy_to_user (arg, ukev, num*sizeof (struct ukevent ))) + err = -EINVAL ;EFAULT
Ok.
Quoted 23 lines
kernel/kevent/kevent_user.c line 475
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+kfree (ukev); + goto out; + } + } + +for (i=0 ; i<num; ++i) { +if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + err = -EINVAL ;EFAULT
kernel/kevent/kevent_user.c line 484
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+break ; + } + +if (kevent_modify (&uk, u)) + uk.ret_flags |=KEVENT_RET_BROKEN ; + uk.ret_flags |=KEVENT_RET_DONE ; + +if (copy_to_user (arg, &uk,sizeof (struct ukevent ))) { + err = -EINVAL ;EFAULT.
Quoted 4 lines
kernel/kevent/kevent_user.c line 439
Quoted 2 lines
kernel/kevent/kevent_user.c new file +2
-0+if (copy_from_user (&uk, arg,sizeof (struct ukevent ))) { + err = -EINVAL ;EFAULT (all over the place).
Ok, I will return EFAULT when copy*user fails.
Quoted 5 lines
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+static void kevent_user_enqueue (struct kevent_user *u,struct kevent *k) +{ +unsigned long flags; +unsigned int hash =kevent_user_hash (&k->event); +
Quoted 8 lines
kernel/kevent/kevent_user.c line 554
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+spin_lock_irqsave (&u->kevent_lock, flags); +list_add_tail (&k->kevent_entry, &u->kevent_list[hash]); + u->kevent_num++; +kevent_user_get (u); +spin_unlock_irqrestore (&u->kevent_lock, flags); +}
kevent_user_get()can be moved outside the lock?
Yes.
Quoted 15 lines
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+/* +* Copy all ukevents from userspace, allocate kevent for each one +* and add them into appropriate kevent_storages, +* e.g. sockets, inodes and so on... +* If something goes wrong, all events will be dequeued and +* negative error will be returned. +* On success number of finished events is returned and +* Array of finished events (struct ukevent) will be placed behind +* kevent_user_control structure. User must run through that array and check +* ret_flags field of each ukevent structure to determine if it is fired or failed event. +*/ +static int kevent_user_ctl_add (struct kevent_user *u,unsigned int num,void __user *arg) +{ +int err, cerr =0 , knum =0 , rnum =0 , i; +void __user *orig = arg;
Quoted 8 lines
kernel/kevent/kevent_user.c line 614
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+struct ukevent uk; + +mutex_lock (&u->ctl_mutex); + + err = -ENFILE ; +if (u->kevent_num + num >=KEVENT_MAX_EVENTS )Can a malicious user force an arithmetic overflow here?
All numbers here are unsigned and are compared against 4096. So, answer is no.
Quoted 17 lines
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ goto out_remove; + +if (num >KEVENT_MIN_BUFFS_ALLOC ) { +struct ukevent *ukev;kernel/kevent/kevent_user.c line 628
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ + ukev =kevent_get_user (num, arg); +if (ukev) { +for (i=0 ; i<num; ++i) { + err =kevent_user_add_ukevent (&ukev[i], u); +if (err) { +kevent_user_stat_increase_im (u); +if (i != rnum) +memcpy (&ukev[rnum], &ukev[i],sizeof (struct ukevent )); + rnum++;What's happening here? The games with
rnum' and comparing it withi'??
Quoted 1 line
Perhaps these are not the best-chosen identifiers..
When kevent is ready immediately it is copied into the same buffer into previous (rnum ready num) position. kevent at "rpos" was not ready immediately (otherwise it would be copied and rnum increased) and thus it is copied into the queue and can be overwritten here.
Quoted 30 lines
Quoted 30 lines
kernel/kevent/kevent_user.c new file +30
-0+/* +* In nonblocking mode it returns as many events as possible, but not more than @max_nr. +* In blocking mode it waits until timeout or if at least @min_nr events are ready. +*/ +static int kevent_user_wait (struct file *file,struct kevent_user *u, +unsigned int min_nr,unsigned int max_nr,unsigned int timeout, +void __user *buf) +{ +struct kevent *k; +int cerr =0 , num =0 ; + +if (!(file->f_flags &O_NONBLOCK )) { +wait_event_interruptible_timeout (u->wait, + u->ready_num >= min_nr,msecs_to_jiffies (timeout)); + } + +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + } + +/* +* If it is one-shot kevent, it has been removed already from +* origin's queue, so we can easily free it here. +*/ +if (k->event.req_flags &KEVENT_REQ_ONESHOT ) +kevent_finish_user (k,1 ); + ++num;
Quoted 8 lines
kernel/kevent/kevent_user.c line 706
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+kevent_user_stat_increase_wait (u); + } + +return (cerr)?cerr:num; +}So if this returns an error, the user doesn't know how many events were
actually completed? That doesn't seem good.
What is the alternative?read() work the same way - either error or number of bytes read.
Quoted 4 lines
kernel/kevent/kevent_user.c line 796
Quoted 1 line
kernel/kevent/kevent_user.c new file +1
-0+asmlinkage long sys_kevent_ctl (int fd,unsigned int cmd,unsigned int num,void __user *arg)At some point Michael will want to be writing the manpages for things like this. He'll start out by reading the comment block, poor guy.
I will add comments.
Quoted 12 lines
Quoted 12 lines
kernel/kevent/kevent_user.c new file +12
-0+{ +int err = -EINVAL ; +struct file *file; + +if (cmd ==KEVENT_CTL_INIT ) +return kevent_ctl_init (); + + file =fget (fd); +if (!file) +return -ENODEV ; + +if (file->f_op != &kevent_user_fops)
Quoted 8 lines
kernel/kevent/kevent_user.c line 816
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+ goto out_fput; + + err =kevent_ctl_process (file, cmd, num, arg); + +out_fput: +fput (file); +return err; +}
So let me quote your first words about kevent:
Quoted 7 lines
Summary:
- has serious bugs which indicate that much better testing is needed.
- All
-EFOOreturn values need to be reviewed for appropriateness- needs much better commenting before I can do more than a local-level review.
As far as I can see there are no serious bugs except absence of two checks for and typo in function order, which abviously will be fixed. All EFOO will be changed according to comments and better comments will be added.
Thank you for review, Andrew.
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take6 1/3] kevent: Core files.
From: Evgeniy Polyakov <[email protected]>
Date: Thu, 10 Aug 2006 10:14:33 +0400
Quoted 8 lines · Andrew Morton
Quoted 4 lines
On big-endian machines, this pointer will appear to be word-swapped as far as a 64-bit kernel is concerned. Or something.
IOW: What's going on here??
It is user data - I put there a union just to simplify userspace, so it sould not require some typecasting.
And this is consistent with similar mechianism we use for netlink socket dumping, so that we don't have compat layer crap just because we provide a place for the user to store his pointer or whatever there.
Quoted 8 lines
Quoted 5 lines
kernel/kevent/kevent.c line 94
Quoted 3 lines
kernel/kevent/kevent.c new file +3
-0+ k->kevent_entry.next =LIST_POISON1 ; + k->storage_entry.prev =LIST_POISON2 ; + k->ready_entry.next =LIST_POISON1 ;Nope ;)
I use pointer checks to determine if entry is in the list or not, why it is frowned upon here?
As Andrew mentioned in another posting, these poison macros are likely to simply go away some day, so you should not use them.
If you want pointer encoded tags you use internally, define your own.
Re: [take6 1/3] kevent: Core files.
Quoted 6 lines · David Miller
Quoted 6 lines
Quoted 5 lines
kernel/kevent/kevent.c line 94
Quoted 3 lines
kernel/kevent/kevent.c new file +3
-0+ k->kevent_entry.next =LIST_POISON1 ; + k->storage_entry.prev =LIST_POISON2 ; + k->ready_entry.next =LIST_POISON1 ;Nope ;)
Quoted 6 lines
Quoted 2 lines
I use pointer checks to determine if entry is in the list or not, why it is frowned upon here?
As Andrew mentioned in another posting, these poison macros are likely to simply go away some day, so you should not use them.
They exist for ages and sudently can go away?..
Quoted 1 line
If you want pointer encoded tags you use internally, define your own.
I think if I will add code like thislist_del(&k->entry);k->entry.prev = KEVENT_POISON1;k->entry.next = KEVENT_POISON2;
I will be suggested to make myself a lobotomy.
I have enough space in flags in each kevent, so I will use some bits there.
Signature · 1 line
Evgeniy Polyakov
Re: [take6 1/3] kevent: Core files.
In reply to Evgeniy PolyakovQuoted 11 lines · On Thu, 10 Aug 2006 10:14:33 +0400 Evgeniy Polyakov <[email protected]> wrote:
Quoted 9 lines
include/linux/kevent.h line 113
Quoted 5 lines
include/linux/kevent.h new file +5
-0+union { +__u32 user[2 ];/* User's data. It is not used, just copied to/from user. */ +void *ptr; + }; +};What is this union for?
`ptr' needs a
__usertag, does it not?Not, it is never touched by kernel.
hrm, if you say so.
Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
+/*
+ * Must be called before event is going to be added into some origin's queue.
+ * Initializes ->enqueue(), ->dequeue() and ->callback() callbacks.
+ * If failed, kevent should not be used orkevent_enqueue()will fail to add
+ * this kevent into origin's queue with setting
+ *KEVENT_RET_BROKENflag inkevent->event.ret_flags.
+ */Quoted 8 lines
kernel/kevent/kevent.c line 94
Quoted 6 lines
kernel/kevent/kevent.c new file +6
-0+int kevent_init (struct kevent *k) +{ +spin_lock_init (&k->ulock); + k->kevent_entry.next =LIST_POISON1 ; + k->storage_entry.prev =LIST_POISON2 ; + k->ready_entry.next =LIST_POISON1 ;Nope ;)
Quoted 5 lines
I use pointer checks to determine if entry is in the list or not, why it is frowned upon here?
Please do not say about poisoning which takes a lot of cpu cycles to get new cachelines and so on - everything in that entry is in the cache, since entry was added/deleted/accessed through list walk macro.
"poisoning which takes a lot of cpu cycles". So there ;)
I assure you, that poisoning code might disappear at any time.
If you want to be able to determine whether a list_head has been detached you can detach it with list_del_init() and then use list_empty() on it.
Quoted 7 lines
Quoted 5 lines
kernel/kevent/kevent.c line 238
Quoted 3 lines
kernel/kevent/kevent.c new file +3
-0+} + +late_initcall (kevent_sys_init);Why is it
late_initcall? (A comment is needed)Why not?
Why?
There must have been some reason for having made this a late_initcall() and that reason is 100% concealed from the reader of this code.
IOW, it needs a comment.
Quoted 10 lines
Quoted 7 lines
kernel/kevent/kevent_user.c line 92
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+static inline void kevent_user_ring_set (struct kevent_user *u,unsigned int num) +{ +unsigned int *idx; + + idx = (unsigned int *)u->pring[0 ];This is a bit ugly.
I specially use first 4 bytes in the first page to store index there, since it must be accessed from userspace and kernelspace.
Sure, but the C language is the preferred way in which we communicate and calcuate pointer offsets.
Quoted 27 lines
Quoted 27 lines
kernel/kevent/kevent_user.c line 101
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+ idx[0 ] = num; +} + +/* +* Note that kevents does not exactly fill the page (each ukevent is 40 bytes), +* so we reuse 4 bytes at the begining of the first page to store index. +* Take that into account if you want to change size of struct ukevent. +*/ +#define KEVENTS_ON_PAGE (PAGE_SIZE/sizeof(struct ukevent))How about doing
struct ukevent_ring { unsigned int index; struct ukevent[0]; }and removing all those nasty typeasting and offsetting games?
In fact you can even do
struct ukevent_ring { struct ukevent[(PAGE_SIZE - sizeof(unsigned int)) / sizeof(struct ukevent)]; unsigned int index; };
Quoted 6 lines
Quoted 1 line
if you're careful ;)
Ring takes more than one page, so it will be struct
ukevent_ring_0and structukevent_ring_other. Does it really needed?
Not a big problem, if you do thing it worse it.
Well, I've given a couple of prototype-style suggestions. Please take a look, see if all this open-coded offsetting magic can be done by the compiler in some reliable and readable fashion. It might not work out, but I suspect it will.
Quoted 11 lines
Quoted 9 lines
kernel/kevent/kevent_user.c line 143
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+ u->pring =kmalloc (pnum *sizeof (unsigned long ),GFP_KERNEL ); +if (!u->pring) +return -ENOMEM ; + +for (i=0 ; i<pnum; ++i) { + u->pring[i] =__get_free_page (GFP_KERNEL ); +if (!u->pring)bug: this is testing the wrong thing.
HOw come?
Take a closer look ;)
Quoted 1 line
__get_free_page()can return 0 if page was not allocated.
And that 0 is copied to u->pring[0], not to u->pring.
Quoted 1 line
Quoted 1 line
The function name is mistyped.
Did you miss an "OK"? It needs s/kevnet_user_mmap/kevent_user_mmap/g
Quoted 7 lines
Quoted 2 lines
This code doesn't have many comments, does it? What are we mapping here, and why would an application want to map it?
That code waits comments from people who requested it.
It is ring of the ready events, which can be read by userspace instead of calling syscall, so syscall just becomes "wait until there is a place" or something like that.
hm. Well, please fully comment code prior to sending it out for review. I do go on about this, but trust me, it makes the review much more effective.
Afaict this mmap function gives a user a free way of getting pinned memory. What is the upper bound on the amount of memory which a user can thus obtain?
Quoted 31 lines
Quoted 3 lines
Quoted 1 line
+static int
kevent_modify(struct ukevent *uk, struct kevent_user *u)<wonders what this function does>
Let me guess... It modifies kevent? :)
I will add comments.Quoted 15 lines
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+{ +struct kevent *k; +unsigned int hash =kevent_user_hash (uk); +int err = -ENODEV ; +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); + k =__kevent_search (&u->kevent_list[hash], uk, u); +if (k) { +spin_lock (&k->ulock); + k->event.event = uk->event; + k->event.req_flags = uk->req_flags; + k->event.ret_flags =0 ; +spin_unlock (&k->ulock); +kevent_requeue (k);Quoted 8 lines
kernel/kevent/kevent_user.c line 392
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+ err =0 ; + } +spin_unlock_irqrestore (&u->kevent_lock, flags); + +return err; +}ENODEV: "No such device". Doesn't sound appropriate.
Quoted 1 line
ENOKEVENT? I expect ENODEV means "there is no requested thing".
yes, it is hard to map standard errnos onto new and complex non-standard features.
I don't have a good answer to this, sorry.
Perhaps we should do
#define EPER_SYSCALL_BASE 0x10000
and then each syscall is free to implement new, syscall-specific errnos starting at this base. But that might be a stupid idea - I don't know. I'm sure the implementor of strerror() would think so ;)
Quoted 4 lines
Quoted 2 lines
EFAULT (all over the place).
Ok, I will return EFAULT when copy*user fails.
If that makes sense, fine. Sometimes it makes sense to return the number of bytes transferred up to the point of the fault. Please have a careful think and decide which behaviour is best in each of these cases.
Quoted 11 lines
Quoted 10 lines
kernel/kevent/kevent_user.c line 614
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+int err, cerr =0 , knum =0 , rnum =0 , i; +void __user *orig = arg; +struct ukevent uk; + +mutex_lock (&u->ctl_mutex); + + err = -ENFILE ; +if (u->kevent_num + num >=KEVENT_MAX_EVENTS )Can a malicious user force an arithmetic overflow here?
Quoted 1 line
All numbers here are unsigned and are compared against 4096.
Are they? I only see a comparison of a _sum_ against KEVENT_MAX_EVENTS. So if the user passes 0x0800,0xfffffff0, for example?
Quoted 19 lines
So, answer is no.
Quoted 17 lines
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ goto out_remove; + +if (num >KEVENT_MIN_BUFFS_ALLOC ) { +struct ukevent *ukev;kernel/kevent/kevent_user.c line 628
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ + ukev =kevent_get_user (num, arg); +if (ukev) { +for (i=0 ; i<num; ++i) { + err =kevent_user_add_ukevent (&ukev[i], u); +if (err) { +kevent_user_stat_increase_im (u); +if (i != rnum) +memcpy (&ukev[rnum], &ukev[i],sizeof (struct ukevent )); + rnum++;What's happening here? The games with
rnum' and comparing it withi'??
Quoted 6 lines
Quoted 1 line
Perhaps these are not the best-chosen identifiers..
When kevent is ready immediately it is copied into the same buffer into previous (rnum ready num) position. kevent at "rpos" was not ready immediately (otherwise it would be copied and rnum increased) and thus it is copied into the queue and can be overwritten here.
If you say so ;)
Please bear in mind that Michael Kerrisk <[email protected]> will want to be writing manpages for all this stuff.
And I must say that Michael repeatedly and correctly dragged me across the coals for something as simple and stupid as sys_sync_file_range(). Based on that experience, I wouldn't consider a new syscall like this to be settled until Michael has fully understood it. And I suspect he doesn't fully understand it until he has fully documented it.
Quoted 39 lines
Quoted 30 lines
Quoted 30 lines
kernel/kevent/kevent_user.c new file +30
-0+/* +* In nonblocking mode it returns as many events as possible, but not more than @max_nr. +* In blocking mode it waits until timeout or if at least @min_nr events are ready. +*/ +static int kevent_user_wait (struct file *file,struct kevent_user *u, +unsigned int min_nr,unsigned int max_nr,unsigned int timeout, +void __user *buf) +{ +struct kevent *k; +int cerr =0 , num =0 ; + +if (!(file->f_flags &O_NONBLOCK )) { +wait_event_interruptible_timeout (u->wait, + u->ready_num >= min_nr,msecs_to_jiffies (timeout)); + } + +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + } + +/* +* If it is one-shot kevent, it has been removed already from +* origin's queue, so we can easily free it here. +*/ +if (k->event.req_flags &KEVENT_REQ_ONESHOT ) +kevent_finish_user (k,1 ); + ++num;Quoted 8 lines
kernel/kevent/kevent_user.c line 706
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+kevent_user_stat_increase_wait (u); + } + +return (cerr)?cerr:num; +}So if this returns an error, the user doesn't know how many events were
actually completed? That doesn't seem good.
Quoted 2 lines
What is the alternative?
read()work the same way - either error or number of bytes read.
No. If read() hits an IO error or EFAULT partway through, read() will return the number-of-bytes-trasferred. read() will only return a -ve errno if it transferred zero bytes. This way, there is no lost information.
However kevent_user_wait() will return a -ve errno even if it has reaped some events. That's lost information and this might make it hard for a robust userspace client to implement error recovery?
Quoted 4 lines
So let me quote your first words about kevent:
Quoted 2 lines
Summary:
Quoted 8 lines
Quoted 5 lines
- has serious bugs which indicate that much better testing is needed.
- All
-EFOOreturn values need to be reviewed for appropriateness- needs much better commenting before I can do more than a local-level review.
As far as I can see there are no serious bugs except absence of two checks for and typo in function order, which abviously will be fixed.
Thus far I have found at least two bugs in this patchset which provide at least a local DoS and possibly a privilege escalation (aka a roothole) to local users. We hit a similar one in the epoll() implementation a while back.
This is serious stuff. So experience tells us to be fanatical in the checking of incoming syscall args. Check all the arguments to death for correct values. Look out for overflows in additions and multiplications. Look out for opportunities for excessive resource consumption. Exhaustively test your new syscalls with many combinations of values when the kernel is in various states.
Quoted 2 lines
All EFOO will be changed according to comments and better comments will be added.
Thanks.
Re: [take6 1/3] kevent: Core files.
Quoted 13 lines · Andrew Morton
Quoted 7 lines
Quoted 6 lines
kernel/kevent/kevent.c line 94
Quoted 4 lines
kernel/kevent/kevent.c new file +4
-0+spin_lock_init (&k->ulock); + k->kevent_entry.next =LIST_POISON1 ; + k->storage_entry.prev =LIST_POISON2 ; + k->ready_entry.next =LIST_POISON1 ;Nope ;)
Quoted 5 lines
I use pointer checks to determine if entry is in the list or not, why it is frowned upon here?
Please do not say about poisoning which takes a lot of cpu cycles to get new cachelines and so on - everything in that entry is in the cache, since entry was added/deleted/accessed through list walk macro.
Quoted 6 lines
"poisoning which takes a lot of cpu cycles". So there ;)
I assure you, that poisoning code might disappear at any time.
If you want to be able to determine whether a
list_headhas been detached you can detach it withlist_del_init()and then uselist_empty()on it.
I can't due to RCU rules.
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
kernel/kevent/kevent.c line 238
Quoted 3 lines
kernel/kevent/kevent.c new file +3
-0+} + +late_initcall (kevent_sys_init);Why is it
late_initcall? (A comment is needed)
Quoted 6 lines
Quoted 1 line
Why not?
Why?
There must have been some reason for having made this a
late_initcall()and that reason is 100% concealed from the reader of this code.
kevent must be initialized before use, and it must happen before userspace started, so I use late_initcall(), as I said it can be anything other which is called before userspace.
Quoted 1 line
IOW, it needs a comment.
Sure.
I'm working right now on fixing all issues mentioned in this thread, and comments are not on the last place.
Quoted 48 lines
Quoted 10 lines
Quoted 7 lines
kernel/kevent/kevent_user.c line 92
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+static inline void kevent_user_ring_set (struct kevent_user *u,unsigned int num) +{ +unsigned int *idx; + + idx = (unsigned int *)u->pring[0 ];This is a bit ugly.
I specially use first 4 bytes in the first page to store index there, since it must be accessed from userspace and kernelspace.
Sure, but the C language is the preferred way in which we communicate and calcuate pointer offsets.
Quoted 27 lines
Quoted 27 lines
kernel/kevent/kevent_user.c line 101
Quoted 9 lines
kernel/kevent/kevent_user.c new file +9
-0+ idx[0 ] = num; +} + +/* +* Note that kevents does not exactly fill the page (each ukevent is 40 bytes), +* so we reuse 4 bytes at the begining of the first page to store index. +* Take that into account if you want to change size of struct ukevent. +*/ +#define KEVENTS_ON_PAGE (PAGE_SIZE/sizeof(struct ukevent))How about doing
struct ukevent_ring { unsigned int index; struct ukevent[0]; }and removing all those nasty typeasting and offsetting games?
In fact you can even do
struct ukevent_ring { struct ukevent[(PAGE_SIZE - sizeof(unsigned int)) / sizeof(struct ukevent)]; unsigned int index; };Quoted 6 lines
Quoted 1 line
if you're careful ;)
Ring takes more than one page, so it will be struct
ukevent_ring_0and structukevent_ring_other. Does it really needed?
Not a big problem, if you do thing it worse it.
Quoted 4 lines
Well, I've given a couple of prototype-style suggestions. Please take a look, see if all this open-coded offsetting magic can be done by the compiler in some reliable and readable fashion. It might not work out, but I suspect it will.
I think I will use structure with index on each page, since kevents are unaligned to exaclty fit page, and it can be some kind of (later) optimisation to use not global counter, but per-page one.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
kernel/kevent/kevent_user.c line 143
Quoted 7 lines
kernel/kevent/kevent_user.c new file +7
-0+ u->pring =kmalloc (pnum *sizeof (unsigned long ),GFP_KERNEL ); +if (!u->pring) +return -ENOMEM ; + +for (i=0 ; i<pnum; ++i) { + u->pring[i] =__get_free_page (GFP_KERNEL ); +if (!u->pring)bug: this is testing the wrong thing.
Quoted 3 lines
Quoted 1 line
HOw come?
Take a closer look ;)
[i] My fault :)
Quoted 7 lines
Quoted 1 line
__get_free_page()can return 0 if page was not allocated.And that 0 is copied to u->pring[0], not to
u->pring.Quoted 1 line
Quoted 1 line
The function name is mistyped.
Did you miss an "OK"? It needs s/kevnet_user_mmap/kevent_user_mmap/g
It is already fixed :)
Quoted 8 lines
Quoted 7 lines
Quoted 2 lines
This code doesn't have many comments, does it? What are we mapping here, and why would an application want to map it?
That code waits comments from people who requested it.
It is ring of the ready events, which can be read by userspace instead of calling syscall, so syscall just becomes "wait until there is a place" or something like that.
Quoted 6 lines
hm. Well, please fully comment code prior to sending it out for review. I do go on about this, but trust me, it makes the review much more effective.
Afaict this mmap function gives a user a free way of getting pinned memory. What is the upper bound on the amount of memory which a user can thus obtain?
it is limited by maximum queue length which is 4k entries right now, so maximum number of paged here is 4k*40/page_size, i.e. about 40 pages on x86.
Quoted 38 lines
Quoted 31 lines
Quoted 3 lines
Quoted 1 line
+static int
kevent_modify(struct ukevent *uk, struct kevent_user *u)<wonders what this function does>
Let me guess... It modifies kevent? :)
I will add comments.Quoted 15 lines
Quoted 15 lines
kernel/kevent/kevent_user.c new file +15
-0+{ +struct kevent *k; +unsigned int hash =kevent_user_hash (uk); +int err = -ENODEV ; +unsigned long flags; + +spin_lock_irqsave (&u->kevent_lock, flags); + k =__kevent_search (&u->kevent_list[hash], uk, u); +if (k) { +spin_lock (&k->ulock); + k->event.event = uk->event; + k->event.req_flags = uk->req_flags; + k->event.ret_flags =0 ; +spin_unlock (&k->ulock); +kevent_requeue (k);Quoted 8 lines
kernel/kevent/kevent_user.c line 392
Quoted 6 lines
kernel/kevent/kevent_user.c new file +6
-0+ err =0 ; + } +spin_unlock_irqrestore (&u->kevent_lock, flags); + +return err; +}ENODEV: "No such device". Doesn't sound appropriate.
Quoted 1 line
ENOKEVENT? I expect ENODEV means "there is no requested thing".
yes, it is hard to map standard errnos onto new and complex non-standard features.
I don't have a good answer to this, sorry.
Quoted 7 lines
Perhaps we should do
#define
EPER_SYSCALL_BASE0x10000and then each syscall is free to implement new, syscall-specific errnos starting at this base. But that might be a stupid idea - I don't know. I'm sure the implementor of
strerror()would think so ;)
There are some issues with errno and rules for kernel-only errno holes... Let's just return EINVAL here.
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
EFAULT (all over the place).
Ok, I will return EFAULT when copy*user fails.
If that makes sense, fine. Sometimes it makes sense to return the number of bytes transferred up to the point of the fault. Please have a careful think and decide which behaviour is best in each of these cases.
No, it much better to show that things are broken.
Half of the event transferred will not help user, since his pointer is last data in the structure :)
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
kernel/kevent/kevent_user.c line 614
Quoted 8 lines
kernel/kevent/kevent_user.c new file +8
-0+int err, cerr =0 , knum =0 , rnum =0 , i; +void __user *orig = arg; +struct ukevent uk; + +mutex_lock (&u->ctl_mutex); + + err = -ENFILE ; +if (u->kevent_num + num >=KEVENT_MAX_EVENTS )Can a malicious user force an arithmetic overflow here?
Quoted 4 lines
Quoted 1 line
All numbers here are unsigned and are compared against 4096.
Are they? I only see a comparison of a _sum_ against
KEVENT_MAX_EVENTS. So if the user passes 0x0800,0xfffffff0, for example?
I've already added check for that user value, you are correct that sum can overflow.
Quoted 28 lines
Quoted 19 lines
So, answer is no.
Quoted 17 lines
Quoted 4 lines
kernel/kevent/kevent_user.c new file +4
-0+ goto out_remove; + +if (num >KEVENT_MIN_BUFFS_ALLOC ) { +struct ukevent *ukev;kernel/kevent/kevent_user.c line 628
Quoted 10 lines
kernel/kevent/kevent_user.c new file +10
-0+ + ukev =kevent_get_user (num, arg); +if (ukev) { +for (i=0 ; i<num; ++i) { + err =kevent_user_add_ukevent (&ukev[i], u); +if (err) { +kevent_user_stat_increase_im (u); +if (i != rnum) +memcpy (&ukev[rnum], &ukev[i],sizeof (struct ukevent )); + rnum++;What's happening here? The games with
rnum' and comparing it withi'??Quoted 6 lines
Quoted 1 line
Perhaps these are not the best-chosen identifiers..
When kevent is ready immediately it is copied into the same buffer into previous (rnum ready num) position. kevent at "rpos" was not ready immediately (otherwise it would be copied and rnum increased) and thus it is copied into the queue and can be overwritten here.
If you say so ;)
Quoted 8 lines
Please bear in mind that Michael Kerrisk <[email protected]> will want to be writing manpages for all this stuff.
And I must say that Michael repeatedly and correctly dragged me across the coals for something as simple and stupid as
sys_sync_file_range(). Based on that experience, I wouldn't consider a new syscall like this to be settled until Michael has fully understood it. And I suspect he doesn't fully understand it until he has fully documented it.
Yep, comments are always not that cool thing to do...
Quoted 42 lines
Quoted 39 lines
Quoted 30 lines
Quoted 30 lines
kernel/kevent/kevent_user.c new file +30
-0+/* +* In nonblocking mode it returns as many events as possible, but not more than @max_nr. +* In blocking mode it waits until timeout or if at least @min_nr events are ready. +*/ +static int kevent_user_wait (struct file *file,struct kevent_user *u, +unsigned int min_nr,unsigned int max_nr,unsigned int timeout, +void __user *buf) +{ +struct kevent *k; +int cerr =0 , num =0 ; + +if (!(file->f_flags &O_NONBLOCK )) { +wait_event_interruptible_timeout (u->wait, + u->ready_num >= min_nr,msecs_to_jiffies (timeout)); + } + +while (num < max_nr && ((k =kqueue_dequeue_ready (u)) !=NULL )) { +if (copy_to_user (buf + num*sizeof (struct ukevent ), + &k->event,sizeof (struct ukevent ))) { + cerr = -EINVAL ; +break ; + } + +/* +* If it is one-shot kevent, it has been removed already from +* origin's queue, so we can easily free it here. +*/ +if (k->event.req_flags &KEVENT_REQ_ONESHOT ) +kevent_finish_user (k,1 ); + ++num;Quoted 8 lines
kernel/kevent/kevent_user.c line 706
Quoted 5 lines
kernel/kevent/kevent_user.c new file +5
-0+kevent_user_stat_increase_wait (u); + } + +return (cerr)?cerr:num; +}So if this returns an error, the user doesn't know how many events were
actually completed? That doesn't seem good.Quoted 2 lines
What is the alternative?
read()work the same way - either error or number of bytes read.
Quoted 8 lines
No. If
read()hits an IO error or EFAULT partway through,read()will return the number-of-bytes-trasferred.read()will only return a -ve errno if it transferred zero bytes. This way, there is no lost information.However
kevent_user_wait()will return a -ve errno even if it has reaped some events. That's lost information and this might make it hard for a robust userspace client to implement error recovery?
I have no strong opinion on what value must be returned here. I always think that if error happend, than it must be indicated. But it is perfectly ok to return number of correctly read kevents and userspace can compare that number with requested number.
Quoted 10 lines
Quoted 10 lines
So let me quote your first words about kevent:
Quoted 7 lines
Summary:
- has serious bugs which indicate that much better testing is needed.
- All
-EFOOreturn values need to be reviewed for appropriateness- needs much better commenting before I can do more than a local-level review.
Quoted 7 lines
Quoted 2 lines
As far as I can see there are no serious bugs except absence of two checks for and typo in function order, which abviously will be fixed.
Thus far I have found at least two bugs in this patchset which provide at least a local DoS and possibly a privilege escalation (aka a roothole) to local users. We hit a similar one in the
epoll()implementation a while back.
It is already fixed.
Quoted 6 lines
This is serious stuff. So experience tells us to be fanatical in the checking of incoming syscall args. Check all the arguments to death for correct values. Look out for overflows in additions and multiplications. Look out for opportunities for excessive resource consumption. Exhaustively test your new syscalls with many combinations of values when the kernel is in various states.
Agree, such security-related issues must be reviewed and tested as much as possible.
Signature · 1 line
Evgeniy Polyakov
Re: [take6 1/3] kevent: Core files.
Quoted 7 lines · On Thu, 10 Aug 2006 11:50:47 +0400 Evgeniy Polyakov <[email protected]> wrote:
Quoted 3 lines
Afaict this mmap function gives a user a free way of getting pinned memory. What is the upper bound on the amount of memory which a user can thus obtain?
it is limited by maximum queue length which is 4k entries right now, so maximum number of paged here is 4k*40/page_size, i.e. about 40 pages on x86.
Is that per user or per fd? If the latter that is, with the usual RLIMIT_NOFILE, 160MBytes. 2GB with 64k pagesize. Problem ;)
Re: [take6 1/3] kevent: Core files.
Quoted 10 lines · Andrew Morton
Quoted 7 lines
Quoted 3 lines
Afaict this mmap function gives a user a free way of getting pinned memory. What is the upper bound on the amount of memory which a user can thus obtain?
it is limited by maximum queue length which is 4k entries right now, so maximum number of paged here is 4k*40/page_size, i.e. about 40 pages on x86.
Is that per user or per fd? If the latter that is, with the usual
RLIMIT_NOFILE, 160MBytes. 2GB with 64k pagesize. Problem ;)
Per kevent fd.
I have some ideas about better mmap ring implementation, which would dinamically grow it's buffer when events are added and reuse the same place for next events, but there are some nitpics unresolved yet. Let's not see there in next releases (no merge of course), until better solution is ready. I will change that area when other things are ready.
Signature · 1 line
Evgeniy Polyakov
Re: [take6 1/3] kevent: Core files.
Quoted 12 lines · On Thu, 10 Aug 2006 12:22:35 +0400 Evgeniy Polyakov <[email protected]> wrote:
Quoted 10 lines · Andrew Morton
Quoted 7 lines
Quoted 3 lines
Afaict this mmap function gives a user a free way of getting pinned memory. What is the upper bound on the amount of memory which a user can thus obtain?
it is limited by maximum queue length which is 4k entries right now, so maximum number of paged here is 4k*40/page_size, i.e. about 40 pages on x86.
Is that per user or per fd? If the latter that is, with the usual
RLIMIT_NOFILE, 160MBytes. 2GB with 64k pagesize. Problem ;)
Quoted 6 lines
Per kevent fd.
I have some ideas about better mmap ring implementation, which would dinamically grow it's buffer when events are added and reuse the same place for next events, but there are some nitpics unresolved yet. Let's not see there in next releases (no merge of course), until better solution is ready. I will change that area when other things are ready.
This is not a problem with the mmap interface per-se. If the proposed event code permits each user to pin 160MB of kernel memory then that would be a serious problem.
Re: [take6 1/3] kevent: Core files.
Quoted 10 lines · Andrew Morton
Quoted 6 lines
Per kevent fd.
I have some ideas about better mmap ring implementation, which would dinamically grow it's buffer when events are added and reuse the same place for next events, but there are some nitpics unresolved yet. Let's not see there in next releases (no merge of course), until better solution is ready. I will change that area when other things are ready.This is not a problem with the mmap interface per-se. If the proposed event code permits each user to pin 160MB of kernel memory then that would be a serious problem.
The main disadvantage is that all memory is allocated on the start even if it will not be used later. I think dynamic grow is appropriate solution, since user will have that memory used anyway, since kevents are allocated, just part of them will be allocated from possibly mmaped memory.
Signature · 1 line
Evgeniy Polyakov
Re: [take6 1/3] kevent: Core files.
Quoted 12 lines · On Fri, 11 Aug 2006 10:15:35 +0400 Evgeniy Polyakov <[email protected]> wrote:
Quoted 10 lines · Andrew Morton
Quoted 6 lines
Per kevent fd.
I have some ideas about better mmap ring implementation, which would dinamically grow it's buffer when events are added and reuse the same place for next events, but there are some nitpics unresolved yet. Let's not see there in next releases (no merge of course), until better solution is ready. I will change that area when other things are ready.This is not a problem with the mmap interface per-se. If the proposed event code permits each user to pin 160MB of kernel memory then that would be a serious problem.
Quoted 5 lines
The main disadvantage is that all memory is allocated on the start even if it will not be used later. I think dynamic grow is appropriate solution, since user will have that memory used anyway, since kevents are allocated, just part of them will be allocated from possibly mmaped memory.
But the worst-case remains the same, doesn't it? 160MB of pinned kernel memory per user?
Re: [take6 1/3] kevent: Core files.
Quoted 15 lines · Andrew Morton
Quoted 12 lines · On Fri, 11 Aug 2006 10:15:35 +0400 Evgeniy Polyakov <[email protected]> wrote:
Quoted 10 lines · Andrew Morton
Quoted 6 lines
Per kevent fd.
I have some ideas about better mmap ring implementation, which would dinamically grow it's buffer when events are added and reuse the same place for next events, but there are some nitpics unresolved yet. Let's not see there in next releases (no merge of course), until better solution is ready. I will change that area when other things are ready.This is not a problem with the mmap interface per-se. If the proposed event code permits each user to pin 160MB of kernel memory then that would be a serious problem.
Quoted 8 lines
Quoted 5 lines
The main disadvantage is that all memory is allocated on the start even if it will not be used later. I think dynamic grow is appropriate solution, since user will have that memory used anyway, since kevents are allocated, just part of them will be allocated from possibly mmaped memory.
But the worst-case remains the same, doesn't it? 160MB of pinned kernel memory per user?
Yes. And now I think dynamic growing is not a good solution, since user can not know when he must call mmap() again to get additional pages (although I have some hacks to "dynamically" replace previously mmapped pages with new ones).
This area can be decreased down to 70mb by reducing amount of information placed into the buffer (only user's data and flags) without additional hints.
Signature · 1 line
Evgeniy Polyakov
Re: [take6 1/3] kevent: Core files.
Quoted 25 lines · On Fri, 11 Aug 2006 10:30:21 +0400 Evgeniy Polyakov <[email protected]> wrote:
Quoted 15 lines · Andrew Morton
Quoted 12 lines · On Fri, 11 Aug 2006 10:15:35 +0400 Evgeniy Polyakov <[email protected]> wrote:
Quoted 10 lines · Andrew Morton
Quoted 6 lines
Per kevent fd.
I have some ideas about better mmap ring implementation, which would dinamically grow it's buffer when events are added and reuse the same place for next events, but there are some nitpics unresolved yet. Let's not see there in next releases (no merge of course), until better solution is ready. I will change that area when other things are ready.This is not a problem with the mmap interface per-se. If the proposed event code permits each user to pin 160MB of kernel memory then that would be a serious problem.
Quoted 8 lines
Quoted 5 lines
The main disadvantage is that all memory is allocated on the start even if it will not be used later. I think dynamic grow is appropriate solution, since user will have that memory used anyway, since kevents are allocated, just part of them will be allocated from possibly mmaped memory.
But the worst-case remains the same, doesn't it? 160MB of pinned kernel memory per user?
Quoted 9 lines
Yes. And now I think dynamic growing is not a good solution, since user can not know when he must call
mmap()again to get additional pages (although I have some hacks to "dynamically" replace previously mmapped pages with new ones).This area can be decreased down to 70mb by reducing amount of information placed into the buffer (only user's data and flags) without additional hints.
70MB is still very bad, naturally.
There are other ways in which users can do this sort of thing - passing fd's across sockets, allocating zillions of pagetables come to mind. But we don't want to add more.
Possible options:
- Add a new rlimit for the number of kevent fd's
- Add a new rlimit for the amount of kevent memory
- Add a new rlimit for the total amount of pinned kernel memory. First user is kevent.
- Account a kevent fd as being worth 100 regular fds, so the naughty user hits EMFILE early (ug).
A new rlimit is attractive, and they're easy to add. Problem is, userspace support is hard (I think). afaik a standard Linux system doesn't have global and per-user rlimit config files which are parsed and acted upon at login. That would make rlimits more useful.
Re: [take6 1/3] kevent: Core files.
Quoted 6 lines · Andrew Morton
Quoted 4 lines
This area can be decreased down to 70mb by reducing amount of information placed into the buffer (only user's data and flags) without additional hints.
70MB is still very bad, naturally.
Actually I do not think that 4k events is a good choice - I expect people will scale it to tens of thousands at least, so we definitely want not to allow user to create way too many kevent fds.
Quoted 4 lines
There are other ways in which users can do this sort of thing - passing fd's across sockets, allocating zillions of pagetables come to mind. But we don't want to add more.
Quoted 8 lines
Possible options:
- Add a new rlimit for the number of kevent fd's
- Add a new rlimit for the amount of kevent memory
- Add a new rlimit for the total amount of pinned kernel memory. First user is kevent.
I think this rlimit and first one are the best choises.
Quoted 7 lines
- Account a kevent fd as being worth 100 regular fds, so the naughty user hits EMFILE early (ug).
A new rlimit is attractive, and they're easy to add. Problem is, userspace support is hard (I think). afaik a standard Linux system doesn't have global and per-user rlimit config files which are parsed and acted upon at login. That would make rlimits more useful.
As for now it is possible to use stack size rlimit for example.
Signature · 1 line
Evgeniy Polyakov
Re: [take6 1/3] kevent: Core files.
In reply to Evgeniy PolyakovQuoted 4 lines · Evgeniy Polyakov
The main disadvantage is that all memory is allocated on the start even if it will not be used later. I think dynamic grow is appropriate solution, since user will have that memory used anyway, since kevents are allocated,
If you _allocate_ memory at startup you're doing something wrong. All you should do is allocate address space. Memory should be allocated when it is needed.
Growing a memory region is always hard because it means you cannot keep any addresses around and always have to reload a base pointer. That's not ideal.
Especially on 64-bit machines address space really is no limitation anymore. So, allocate as much as needed, allocate memory when it's needed, and don't resize.
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Re: [take6 1/3] kevent: Core files.
Quoted 10 lines · Ulrich Drepper
Quoted 4 lines · Evgeniy Polyakov
The main disadvantage is that all memory is allocated on the start even if it will not be used later. I think dynamic grow is appropriate solution, since user will have that memory used anyway, since kevents are allocated,
If you _allocate_ memory at startup you're doing something wrong. All you should do is allocate address space. Memory should be allocated when it is needed.
Quoted 7 lines
Growing a memory region is always hard because it means you cannot keep any addresses around and always have to reload a base pointer. That's not ideal.
Especially on 64-bit machines address space really is no limitation anymore. So, allocate as much as needed, allocate memory when it's needed, and don't resize.
That requires mmap hacks to substitute pages in run-time without user notifications. I do not expect it is a good solution, since on x86 it requires full TLB flush (at least when I did it there were no exported methods to flush separate addresses).
Quoted 3 lines
Signature · 1 line
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take6 1/3] kevent: Core files.
From: Evgeniy Polyakov <[email protected]>
Date: Fri, 11 Aug 2006 10:33:53 +0400
Quoted 4 lines
That requires mmap hacks to substitute pages in run-time without user notifications. I do not expect it is a good solution, since on x86 it requires full TLB flush (at least when I did it there were no exported methods to flush separate addresses).
You just need to provide a do_no_page method, the VM layer will take care of the page level flushing or whatever else might be needed.
Re: [take6 1/3] kevent: Core files.
Quoted 11 lines · David Miller
From: Evgeniy Polyakov <[email protected]>
Date: Fri, 11 Aug 2006 10:33:53 +0400Quoted 4 lines
That requires mmap hacks to substitute pages in run-time without user notifications. I do not expect it is a good solution, since on x86 it requires full TLB flush (at least when I did it there were no exported methods to flush separate addresses).
You just need to provide a
do_no_pagemethod, the VM layer will take care of the page level flushing or whatever else might be needed.
Yes, it is the simplest way to extend mapping but not to replace pages which are successfully mapped, but such hacks are not needed for kevent which only expects to extend mapping when number of ready kevents increases.
So I will create such implementation and will place a reduced amount of info into that pages.
Signature · 1 line
Evgeniy Polyakov
cover kevent: generic event handling mechanism.
In reply to Andrew MortonHello.
Generic event handling mechanism.
Changes from 'take6' patchset:
* a lot of comments!
* do not use list poisoning for detection of the fact, that entry is in the list
* return number of ready kevents even if copy*user() fails
* strict check for number of kevents in syscall
* use ARRAY_SIZE for array size calculation
* changed superblock magic number
* use SLAB_PANIC instead of direct panic() call
* changed -E* return values
* a lot of small cleanups and indent fixes
* fully removed AIO stuff from patchset
Changes from 'take5' patchset:
* removed compilation warnings about unused wariables when lockdep is not turned on
* do not use internal socket structures, use appropriate (exported) wrappers instead
* removed default 1 second timeout
* removed AIO stuff from patchset
Changes from 'take4' patchset:
* use miscdevice instead of chardevice
* comments fixes
Changes from 'take3' patchset:
* removed serializing mutex from kevent_user_wait()
* moved storage list processing to RCU
* removed lockdep screaming - all storage locks are initialized in the same function, so it was learned to differentiate between various cases
* remove kevent from storage if is marked as broken after callback
* fixed a typo in mmaped buffer implementation which would end up in wrong index calcualtion
Changes from 'take2' patchset:
* split kevent_finish_user() to locked and unlocked variants
* do not use KEVENT_STAT ifdefs, use inline functions instead
* use array of callbacks of each type instead of each kevent callback initialization
* changed name of ukevent guarding lock
* use only one kevent lock in kevent_user for all hash buckets instead of per-bucket locks
* do not use kevent_user_ctl structure instead provide needed arguments as syscall parameters
* various indent cleanups
* added optimisation, which is aimed to help when a lot of kevents are being copied from userspace
* mapped buffer (initial) implementation (no userspace yet)
Changes from 'take1' patchset:
- rebased against 2.6.18-git tree
- removed ioctl controlling
- added new syscall kevent_get_events(int fd, unsigned int min_nr, unsigned int max_nr, unsigned int timeout, void __user *buf, unsigned flags)
- use old syscall kevent_ctl for creation/removing, modification and initial kevent initialization
- use mutuxes instead of semaphores
- added file descriptor check and return error if provided descriptor does not match kevent file operations
- various indent fixes
- removed aio_sendfile() declarations.
Thank you.
Signature · 1 line
Evgeniy Polyakov
PATCH 1/1 kevent: core files and timer/poll notifications.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
- timer and poll/select notifications
With this patchset rate of requests per second has achieved 2500 req/sec while with epoll/kqueue and similar techniques it is about 1600-1800 requests per second on my test hardware and trivial web server.
arch/i386/kernel/syscall_table.S +2 -0
@@ -317,3 +317,5 @@ ENTRY(sys_call_table) .long sys_tee /* 315 */ .long sys_vmsplice .long sys_move_pages + .long sys_kevent_get_events + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +2 -0
@@ -713,4 +713,6 @@ #endif .quad sys_tee .quad compat_sys_vmsplice .quad compat_sys_move_pages + .quad sys_kevent_get_events + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +3 -1
@@ -323,10 +323,12 @@ #define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316#define __NR_move_pages 317 +#define __NR_kevent_get_events 318 +#define __NR_kevent_ctl 319#ifdef __KERNEL__ -#define NR_syscalls 318 +#define NR_syscalls 320/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/unistd.h +5 -1
@@ -619,10 +619,14 @@ #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice)#define __NR_move_pages 279 __SYSCALL(__NR_move_pages, sys_move_pages) +#define __NR_kevent_get_events 280 +__SYSCALL(__NR_kevent_get_events, sys_kevent_get_events) +#define __NR_kevent_ctl 281 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_move_pages +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS
include/linux/kevent.h new file +302 -0
302 lines not on this page · raw message
include/linux/kevent_storage.h new file +11 -0
@@ -0,0 +1,11 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +3 -0
@@ -597,4 +597,7 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_kevent_get_events (int ctl_fd,unsigned int min,unsigned int max, +unsigned int timeout,void __user *buf,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,unsigned int cmd,unsigned int num,void __user *buf);#endif
init/Kconfig +2 -0
@@ -218,6 +218,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o
kernel/kevent/Kconfig new file +59 -0
59 lines not on this page · raw message
kernel/kevent/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +obj-y := kevent.o kevent_user.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
kernel/kevent/kevent.c new file +251 -0
251 lines not on this page · raw message
kernel/kevent/kevent_poll.c new file +220 -0
220 lines not on this page · raw message
kernel/kevent/kevent_timer.c new file +119 -0
119 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +959 -0
959 lines not on this page · raw message
kernel/sys_ni.c +3 -0
@@ -122,6 +122,9 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_kevent_get_events); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
Signature · 1 line
Evgeniy Polyakov
Re: [take7 1/1] kevent: core files and timer/poll notifications.
Quoted 3 lines · Evgeniy Polyakov
With this patchset rate of requests per second has achieved 2500 req/sec while with epoll/kqueue and similar techniques it is about 1600-1800 requests per second on my test hardware and trivial web server.
Nope, it is old record from archives... Current one is 2600+
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take6 0/3] kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovFrom: Evgeniy Polyakov <[email protected]>
Date: Wed, 9 Aug 2006 12:02:39 +0400
Evgeniy, it's things like the following that make it very draining mentally to review your work.
Quoted 1 line
* removed AIO stuff from patchset
You didn't really do this, you leave the aio_* syscalls and stubs in there, and you also left things like tcp_async_send() in there.
All the foo_naio_*() stuff is still in there to.
Please remove all of async business we've asked you to.
Thanks.
Re: [take6 0/3] kevent: Generic event handling mechanism.
Quoted 10 lines · David Miller
From: Evgeniy Polyakov <[email protected]>
Date: Wed, 9 Aug 2006 12:02:39 +0400Evgeniy, it's things like the following that make it very draining mentally to review your work.
Quoted 1 line
* removed AIO stuff from patchset
You didn't really do this, you leave the aio_* syscalls and stubs in there, and you also left things like
tcp_async_send()in there.
By AIO I meant VFS AIO, not network stuff, exactly that part was frown upon in reviews.
Quoted 3 lines
All the foo_naio_*() stuff is still in there to.
Please remove all of async business we've asked you to.
So you want to review kevent core only at the first point and postpone network AIO and the rest implementation after core is correct. Should I remove poll/timer notifications too?
Signature · 1 line
Evgeniy Polyakov
David Miller
maintainerRe: [take6 0/3] kevent: Generic event handling mechanism.
From: Evgeniy Polyakov <[email protected]>
Date: Wed, 9 Aug 2006 12:07:57 +0400
Quoted 2 lines
So you want to review kevent core only at the first point and postpone network AIO and the rest implementation after core is correct.
That's the idea
Quoted 1 line
Should I remove poll/timer notifications too?
That can stay
Re: [take6 0/3] kevent: Generic event handling mechanism.
Quoted 11 lines · David Miller
From: Evgeniy Polyakov <[email protected]>
Date: Wed, 9 Aug 2006 12:07:57 +0400Quoted 2 lines
So you want to review kevent core only at the first point and postpone network AIO and the rest implementation after core is correct.
That's the idea
Quoted 1 line
Should I remove poll/timer notifications too?
That can stay
Ok, I will regenerate the lastest patchset completely without AIO stuff (both network and VFS) and resend it soon.
Thank you.
Signature · 1 line
Evgeniy Polyakov
cover kevent: Generic event handling mechanism.
In reply to Evgeniy PolyakovGeneric event handling mechanism.
Changes from 'take7' patchset:
* new mmap interface (not tested, waiting for other changes to be acked)
- use nopage() method to dynamically substitue pages
- allocate new page for events only when new added kevent requres it
- do not use ugly index dereferencing, use structure instead
- reduced amount of data in the ring (id and flags), maximum 12 pages on x86 per kevent fd
Changes from 'take6' patchset:
* a lot of comments!
* do not use list poisoning for detection of the fact, that entry is in the list
* return number of ready kevents even if copy*user() fails
* strict check for number of kevents in syscall
* use ARRAY_SIZE for array size calculation
* changed superblock magic number
* use SLAB_PANIC instead of direct panic() call
* changed -E* return values
* a lot of small cleanups and indent fixes
Changes from 'take5' patchset:
* removed compilation warnings about unused wariables when lockdep is not turned on
* do not use internal socket structures, use appropriate (exported) wrappers instead
* removed default 1 second timeout
* removed AIO stuff from patchset
Changes from 'take4' patchset:
* use miscdevice instead of chardevice
* comments fixes
Changes from 'take3' patchset:
* removed serializing mutex from kevent_user_wait()
* moved storage list processing to RCU
* removed lockdep screaming - all storage locks are initialized in the same function, so it was learned to differentiate between various cases
* remove kevent from storage if is marked as broken after callback
* fixed a typo in mmaped buffer implementation which would end up in wrong index calcualtion
Changes from 'take2' patchset:
* split kevent_finish_user() to locked and unlocked variants
* do not use KEVENT_STAT ifdefs, use inline functions instead
* use array of callbacks of each type instead of each kevent callback initialization
* changed name of ukevent guarding lock
* use only one kevent lock in kevent_user for all hash buckets instead of per-bucket locks
* do not use kevent_user_ctl structure instead provide needed arguments as syscall parameters
* various indent cleanups
* added optimisation, which is aimed to help when a lot of kevents are being copied from userspace
* mapped buffer (initial) implementation (no userspace yet)
Changes from 'take1' patchset:
- rebased against 2.6.18-git tree
- removed ioctl controlling
- added new syscall kevent_get_events(int fd, unsigned int min_nr, unsigned int max_nr, unsigned int timeout, void __user *buf, unsigned flags)
- use old syscall kevent_ctl for creation/removing, modification and initial kevent initialization
- use mutuxes instead of semaphores
- added file descriptor check and return error if provided descriptor does not match kevent file operations
- various indent fixes
- removed aio_sendfile() declarations.
Thank you.
PATCH 1/2 kevent: Core files.
Core files.
This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines
arch/i386/kernel/syscall_table.S +2 -0
@@ -317,3 +317,5 @@ ENTRY(sys_call_table) .long sys_tee /* 315 */ .long sys_vmsplice .long sys_move_pages + .long sys_kevent_get_events + .long sys_kevent_ctl
arch/x86_64/ia32/ia32entry.S +2 -0
@@ -713,4 +713,6 @@ #endif .quad sys_tee .quad compat_sys_vmsplice .quad compat_sys_move_pages + .quad sys_kevent_get_events + .quad sys_kevent_ctl ia32_syscall_end:
include/asm-i386/unistd.h +3 -1
@@ -323,10 +323,12 @@ #define __NR_sync_file_range 314#define __NR_tee 315#define __NR_vmsplice 316#define __NR_move_pages 317 +#define __NR_kevent_get_events 318 +#define __NR_kevent_ctl 319#ifdef __KERNEL__ -#define NR_syscalls 318 +#define NR_syscalls 320/* * user-visible error numbers are in the range -1 - -128: see
include/asm-x86_64/unistd.h +5 -1
@@ -619,10 +619,14 @@ #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice)#define __NR_move_pages 279 __SYSCALL(__NR_move_pages, sys_move_pages) +#define __NR_kevent_get_events 280 +__SYSCALL(__NR_kevent_get_events, sys_kevent_get_events) +#define __NR_kevent_ctl 281 +__SYSCALL(__NR_kevent_ctl, sys_kevent_ctl)#ifdef __KERNEL__ -#define __NR_syscall_max __NR_move_pages +#define __NR_syscall_max __NR_kevent_ctl#ifndef __NO_STUBS
include/linux/kevent.h new file +309 -0
309 lines not on this page · raw message
include/linux/kevent_storage.h new file +11 -0
@@ -0,0 +1,11 @@ +#ifndef __KEVENT_STORAGE_H +#define __KEVENT_STORAGE_H + +struct kevent_storage +{ +void *origin;/* Originator's pointer, e.g. struct sock or struct file. Can be NULL. */ +struct list_head list;/* List of queued kevents. */ +spinlock_t lock;/* Protects users queue. */ +}; + +#endif /* __KEVENT_STORAGE_H */
include/linux/syscalls.h +3 -0
@@ -597,4 +597,7 @@ asmlinkage long sys_get_robust_list(intasmlinkage long sys_set_robust_list (struct robust_list_head __user *head,size_t len); +asmlinkage long sys_kevent_get_events (int ctl_fd,unsigned int min,unsigned int max, +unsigned int timeout,void __user *buf,unsigned flags); +asmlinkage long sys_kevent_ctl (int ctl_fd,unsigned int cmd,unsigned int num,void __user *buf);#endif
init/Kconfig +2 -0
@@ -218,6 +218,8 @@ config AUDITSYSCALL such as SELinux. To use audit's filesystem watch feature, please ensure that INOTIFY is configured. +source "kernel/kevent/Kconfig" + config IKCONFIG bool "Kernel .config support" ---help---
kernel/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_DETECT_SOFTLOCKUP) += softl obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ obj-$(CONFIG_SECCOMP) += seccomp.o obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o +obj-$(CONFIG_KEVENT) += kevent/ obj-$(CONFIG_RELAY) += relay.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o
kernel/kevent/Kconfig new file +59 -0
59 lines not on this page · raw message
kernel/kevent/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +obj-y := kevent.o kevent_user.o +obj-$(CONFIG_KEVENT_SOCKET) += kevent_socket.o +obj-$(CONFIG_KEVENT_INODE) += kevent_inode.o +obj-$(CONFIG_KEVENT_TIMER) += kevent_timer.o +obj-$(CONFIG_KEVENT_POLL) += kevent_poll.o +obj-$(CONFIG_KEVENT_NAIO) += kevent_naio.o +obj-$(CONFIG_KEVENT_AIO) += kevent_aio.o
kernel/kevent/kevent.c new file +251 -0
251 lines not on this page · raw message
kernel/kevent/kevent_user.c new file +1004 -0
1,004 lines not on this page · raw message
kernel/sys_ni.c +3 -0
@@ -122,6 +122,9 @@ cond_syscall(ppc_rtas);cond_syscall (sys_spu_run);cond_syscall (sys_spu_create); +cond_syscall (sys_kevent_get_events); +cond_syscall (sys_kevent_ctl); +/* mmu depending weak syscall entries */ cond_syscall (sys_mprotect);cond_syscall (sys_msync);
PATCH 2/2 kevent: poll/select() notifications. Timer notifications.
poll/select() notifications. Timer notifications.
This patch includes generic poll/select and timer notifications.
kevent_poll works simialr to epoll and has the same issues (callback is invoked not from internal state machine of the caller, but through process awake).
Timer notifications can be used for fine grained per-process time management, since interval timers are very inconvenient to use, and they are limited.
kernel/kevent/kevent_poll.c new file +220 -0
@@ -0,0 +1,220 @@ +/* +* kevent_poll.c +* +* 2006 Copyright (c) Evgeniy Polyakov <[email protected]> +* All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/file.h> +#include <linux/kevent.h> +#include <linux/poll.h> +#include <linux/fs.h> + +static kmem_cache_t *kevent_poll_container_cache; +static kmem_cache_t *kevent_poll_priv_cache; + +struct kevent_poll_ctl +{ +struct poll_table_struct pt; +struct kevent *k; +}; + +struct kevent_poll_wait_container +{ +struct list_head container_entry; +wait_queue_head_t *whead; +wait_queue_t wait; +struct kevent *k; +}; + +struct kevent_poll_private +{ +struct list_head container_list; +spinlock_t container_lock; +}; + +static int kevent_poll_enqueue (struct kevent *k); +static int kevent_poll_dequeue (struct kevent *k); +static int kevent_poll_callback (struct kevent *k); + +static int kevent_poll_wait_callback (wait_queue_t *wait, +unsigned mode,int sync,void *key) +{ +struct kevent_poll_wait_container *cont = + container_of(wait, struct kevent_poll_wait_container, wait); +struct kevent *k = cont->k; +struct file *file = k->st->origin; +u32 revents; + + revents = file->f_op->poll (file,NULL ); + +kevent_storage_ready (k->st,NULL , revents); + +return 0 ; +} + +static void kevent_poll_qproc (struct file *file,wait_queue_head_t *whead, +struct poll_table_struct *poll_table) +{ +struct kevent *k = + container_of(poll_table, struct kevent_poll_ctl, pt)->k; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *cont; +unsigned long flags; + + cont =kmem_cache_alloc (kevent_poll_container_cache,SLAB_KERNEL ); +if (!cont) { +kevent_break (k); +return ; + } + + cont->k = k; +init_waitqueue_func_entry (&cont->wait, kevent_poll_wait_callback); + cont->whead = whead; + +spin_lock_irqsave (&priv->container_lock, flags); +list_add_tail (&cont->container_entry, &priv->container_list); +spin_unlock_irqrestore (&priv->container_lock, flags); + +add_wait_queue (whead, &cont->wait); +} + +static int kevent_poll_enqueue (struct kevent *k) +{ +struct file *file; +int err, ready =0 ; +unsigned int revents; +struct kevent_poll_ctl ctl; +struct kevent_poll_private *priv; + + file =fget (k->event.id.raw[0 ]); +if (!file) +return -ENODEV ; + + err = -EINVAL ; +if (!file->f_op || !file->f_op->poll) + goto err_out_fput; + + err = -ENOMEM ; + priv =kmem_cache_alloc (kevent_poll_priv_cache,SLAB_KERNEL ); +if (!priv) + goto err_out_fput; + +spin_lock_init (&priv->container_lock); +INIT_LIST_HEAD (&priv->container_list); + + k->priv = priv; + + ctl.k = k; +init_poll_funcptr (&ctl.pt, &kevent_poll_qproc); + + err =kevent_storage_enqueue (&file->st, k); +if (err) + goto err_out_free; + + revents = file->f_op->poll (file, &ctl.pt); +if (revents & k->event.event) { + ready =1 ; +kevent_poll_dequeue (k); + } + +return ready; + +err_out_free: +kmem_cache_free (kevent_poll_priv_cache, priv); +err_out_fput: +fput (file); +return err; +} + +static int kevent_poll_dequeue (struct kevent *k) +{ +struct file *file = k->st->origin; +struct kevent_poll_private *priv = k->priv; +struct kevent_poll_wait_container *w, *n; +unsigned long flags; + +kevent_storage_dequeue (k->st, k); + +spin_lock_irqsave (&priv->container_lock, flags); +list_for_each_entry_safe (w, n, &priv->container_list, container_entry) { +list_del (&w->container_entry); +remove_wait_queue (w->whead, &w->wait); +kmem_cache_free (kevent_poll_container_cache, w); + } +spin_unlock_irqrestore (&priv->container_lock, flags); + +kmem_cache_free (kevent_poll_priv_cache, priv); + k->priv =NULL ; + +fput (file); + +return 0 ; +} + +static int kevent_poll_callback (struct kevent *k) +{ +struct file *file = k->st->origin; +unsigned int revents = file->f_op->poll (file,NULL ); +return (revents & k->event.event); +} + +static int __init kevent_poll_sys_init (void ) +{ +struct kevent_callbacks *pc = &kevent_registered_callbacks[KEVENT_POLL ]; + + kevent_poll_container_cache =kmem_cache_create ("kevent_poll_container_cache" , +sizeof (struct kevent_poll_wait_container ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_container_cache) { +printk (KERN_ERR "Failed to create kevent poll container cache.\n" ); +return -ENOMEM ; + } + + kevent_poll_priv_cache =kmem_cache_create ("kevent_poll_priv_cache" , +sizeof (struct kevent_poll_private ),0 ,0 ,NULL ,NULL ); +if (!kevent_poll_priv_cache) { +printk (KERN_ERR "Failed to create kevent poll private data cache.\n" ); +kmem_cache_destroy (kevent_poll_container_cache); + kevent_poll_container_cache =NULL ; +return -ENOMEM ; + } + + pc->enqueue = &kevent_poll_enqueue; + pc->dequeue = &kevent_poll_dequeue; + pc->callback = &kevent_poll_callback; + +printk (KERN_INFO "Kevent poll()/select() subsystem has been initialized.\n" ); +return 0 ; +} + +static struct lock_class_key kevent_poll_key; + +void kevent_poll_reinit (struct file *file) +{ +lockdep_set_class (&file->st.lock, &kevent_poll_key); +} + +static void __exit kevent_poll_sys_fini(void) +{ +kmem_cache_destroy (kevent_poll_priv_cache); +kmem_cache_destroy (kevent_poll_container_cache); +} + +module_init (kevent_poll_sys_init); +module_exit (kevent_poll_sys_fini);
kernel/kevent/kevent_timer.c new file +119 -0
119 lines not on this page · raw message