Skip to contentSkip to conversation

kevent: core files.

RFC In review Evgeniy Polyakov 23 participants 182 messages Jul 9, 2006 – Aug 22, 2006 Page 1 of 2

Subsystems (8)
Networking Network drivers VFS TCP +4 more subsystems
lore
Share & tools
lore
#0 lore raw

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
index af56987..93e23ff 100644
@@ -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
index 5a92fed..534d516 100644
@@ -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
index de2ccc1..52f8642 100644
@@ -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
index f2cdbea..1f31f86 100644
@@ -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
index 0aff22b..352c34b 100644
@@ -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
new file mode 100644
index 0000000..bd891f0
@@ -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
index bd67a44..33d436e 100644
@@ -587,4 +587,8 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index df864a3..6135afc 100644
@@ -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
index f6ef00f..eb057ea 100644
@@ -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
new file mode 100644
index 0000000..7dcd651
@@ -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
index 5433195..dcbacf5 100644
@@ -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
#1 lore raw

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);
+}
#2 lore raw
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_cache be NULL? Note that you can use kmem_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
maintainer
#3 lore raw
In reply to Evgeniy Polyakov

From: 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 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).

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.

#4 lore raw
Quoted 13 lines · David Miller

From: Evgeniy Polyakov <[email protected]>
Date: Sun, 9 Jul 2006 17:24:46 +0400

Quoted 10 lines

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).

Quoted 8 lines
Quoted 1 line

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
#5 lore raw
In reply to David Miller
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 of
tpacket_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

#6 lore raw
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 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:

Quoted 3 lines

int kevent_getevents(int event_fd, struct ukevent *events,
int min_events, int max_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 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 of
tpacket_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
#7 lore raw
Quoted 7 lines
Quoted 4 lines

int kevent_getevents(int event_fd, struct ukevent *events,
int min_events, int max_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

#8 lore raw
Quoted 5 lines · Zach Brown
Quoted 5 lines
Quoted 4 lines

int kevent_getevents(int event_fd, struct ukevent *events,
int min_events, int max_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
#9 lore raw
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

#10 lore raw
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
#11 lore raw
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

#12 lore raw
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 ❖
#13 lore raw
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]>
#14 lore raw
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
#15 lore raw
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]>
#16 lore raw
In reply to Nicholas Miell
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 ❖
#17 lore raw
In reply to Ulrich Drepper
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
#18 lore raw
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

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.

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 ❖
#19 lore raw
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

#20 lore raw
In reply to Ulrich Drepper
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 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)

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
#21 lore raw
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.txt
#22 lore raw
Quoted 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
maintainer
#23 lore raw

From: 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.

#24 lore raw
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.txt
#25 lore raw
Quoted 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 ❖
maintainer
#26 lore raw

From: 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.

#27 lore raw
In reply to Evgeniy Polyakov
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
maintainer
#28 lore raw

From: 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.

#29 lore raw
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?

maintainer
#30 lore raw

From: 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?"

#31 lore raw
In reply to David Miller
Quoted 40 lines · David Miller

From: 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

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
#32 lore raw
In reply to 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

#33 lore raw

cover kevent: introduction.

In reply to Evgeniy Polyakov

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 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.

#34 lore raw

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
index dd63d47..0af988a 100644
@@ -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
index 5d4a7d1..e157ad4 100644
@@ -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
index fc1c8dd..a76e50d 100644
@@ -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
index 94387c9..9e61299 100644
@@ -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
new file mode 100644
index 0000000..bd891f0
@@ -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
index 008f04c..57e64de 100644
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index a099fc6..c550fcc 100644
@@ -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
index d62ec66..2d7a6dd 100644
@@ -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
new file mode 100644
index 0000000..7dcd651
@@ -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
index 6991bec..8843cca 100644
@@ -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);
#35 lore raw

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
index 5755d57..d4d2f5c 100644
@@ -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
index b467026..fc2b49d 100644
@@ -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
index 4307e76..9267873 100644
@@ -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
index 324b3ea..c43a153 100644
@@ -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_kio
 	return 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
index 0720bdd..5a1899b 100644
@@ -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
index 51fcfbc..9922373 100644
@@ -617,6 +617,16 @@ #endif
 			spin_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 sock 
 	if (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(struct
 		wake_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 soc
 		wake_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
index d1d7dec..2878c2a 100644
@@ -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
index 738dad9..f70d045 100644
@@ -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
index f6f39e8..ae4f23c 100644
@@ -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 @@ #endif
 	   	reqsk_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
index 923989d..a5d3ac8 100644
@@ -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,
#36 lore raw

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
new file mode 100644
index 0000000..4950e7c
@@ -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

#37 lore raw

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
index 6a0b9ad..a3ee530 100644
@@ -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
index fb4d322..9316551 100644
@@ -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
index c5ee9f0..d9210d4 100644
@@ -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
index 0131ba0..b649317 100644
@@ -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
index 0bf9f04..fdbd0ba 100644
@@ -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 defined CONFIG_KEVENT
+		kevent_storage_init(inode, &inode->st);
+#endif
 	}
 	return inode;
 }
 
 void destroy_inode(struct inode *inode) 
 {
+#if defined CONFIG_KEVENT_INODE || defined CONFIG_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
index 12dfdcf..f8dca72 100644
@@ -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
index 2561020..65eb438 100644
@@ -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_INOTIFY
 	struct 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_EPOLL
 	struct 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
index cc5dec7..0acc8db 100644
@@ -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(struc
 static 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

#38 lore raw
In reply to Evgeniy Polyakov

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]>
#39 lore raw
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
#40 lore raw
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]>
#41 lore raw
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
#42 lore raw
In reply to 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_REQUESTS PAGE_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

maintainer
#43 lore raw

From: 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_REQUESTS PAGE_SIZE/sizeof(struct kevent)

This is unused?

It is probably groundwork for the mmap() ring buffer... :)

#44 lore raw
Quoted 11 lines · David Miller

From: 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.

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_REQUESTS PAGE_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
#45 lore raw
In reply to Zach Brown
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		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.

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	0x4

I 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	0x1000

And 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_REQUESTS PAGE_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() in list.h but 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) 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; }) .

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_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.

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.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.

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 set LIST_POISON1? If so, please use list_del_init() and list_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_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.

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_num while holding the ready_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 then pr_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_head and lock into one struct (like struct sk_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
maintainer
#46 lore raw

From: 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.

#47 lore raw
Quoted 7 lines · David Miller

From: 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.

Ok, I will use that type.

Signature · 1 line
	Evgeniy Polyakov
#48 lore raw

cover kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov

Generic 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.

#49 lore raw

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
index dd63d47..0af988a 100644
@@ -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
index 5d4a7d1..e157ad4 100644
@@ -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
index fc1c8dd..a76e50d 100644
@@ -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
index 94387c9..9a0b581 100644
@@ -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
new file mode 100644
index 0000000..bd891f0
@@ -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
index 008f04c..143f3b5 100644
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index a099fc6..c550fcc 100644
@@ -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
index d62ec66..2d7a6dd 100644
@@ -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
new file mode 100644
index 0000000..d1ef9ba
@@ -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
index 6991bec..8843cca 100644
@@ -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);
#50 lore raw

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
index 6a0b9ad..a3ee530 100644
@@ -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
index fb4d322..9316551 100644
@@ -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
index c5ee9f0..d9210d4 100644
@@ -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
index 0131ba0..b649317 100644
@@ -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
index 0bf9f04..fdbd0ba 100644
@@ -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 defined CONFIG_KEVENT
+		kevent_storage_init(inode, &inode->st);
+#endif
 	}
 	return inode;
 }
 
 void destroy_inode(struct inode *inode) 
 {
+#if defined CONFIG_KEVENT_INODE || defined CONFIG_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
index 12dfdcf..f8dca72 100644
@@ -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
index 2561020..65eb438 100644
@@ -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_INOTIFY
 	struct 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_EPOLL
 	struct 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
index cc5dec7..0acc8db 100644
@@ -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(struc
 static 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

#51 lore raw

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
index 5755d57..9300678 100644
@@ -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
index b467026..fc2b49d 100644
@@ -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
index 4307e76..9267873 100644
@@ -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
index 324b3ea..c43a153 100644
@@ -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_kio
 	return 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
index 0720bdd..5a1899b 100644
@@ -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
index 51fcfbc..9922373 100644
@@ -617,6 +617,16 @@ #endif
 			spin_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 sock 
 	if (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(struct
 		wake_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 soc
 		wake_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
index d1d7dec..2878c2a 100644
@@ -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
index 738dad9..f70d045 100644
@@ -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
index f6f39e8..ae4f23c 100644
@@ -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 @@ #endif
 	   	reqsk_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
index 923989d..a5d3ac8 100644
@@ -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,
#52 lore raw

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
new file mode 100644
index 0000000..46cc8f0
@@ -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

#53 lore raw

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.c

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

Eric

#54 lore raw

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
#55 lore raw

Re: [take3 3/4] kevent: Network AIO, socket notifications.

In reply to Evgeniy Polyakov

kernel/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

#56 lore raw

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 in sock_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
#57 lore raw

Re: [take3 2/4] kevent: AIO, aio_sendfile() implementation.

In reply to Evgeniy Polyakov
Quoted 13 lines · Evgeniy Polyakov

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.

...

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

#58 lore raw

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.
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.

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.c code, wondering if its possible to make common
generic/helper routines in mpage.c and 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
#59 lore raw

Re: [take3 1/4] kevent: Core files.

In reply to Evgeniy Polyakov
Quoted 12 lines · Evgeniy Polyakov

Core files.

This patch includes core kevent files:
- userspace controlling
- kernelspace interfaces
- initialization
- notification state machines

kernel/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

#60 lore raw

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_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.

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
#61 lore raw

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_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.

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

#62 lore raw

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_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.

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 protect ready_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 is ep_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
maintainer
#63 lore raw

Re: [take3 1/4] kevent: Core files.

In reply to Evgeniy Polyakov

From: 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.

#64 lore raw

Re: [take3 0/4] kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov
Quoted 9 lines · Evgeniy Polyakov

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)

Also added optimisation aimed to help when a lot of kevents are being copied from userspace in one syscall.

Signature · 1 line
	Evgeniy Polyakov
#65 lore raw

cover kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov

Generic 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.

#66 lore raw

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
index dd63d47..0af988a 100644
@@ -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
index 5d4a7d1..e157ad4 100644
@@ -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
index fc1c8dd..a76e50d 100644
@@ -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
index 94387c9..9a0b581 100644
@@ -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
new file mode 100644
index 0000000..bd891f0
@@ -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
index 008f04c..143f3b5 100644
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index a099fc6..c550fcc 100644
@@ -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
index d62ec66..2d7a6dd 100644
@@ -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
new file mode 100644
index 0000000..d1ef9ba
@@ -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
index 6991bec..8843cca 100644
@@ -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);
#67 lore raw

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
index 6a0b9ad..a3ee530 100644
@@ -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
index fb4d322..9316551 100644
@@ -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
index c5ee9f0..d9210d4 100644
@@ -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
index 0131ba0..7f5b35f 100644
@@ -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
index 0bf9f04..fdbd0ba 100644
@@ -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 defined CONFIG_KEVENT
+		kevent_storage_init(inode, &inode->st);
+#endif
 	}
 	return inode;
 }
 
 void destroy_inode(struct inode *inode) 
 {
+#if defined CONFIG_KEVENT_INODE || defined CONFIG_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
index 12dfdcf..f8dca72 100644
@@ -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
index 2561020..65eb438 100644
@@ -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_INOTIFY
 	struct 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_EPOLL
 	struct 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
index cc5dec7..0acc8db 100644
@@ -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(struc
 static 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

#68 lore raw

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
index 5755d57..9300678 100644
@@ -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
index b467026..fc2b49d 100644
@@ -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
index 4307e76..9267873 100644
@@ -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
index 324b3ea..c43a153 100644
@@ -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_kio
 	return 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
index 0720bdd..5a1899b 100644
@@ -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
index 51fcfbc..138ce90 100644
@@ -617,6 +617,16 @@ #endif
 			spin_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 sock 
 	if (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(struct
 		wake_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 soc
 		wake_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
index d1d7dec..2878c2a 100644
@@ -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
index 738dad9..f70d045 100644
@@ -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
index f6f39e8..ae4f23c 100644
@@ -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 @@ #endif
 	   	reqsk_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
index 923989d..a5d3ac8 100644
@@ -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,
#69 lore raw

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
new file mode 100644
index 0000000..8a4f863
@@ -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

#70 lore raw

Re: [take4 1/4] kevent: Core files.

In reply to Evgeniy Polyakov
Quoted 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

#71 lore raw

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
#72 lore raw

cover kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov

Generic 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.

#73 lore raw

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
index dd63d47..0af988a 100644
@@ -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
index 5d4a7d1..e157ad4 100644
@@ -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
index fc1c8dd..a76e50d 100644
@@ -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
index 94387c9..9a0b581 100644
@@ -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
new file mode 100644
index 0000000..bd891f0
@@ -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
index 008f04c..143f3b5 100644
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index a099fc6..c550fcc 100644
@@ -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
index d62ec66..2d7a6dd 100644
@@ -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
new file mode 100644
index 0000000..d1ef9ba
@@ -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
index 6991bec..8843cca 100644
@@ -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);
#74 lore raw

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
index 6a0b9ad..a3ee530 100644
@@ -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
index fb4d322..9316551 100644
@@ -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
index c5ee9f0..d9210d4 100644
@@ -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
index 0131ba0..7f5b35f 100644
@@ -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
index 0bf9f04..fdbd0ba 100644
@@ -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 defined CONFIG_KEVENT
+		kevent_storage_init(inode, &inode->st);
+#endif
 	}
 	return inode;
 }
 
 void destroy_inode(struct inode *inode) 
 {
+#if defined CONFIG_KEVENT_INODE || defined CONFIG_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
index 12dfdcf..f8dca72 100644
@@ -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
index 2561020..65eb438 100644
@@ -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_INOTIFY
 	struct 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_EPOLL
 	struct 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
index cc5dec7..0acc8db 100644
@@ -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(struc
 static 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

#75 lore raw

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
index 5755d57..9300678 100644
@@ -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
index b467026..fc2b49d 100644
@@ -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
index 4307e76..9267873 100644
@@ -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
index 324b3ea..c43a153 100644
@@ -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_kio
 	return 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
index 0720bdd..5a1899b 100644
@@ -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
index 51fcfbc..138ce90 100644
@@ -617,6 +617,16 @@ #endif
 			spin_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 sock 
 	if (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(struct
 		wake_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 soc
 		wake_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
index d1d7dec..2878c2a 100644
@@ -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
index 738dad9..f70d045 100644
@@ -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
index f6f39e8..ae4f23c 100644
@@ -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 @@ #endif
 	   	reqsk_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
index 923989d..a5d3ac8 100644
@@ -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,
#76 lore raw

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
new file mode 100644
index 0000000..8a4f863
@@ -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

#77 lore raw

Re: [take5 3/4] kevent: Network AIO, socket notifications.

In reply to Evgeniy Polyakov
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
index 324b3ea..c43a153 100644

Why are you moving around SOCKET_I() and SOCK_INODE() ?

kernel/kevent/kevent_naio.c new file +0 -0
new file mode 100644
index 0000000..98c357f

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

#78 lore raw

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
index 324b3ea..c43a153 100644
Quoted 1 line

Why are you moving around SOCKET_I() and SOCK_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 -0
new file mode 100644
index 0000000..98c357f

Review

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 , function sock_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/fd is 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
#79 lore raw

Re: [take5 1/4] kevent: Core files.

In reply to Evgeniy Polyakov
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_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

#80 lore raw

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_ops was left static in net/socket.c.

I exported it. It just sneaked out of patchset.

Quoted 5 lines

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.

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
#81 lore raw

Re: [take5 0/4] kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov
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 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).

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

#82 lore raw

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 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).

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_sendfile implementation. 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_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.

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.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 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
maintainer
#83 lore raw

Re: [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_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.

What is wrong with using list_empty() as this indicator?

#84 lore raw

Re: [take5 0/4] kevent: Generic event handling mechanism.

Quoted 5 lines · David Miller
Quoted 3 lines

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.

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
#85 lore raw

Re: [take5 0/4] kevent: Generic event handling mechanism.

Quoted 10 lines · Evgeniy Polyakov
Quoted 5 lines · David Miller
Quoted 3 lines

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.

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.

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
maintainer
#86 lore raw

Re: [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 use list_del_init(), so list_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.

#87 lore raw

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 +0400

Quoted 3 lines

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.

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
#88 lore raw

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 ❖
maintainer
#89 lore raw

Re: [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.

#90 lore raw

Re: [take5 0/4] kevent: Generic event handling mechanism.

In reply to Ulrich Drepper
Quoted 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
#91 lore raw

Re: [take5 0/4] kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov
Quoted 10 lines · Evgeniy Polyakov
Quoted 5 lines · David Miller
Quoted 3 lines

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.

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.

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.

#92 lore raw

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_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.

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 for entry->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.

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 ;)

#93 lore raw

cover kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov

Generic 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.

#94 lore raw

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
index dd63d47..0af988a 100644
@@ -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
index 5d4a7d1..e157ad4 100644
@@ -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
index fc1c8dd..a76e50d 100644
@@ -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
index 94387c9..9a0b581 100644
@@ -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
new file mode 100644
index 0000000..bd891f0
@@ -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
index 008f04c..143f3b5 100644
@@ -597,4 +597,9 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index a099fc6..c550fcc 100644
@@ -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
index d62ec66..2d7a6dd 100644
@@ -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
new file mode 100644
index 0000000..d1ef9ba
@@ -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
index 6991bec..8843cca 100644
@@ -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);
#95 lore raw

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
index 5755d57..9300678 100644
@@ -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
index b467026..fc2b49d 100644
@@ -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
index 4307e76..9267873 100644
@@ -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
index 324b3ea..c43a153 100644
@@ -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_kio
 	return 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
index 0720bdd..5a1899b 100644
@@ -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
index 51fcfbc..138ce90 100644
@@ -617,6 +617,16 @@ #endif
 			spin_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 sock 
 	if (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(struct
 		wake_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 soc
 		wake_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
index d1d7dec..2878c2a 100644
@@ -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
index 738dad9..f70d045 100644
@@ -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
index f6f39e8..ae4f23c 100644
@@ -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 @@ #endif
 	   	reqsk_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
index 923989d..a5d3ac8 100644
@@ -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,
#96 lore raw

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
new file mode 100644
index 0000000..8a4f863
@@ -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

#97 lore raw

Re: [take6 1/3] kevent: Core files.

In reply to Evgeniy Polyakov
Quoted 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 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).

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();
}
#98 lore raw

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
maintainer
#99 lore raw

Re: [take6 1/3] kevent: Core files.

In reply to Stephen Hemminger

From: 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 :-)

#100 lore raw

Re: [take6 1/3] kevent: Core files.

In reply to Evgeniy Polyakov
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 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
new file mode 100644
index 0000000..7b6374b
@@ -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;
+}
#101 lore raw

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 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 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

0x0800 got 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 __user tag, 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 _rcu variant 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_t handlers 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
new file mode 100644
index 0000000..7b6374b
@@ -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 with i'??

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 -EFOO return 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
maintainer
#102 lore raw

Re: [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.

#103 lore raw

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 this
list_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
#104 lore raw

Re: [take6 1/3] kevent: Core files.

In reply to Evgeniy Polyakov
Quoted 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 __user tag, 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 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 ;)

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_0 and struct ukevent_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 with i'??

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 -EFOO return 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.

#105 lore raw

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_head has been detached you can detach it with list_del_init() and then use list_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_0 and struct ukevent_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_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 ;)

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 with i'??

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 -EFOO return 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
#106 lore raw

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 ;)

#107 lore raw

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
#108 lore raw

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.

#109 lore raw

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
#110 lore raw

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?

#111 lore raw

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
#112 lore raw

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.

#113 lore raw

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
#114 lore raw

Re: [take6 1/3] kevent: Core files.

In reply to Evgeniy Polyakov
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.

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 ❖
#115 lore raw

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
maintainer
#116 lore raw

Re: [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.

#117 lore raw

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 +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.

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
#118 lore raw

cover kevent: generic event handling mechanism.

In reply to Andrew Morton

Hello.

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
#119 lore raw

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
index dd63d47..091ff42 100644
@@ -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
index 5d4a7d1..b2af4a8 100644
@@ -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
index fc1c8dd..c9dde13 100644
@@ -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
index 94387c9..61363e0 100644
@@ -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
new file mode 100644
index 0000000..a38575d
@@ -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
index 008f04c..8609910 100644
@@ -597,4 +597,7 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index a099fc6..c550fcc 100644
@@ -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
index d62ec66..2d7a6dd 100644
@@ -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
new file mode 100644
index 0000000..d1ef9ba
@@ -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
index 6991bec..8d3769b 100644
@@ -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
#120 lore raw

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
maintainer
#121 lore raw

Re: [take6 0/3] kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov

From: 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.

#122 lore raw

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 +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.

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
maintainer
#123 lore raw

Re: [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

#124 lore raw

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 +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

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
#125 lore raw

cover kevent: Generic event handling mechanism.

In reply to Evgeniy Polyakov

Generic 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.

#126 lore raw

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
index dd63d47..091ff42 100644
@@ -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
index 5d4a7d1..b2af4a8 100644
@@ -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
index fc1c8dd..c9dde13 100644
@@ -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
index 94387c9..61363e0 100644
@@ -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
new file mode 100644
index 0000000..a38575d
@@ -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
index 008f04c..8609910 100644
@@ -597,4 +597,7 @@ asmlinkage long sys_get_robust_list(int 
 asmlinkage 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
index a099fc6..c550fcc 100644
@@ -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
index d62ec66..2d7a6dd 100644
@@ -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
new file mode 100644
index 0000000..d1ef9ba
@@ -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
index 6991bec..8d3769b 100644
@@ -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);
#127 lore raw

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
new file mode 100644
index 0000000..8a4f863
@@ -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