diff options
author | Davide Libenzi <davidel@xmailserver.org> | 2006-10-11 04:21:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-11 14:14:21 -0400 |
commit | b611967de4dc5c52049676c4369dcac622a7cdfe (patch) | |
tree | 8c19038c8bbaa4851dcb99bed33707deaf5170d1 | |
parent | 0f836e5fecf59d0d0353e9af11fd14a32a3001ae (diff) |
[PATCH] epoll_pwait()
Implement the epoll_pwait system call, that extend the event wait mechanism
with the same logic ppoll and pselect do. The definition of epoll_pwait
is:
int epoll_pwait(int epfd, struct epoll_event *events, int maxevents,
int timeout, const sigset_t *sigmask, size_t sigsetsize);
The difference between the vanilla epoll_wait and epoll_pwait is that the
latter allows the caller to specify a signal mask to be set while waiting
for events. Hence epoll_pwait will wait until either one monitored event,
or an unmasked signal happen. If sigmask is NULL, the epoll_pwait system
call will act exactly like epoll_wait. For the POSIX definition of
pselect, information is available here:
http://www.opengroup.org/onlinepubs/009695399/functions/select.html
Signed-off-by: Davide Libenzi <davidel@xmailserver.org>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Andi Kleen <ak@muc.de>
Cc: Michael Kerrisk <mtk-manpages@gmx.net>
Cc: Ulrich Drepper <drepper@redhat.com>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/i386/kernel/syscall_table.S | 1 | ||||
-rw-r--r-- | fs/eventpoll.c | 56 | ||||
-rw-r--r-- | include/asm-i386/unistd.h | 3 | ||||
-rw-r--r-- | include/linux/syscalls.h | 4 |
4 files changed, 60 insertions, 4 deletions
diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index 7e639f78b0b9..2697e9210e92 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S | |||
@@ -318,3 +318,4 @@ ENTRY(sys_call_table) | |||
318 | .long sys_vmsplice | 318 | .long sys_vmsplice |
319 | .long sys_move_pages | 319 | .long sys_move_pages |
320 | .long sys_getcpu | 320 | .long sys_getcpu |
321 | .long sys_epoll_pwait | ||
diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 557d5b614fae..ae228ec54e94 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c | |||
@@ -105,6 +105,8 @@ | |||
105 | /* Maximum msec timeout value storeable in a long int */ | 105 | /* Maximum msec timeout value storeable in a long int */ |
106 | #define EP_MAX_MSTIMEO min(1000ULL * MAX_SCHEDULE_TIMEOUT / HZ, (LONG_MAX - 999ULL) / HZ) | 106 | #define EP_MAX_MSTIMEO min(1000ULL * MAX_SCHEDULE_TIMEOUT / HZ, (LONG_MAX - 999ULL) / HZ) |
107 | 107 | ||
108 | #define EP_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) | ||
109 | |||
108 | 110 | ||
109 | struct epoll_filefd { | 111 | struct epoll_filefd { |
110 | struct file *file; | 112 | struct file *file; |
@@ -497,7 +499,7 @@ void eventpoll_release_file(struct file *file) | |||
497 | */ | 499 | */ |
498 | asmlinkage long sys_epoll_create(int size) | 500 | asmlinkage long sys_epoll_create(int size) |
499 | { | 501 | { |
500 | int error, fd; | 502 | int error, fd = -1; |
501 | struct eventpoll *ep; | 503 | struct eventpoll *ep; |
502 | struct inode *inode; | 504 | struct inode *inode; |
503 | struct file *file; | 505 | struct file *file; |
@@ -640,7 +642,6 @@ eexit_1: | |||
640 | return error; | 642 | return error; |
641 | } | 643 | } |
642 | 644 | ||
643 | #define MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) | ||
644 | 645 | ||
645 | /* | 646 | /* |
646 | * Implement the event wait interface for the eventpoll file. It is the kernel | 647 | * Implement the event wait interface for the eventpoll file. It is the kernel |
@@ -657,7 +658,7 @@ asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, | |||
657 | current, epfd, events, maxevents, timeout)); | 658 | current, epfd, events, maxevents, timeout)); |
658 | 659 | ||
659 | /* The maximum number of event must be greater than zero */ | 660 | /* The maximum number of event must be greater than zero */ |
660 | if (maxevents <= 0 || maxevents > MAX_EVENTS) | 661 | if (maxevents <= 0 || maxevents > EP_MAX_EVENTS) |
661 | return -EINVAL; | 662 | return -EINVAL; |
662 | 663 | ||
663 | /* Verify that the area passed by the user is writeable */ | 664 | /* Verify that the area passed by the user is writeable */ |
@@ -699,6 +700,55 @@ eexit_1: | |||
699 | } | 700 | } |
700 | 701 | ||
701 | 702 | ||
703 | #ifdef TIF_RESTORE_SIGMASK | ||
704 | |||
705 | /* | ||
706 | * Implement the event wait interface for the eventpoll file. It is the kernel | ||
707 | * part of the user space epoll_pwait(2). | ||
708 | */ | ||
709 | asmlinkage long sys_epoll_pwait(int epfd, struct epoll_event __user *events, | ||
710 | int maxevents, int timeout, const sigset_t __user *sigmask, | ||
711 | size_t sigsetsize) | ||
712 | { | ||
713 | int error; | ||
714 | sigset_t ksigmask, sigsaved; | ||
715 | |||
716 | /* | ||
717 | * If the caller wants a certain signal mask to be set during the wait, | ||
718 | * we apply it here. | ||
719 | */ | ||
720 | if (sigmask) { | ||
721 | if (sigsetsize != sizeof(sigset_t)) | ||
722 | return -EINVAL; | ||
723 | if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) | ||
724 | return -EFAULT; | ||
725 | sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP)); | ||
726 | sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); | ||
727 | } | ||
728 | |||
729 | error = sys_epoll_wait(epfd, events, maxevents, timeout); | ||
730 | |||
731 | /* | ||
732 | * If we changed the signal mask, we need to restore the original one. | ||
733 | * In case we've got a signal while waiting, we do not restore the | ||
734 | * signal mask yet, and we allow do_signal() to deliver the signal on | ||
735 | * the way back to userspace, before the signal mask is restored. | ||
736 | */ | ||
737 | if (sigmask) { | ||
738 | if (error == -EINTR) { | ||
739 | memcpy(¤t->saved_sigmask, &sigsaved, | ||
740 | sizeof(sigsaved)); | ||
741 | set_thread_flag(TIF_RESTORE_SIGMASK); | ||
742 | } else | ||
743 | sigprocmask(SIG_SETMASK, &sigsaved, NULL); | ||
744 | } | ||
745 | |||
746 | return error; | ||
747 | } | ||
748 | |||
749 | #endif /* #ifdef TIF_RESTORE_SIGMASK */ | ||
750 | |||
751 | |||
702 | /* | 752 | /* |
703 | * Creates the file descriptor to be used by the epoll interface. | 753 | * Creates the file descriptor to be used by the epoll interface. |
704 | */ | 754 | */ |
diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 3ca7ab963d7d..beeeaf6b054a 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h | |||
@@ -324,10 +324,11 @@ | |||
324 | #define __NR_vmsplice 316 | 324 | #define __NR_vmsplice 316 |
325 | #define __NR_move_pages 317 | 325 | #define __NR_move_pages 317 |
326 | #define __NR_getcpu 318 | 326 | #define __NR_getcpu 318 |
327 | #define __NR_epoll_pwait 319 | ||
327 | 328 | ||
328 | #ifdef __KERNEL__ | 329 | #ifdef __KERNEL__ |
329 | 330 | ||
330 | #define NR_syscalls 319 | 331 | #define NR_syscalls 320 |
331 | #include <linux/err.h> | 332 | #include <linux/err.h> |
332 | 333 | ||
333 | /* | 334 | /* |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index b0ace3fd7eb9..1912c6cbef55 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
@@ -431,6 +431,10 @@ asmlinkage long sys_epoll_ctl(int epfd, int op, int fd, | |||
431 | struct epoll_event __user *event); | 431 | struct epoll_event __user *event); |
432 | asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, | 432 | asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, |
433 | int maxevents, int timeout); | 433 | int maxevents, int timeout); |
434 | asmlinkage long sys_epoll_pwait(int epfd, struct epoll_event __user *events, | ||
435 | int maxevents, int timeout, | ||
436 | const sigset_t __user *sigmask, | ||
437 | size_t sigsetsize); | ||
434 | asmlinkage long sys_gethostname(char __user *name, int len); | 438 | asmlinkage long sys_gethostname(char __user *name, int len); |
435 | asmlinkage long sys_sethostname(char __user *name, int len); | 439 | asmlinkage long sys_sethostname(char __user *name, int len); |
436 | asmlinkage long sys_setdomainname(char __user *name, int len); | 440 | asmlinkage long sys_setdomainname(char __user *name, int len); |