From: <ljs...@us...> - 2012-06-06 22:53:00
|
Revision: 802 http://cadcdev.svn.sourceforge.net/cadcdev/?rev=802&view=rev Author: ljsebald Date: 2012-06-06 22:52:53 +0000 (Wed, 06 Jun 2012) Log Message: ----------- Add poll() and its support work in the fs and network stuff. Modified Paths: -------------- kos/include/kos/fs.h kos/include/kos/fs_socket.h kos/kernel/fs/fs_socket.c kos/kernel/libc/koslib/Makefile kos/kernel/net/net_tcp.c kos/kernel/net/net_udp.c Added Paths: ----------- kos/include/poll.h kos/kernel/libc/koslib/poll.c Modified: kos/include/kos/fs.h =================================================================== --- kos/include/kos/fs.h 2012-06-06 22:49:14 UTC (rev 801) +++ kos/include/kos/fs.h 2012-06-06 22:52:53 UTC (rev 802) @@ -167,6 +167,9 @@ /** \brief Manipulate file control flags on the given file. */ int (*fcntl)(void *fd, int cmd, va_list ap); + + /** \brief Check if an event is pending on the given file. */ + short (*poll)(void *fd, short events); } vfs_handler_t; /** \brief The number of distinct file descriptors that can be in use at a Modified: kos/include/kos/fs_socket.h =================================================================== --- kos/include/kos/fs_socket.h 2012-06-06 22:49:14 UTC (rev 801) +++ kos/include/kos/fs_socket.h 2012-06-06 22:52:53 UTC (rev 802) @@ -320,6 +320,20 @@ \retval -1 On error (generally, set errno appropriately). */ int (*fcntl)(net_socket_t *s, int cmd, va_list ap); + + /** \brief Poll for events. + + This function should check the given socket for any events that may have + already occured that are specified. This is used to back the ::poll() + system call. This function should not block to wait for any events. This + function may be called in an interrupt. + + \param s The socket to poll. + \param events The events to check for. + \retval A mask of any of the events specified that are + currently true in the socket. 0 if none are true. + */ + short (*poll)(net_socket_t *s, short events); } fs_socket_proto_t; /** \brief Initializer for the entry field in the fs_socket_proto_t struct. */ Added: kos/include/poll.h =================================================================== --- kos/include/poll.h (rev 0) +++ kos/include/poll.h 2012-06-06 22:52:53 UTC (rev 802) @@ -0,0 +1,85 @@ +/* KallistiOS ##version## + + poll.h + Copyright (C) 2012 Lawrence Sebald +*/ + +/** \file poll.h + \brief Definitions for the poll() function. + + This file contains the definitions needed for using the poll() function, as + directed by the POSIX 2008 standard (aka The Open Group Base Specifications + Issue 7). Currently the functionality defined herein only works for sockets, + and that is likely how it will stay for some time. + + The poll() function works quite similarly to the select() function that it + is quite likely that you'd be more familiar with. + + \author Lawrence Sebald +*/ + +#ifndef __POLL_H +#define __POLL_H + +#include <sys/cdefs.h> +#include <sys/types.h> + +__BEGIN_DECLS + +/** \brief Type representing a number of file descriptors. */ +typedef __uint32_t nfds_t; + +/** \brief Structure representing a single file descriptor used by poll(). + \headerfile poll.h +*/ +struct pollfd { + int fd; /**< \brief The file descriptor in question. */ + short events; /**< \brief Events to poll for on input. */ + short revents; /**< \brief Events signalled for output. */ +}; + +/** \defgroup poll_events Events for the poll() function + + These are the events that can be set in the events or revents fields of the + struct pollfd. + + @{ +*/ +#define POLLRDNORM (1 << 0) /**< \brief Normal data may be read */ +#define POLLRDBAND (1 << 1) /**< \brief Priority data may be read */ +#define POLLPRI (1 << 2) /**< \brief High-priority data may be read */ +#define POLLOUT (1 << 3) /**< \brief Normal data may be written */ +#define POLLWRNORM POLLOUT /**< \brief Normal data may be written */ +#define POLLWRBAND (1 << 4) /**< \brief Priority data may be written */ +#define POLLERR (1 << 5) /**< \brief Error has occurred (revents only) */ +#define POLLHUP (1 << 6) /**< \brief Peer disconnected (revents only) */ +#define POLLNVAL (1 << 7) /**< \brief Invalid fd (revents only) */ + +/** \brief Data other than high-priority data may be read */ +#define POLLIN (POLLRDNORM | POLLRDBAND) +/** @} */ + +/** \brief Poll a group of file descriptors for activity. + + This function will poll a group of file descriptors to check for the events + specified on them. The function shall block for the specified period of time + (in milliseconds) waiting for an event to occur. The function shall return + as soon as at least one fd matches the events specified (or one of the error + conditions), or when timeout expires. + + \param fds The file descriptors to check, and what events to look + for on each. + \param nfds Number of elements in fds. + \param timeout Maximum amount of time to block, in milliseconds. Pass + 0 to ensure the function does not block and -1 to block + for an "infinite" amount of time, until an event occurs. + \return -1 on error (sets errno as appropriate), or the number + of file descriptors that matched the event flags before + the function returns. + \sa poll_events +*/ +int poll(struct pollfd fds[], nfds_t nfds, int timeout); + +__END_DECLS + +#endif /* !POLL_H */ Modified: kos/kernel/fs/fs_socket.c =================================================================== --- kos/kernel/fs/fs_socket.c 2012-06-06 22:49:14 UTC (rev 801) +++ kos/kernel/fs/fs_socket.c 2012-06-06 22:52:53 UTC (rev 802) @@ -79,6 +79,11 @@ return sock->protocol->fcntl(sock, cmd, ap); } +static short fs_socket_poll(void *hnd, short events) { + net_socket_t *sock = (net_socket_t *)hnd; + return sock->protocol->poll(sock, events); +} + /* VFS handler */ static vfs_handler_t vh = { /* Name handler */ @@ -109,7 +114,8 @@ NULL, /* stat */ NULL, /* mkdir */ NULL, /* rmdir */ - fs_socket_fcntl /* fcntl */ + fs_socket_fcntl, /* fcntl */ + fs_socket_poll /* poll */ }; /* Have we been initialized? */ Modified: kos/kernel/libc/koslib/Makefile =================================================================== --- kos/kernel/libc/koslib/Makefile 2012-06-06 22:49:14 UTC (rev 801) +++ kos/kernel/libc/koslib/Makefile 2012-06-06 22:52:53 UTC (rev 802) @@ -12,6 +12,6 @@ opendir.o readdir.o closedir.o rewinddir.o scandir.o seekdir.o \ telldir.o usleep.o inet_addr.o realpath.o getcwd.o chdir.o mkdir.o \ creat.o sleep.o rmdir.o rename.o inet_pton.o inet_ntop.o \ - inet_ntoa.o inet_aton.o + inet_ntoa.o inet_aton.o poll.o include $(KOS_BASE)/Makefile.prefab Added: kos/kernel/libc/koslib/poll.c =================================================================== --- kos/kernel/libc/koslib/poll.c (rev 0) +++ kos/kernel/libc/koslib/poll.c 2012-06-06 22:52:53 UTC (rev 802) @@ -0,0 +1,158 @@ +/* KallistiOS ##version## + + poll.c + Copyright (C) 2012 Lawrence Sebald + +*/ + +#include <poll.h> +#include <errno.h> +#include <sys/queue.h> + +#include <arch/irq.h> +#include <kos/fs.h> +#include <kos/mutex.h> +#include <kos/cond.h> + +struct poll_int { + LIST_ENTRY(poll_int) entry; + struct pollfd *fds; + nfds_t nfds; + int nmatched; + condvar_t cv; +}; + +LIST_HEAD(polllist, poll_int) poll_list; + +static mutex_t mutex = MUTEX_INITIALIZER; + +void __poll_event_trigger(int fd, short event) { + struct poll_int *i; + nfds_t j; + int gotone = 0; + short mask; + + if(irq_inside_int()) { + if(mutex_trylock(&mutex)) + /* XXXX: Uhh... this is bad... */ + return; + } + else { + mutex_lock(&mutex); + } + + /* Look through the list of poll fds for any that match */ + LIST_FOREACH(i, &poll_list, entry) { + for(j = 0; j < i->nfds; ++j) { + if(i->fds[j].fd == fd) { + mask = i->fds[j].events | POLLERR | POLLHUP | POLLNVAL; + if(event & mask) { + i->fds[j].revents |= event & mask; + ++i->nmatched; + gotone = 1; + } + } + } + + /* If we got any events, signal the waiting thread to wake it up. */ + if(gotone) { + cond_signal(&i->cv); + gotone = 0; + } + } + + mutex_unlock(&mutex); +} + +int poll(struct pollfd fds[], nfds_t nfds, int timeout) { + struct poll_int p = { { 0 }, fds, nfds, 0, COND_INITIALIZER }; + int tmp; + nfds_t i; + vfs_handler_t *hndl; + void *hnd; + + if(irq_inside_int()) { + if(mutex_trylock(&mutex)) { + errno = EAGAIN; + return -1; + } + } + else { + mutex_lock(&mutex); + } + + /* Check if any of the fds already match */ + for(i = 0; i < nfds; ++i) { + hndl = fs_get_handler(fds[i].fd); + hnd = fs_get_handle(fds[i].fd); + fds[i].revents = 0; + + /* If we didn't get one of these, then assume its a bad fd. */ + if(!hndl || !hnd) { + fds[i].revents = POLLNVAL; + ++p.nmatched; + continue; + } + + if(!hndl->poll) { + /* Assume its a regular file if there's no poll method in the + handler. */ + if(fds[i].events & (POLLRDNORM | POLLWRNORM)) { + fds[i].revents |= (POLLRDNORM | POLLWRNORM) & fds[i].events; + ++p.nmatched; + } + } + else { + if((fds[i].revents = hndl->poll(hnd, fds[i].events))) { + ++p.nmatched; + } + } + } + + /* If the user specified a 0 timeout, or we've already matched something, + bail out now. */ + if(p.nmatched || !timeout) { + mutex_unlock(&mutex); + return p.nmatched; + } + + /* We can't actually wait while we're in an interrupt, so if we got this far + it is an error. */ + if(irq_inside_int()) { + mutex_unlock(&mutex); + errno = EPERM; + return -1; + } + + /* Map to the value used by cond_wait_timed() */ + if(timeout == -1) + timeout = 0; + + /* Add this instance to the list */ + LIST_INSERT_HEAD(&poll_list, &p, entry); + + tmp = errno; + if(cond_wait_timed(&p.cv, &mutex, timeout)) { + if(errno == EAGAIN) { + errno = tmp; + tmp = 0; + goto out; + } + else { + /* The mutex won't be locked if errno != EAGAIN */ + mutex_lock(&mutex); + errno = EINTR; + tmp = -1; + goto out; + } + } + + tmp = p.nmatched; + +out: + /* Remove this instance from the list */ + LIST_REMOVE(&p, entry); + + mutex_unlock(&mutex); + return tmp; +} Modified: kos/kernel/net/net_tcp.c =================================================================== --- kos/kernel/net/net_tcp.c 2012-06-06 22:49:14 UTC (rev 801) +++ kos/kernel/net/net_tcp.c 2012-06-06 22:52:53 UTC (rev 802) @@ -9,6 +9,7 @@ #include <stdlib.h> #include <string.h> #include <stdint.h> +#include <poll.h> #include <sys/socket.h> #include <kos/fs.h> @@ -1916,6 +1917,82 @@ return rv; } +static short net_tcp_poll(net_socket_t *hnd, short events) { + struct tcp_sock *sock; + short rv = 0; + + if(irq_inside_int()) { + if(rwsem_read_trylock(tcp_sem)) { + return 0; + } + } + else { + rwsem_read_lock(tcp_sem); + } + + /* Lock the mutex on the socket itself first. We need to pull some data from + it that doesn't affect the rest of the list, so let's start there... */ + if(!(sock = (struct tcp_sock *)hnd->data)) { + rwsem_read_unlock(tcp_sem); + return POLLNVAL; + } + + if(irq_inside_int()) { + if(mutex_trylock(sock->mutex)) { + rwsem_read_unlock(tcp_sem); + return 0; + } + } + else { + mutex_lock(sock->mutex); + } + + switch(sock->state) { + case TCP_STATE_LISTEN: + if(sock->listen.count) + rv = POLLRDNORM; + break; + + case TCP_STATE_CLOSED | TCP_STATE_RESET: + rv = POLLHUP; + + if(sock->data.rcvbuf_cur_sz) + rv |= POLLRDNORM; + break; + + case TCP_STATE_CLOSED: + case TCP_STATE_SYN_SENT: + break; + + case TCP_STATE_CLOSE_WAIT: + if(sock->data.sndbuf_cur_sz < sock->sndbuf_sz) + rv |= POLLWRNORM | POLLWRBAND; + /* Fall through... */ + + case TCP_STATE_TIME_WAIT: + case TCP_STATE_CLOSING: + rv = POLLRDNORM; + break; + + case TCP_STATE_SYN_RECEIVED: + case TCP_STATE_ESTABLISHED: + if(sock->data.sndbuf_cur_sz < sock->sndbuf_sz) + rv |= POLLWRNORM | POLLWRBAND; + /* Fall through... */ + + case TCP_STATE_FIN_WAIT_1: + case TCP_STATE_FIN_WAIT_2: + case TCP_STATE_LAST_ACK: + if(sock->data.rcvbuf_cur_sz) + rv |= POLLRDNORM; + break; + } + + mutex_unlock(sock->mutex); + rwsem_read_unlock(tcp_sem); + return rv & (events | POLLHUP | POLLERR); +} + static void tcp_rst(netif_t *net, const struct in6_addr *src, const struct in6_addr *dst, uint16_t src_port, uint16_t dst_port, uint16_t flags, uint32_t seq, @@ -2209,6 +2286,8 @@ return NULL; } +extern void __poll_event_trigger(int fd, short event); + /* This function is basically a direct implementation of the first two and a half steps of the SEGMENT ARRIVES event processing defined in RFC 793 on pages 65 and 66. There are a few parts that are omitted and some are put off @@ -2218,7 +2297,7 @@ struct tcp_sock *s, uint16_t flags, int size) { int j = 0; int end_of_opts; - uint16_t mss; + uint16_t mss = 576; /* Incoming segments with a RST should be ignored */ if(flags & TCP_FLAG_RST) @@ -2242,7 +2321,6 @@ break; case TCP_OPT_MSS: - if(j + 4 > end_of_opts || tcp->options[j + 1] != 4) return -1; @@ -2299,6 +2377,7 @@ s->listen.tail = 0; /* Signal the condvar, in case anyone's waiting */ + __poll_event_trigger(s->sock, POLLRDNORM); cond_signal(s->listen.cv); /* We're done, return success. */ @@ -2332,6 +2411,7 @@ if(flags & TCP_FLAG_RST) { if(gotack) { s->state = TCP_STATE_CLOSED | TCP_STATE_RESET; + __poll_event_trigger(s->sock, POLLHUP); cond_signal(s->data.recv_cv); cond_signal(s->data.send_cv); return 0; @@ -2388,12 +2468,14 @@ if(SEQ_GT(ack, s->data.snd.iss)) { s->state = TCP_STATE_ESTABLISHED; tcp_send_ack(s); + __poll_event_trigger(s->sock, POLLWRNORM | POLLWRBAND); cond_signal(s->data.send_cv); } } else { s->state = TCP_STATE_SYN_RECEIVED; tcp_send_syn(s, 1); + __poll_event_trigger(s->sock, POLLWRNORM | POLLWRBAND); cond_signal(s->data.send_cv); } } @@ -2463,6 +2545,7 @@ } else { s->state = TCP_STATE_RESET | TCP_STATE_CLOSED; + __poll_event_trigger(s->sock, POLLHUP); cond_signal(s->data.recv_cv); cond_signal(s->data.send_cv); return 0; @@ -2501,6 +2584,7 @@ s->data.sndbuf_acked += (int32_t)(ack - s->data.snd.una - acksyn); s->data.sndbuf_cur_sz -= (int32_t)(ack - s->data.snd.una - acksyn); s->data.snd.una = ack; + __poll_event_trigger(s->sock, POLLWRNORM | POLLWRBAND); cond_signal(s->data.send_cv); if(s->data.sndbuf_acked >= s->sndbuf_sz) @@ -2600,6 +2684,7 @@ } /* Signal any waiting thread and send an ack for what we read */ + __poll_event_trigger(s->sock, POLLRDNORM); cond_signal(s->data.recv_cv); tcp_send_ack(s); } @@ -2616,6 +2701,7 @@ /* ACK the FIN */ ++s->data.rcv.nxt; tcp_send_ack(s); + __poll_event_trigger(s->sock, POLLRDNORM); cond_signal(s->data.recv_cv); /* Do the various processing that needs to be done based on our state */ @@ -2874,7 +2960,8 @@ net_tcp_input, /* input */ net_tcp_getsockopt, /* getsockopt */ net_tcp_setsockopt, /* setsockopt */ - net_tcp_fcntl /* fcntl */ + net_tcp_fcntl, /* fcntl */ + net_tcp_poll /* poll */ }; int net_tcp_init() { Modified: kos/kernel/net/net_udp.c =================================================================== --- kos/kernel/net/net_udp.c 2012-06-06 22:49:14 UTC (rev 801) +++ kos/kernel/net/net_udp.c 2012-06-06 22:52:53 UTC (rev 802) @@ -9,6 +9,7 @@ #include <string.h> #include <stdlib.h> #include <errno.h> +#include <poll.h> #include <arpa/inet.h> #include <kos/net.h> #include <kos/mutex.h> @@ -50,6 +51,7 @@ uint32 flags; int domain; int hop_limit; + file_t sock; struct udp_pkt_queue packets; }; @@ -184,6 +186,8 @@ udpsock->local_addr.sin6_port = htons(port); } + udpsock->sock = hnd->fd; + mutex_unlock(udp_mutex); return 0; @@ -867,6 +871,33 @@ return rv; } +static short net_udp_poll(net_socket_t *hnd, short events) { + struct udp_sock *sock; + short rv = POLLWRNORM; + + if(irq_inside_int()) { + if(mutex_trylock(udp_mutex) == -1) + return 0; + } + else { + mutex_lock(udp_mutex); + } + + if(!(sock = (struct udp_sock *)hnd->data)) { + mutex_unlock(udp_mutex); + return POLLNVAL; + } + + if(!TAILQ_EMPTY(&sock->packets)) + rv |= POLLRDNORM; + + mutex_unlock(udp_mutex); + + return rv & events; +} + +extern void __poll_event_trigger(int fd, short event); + static int net_udp_input4(netif_t *src, const ip_hdr_t *ip, const uint8 *data, int size) { udp_hdr_t *hdr = (udp_hdr_t *)data; @@ -947,6 +978,7 @@ TAILQ_INSERT_TAIL(&sock->packets, pkt, pkt_queue); ++udp_stats.pkt_recv; + __poll_event_trigger(sock->sock, POLLRDNORM); genwait_wake_one(sock); mutex_unlock(udp_mutex); @@ -1039,6 +1071,7 @@ TAILQ_INSERT_TAIL(&sock->packets, pkt, pkt_queue); ++udp_stats.pkt_recv; + __poll_event_trigger(sock->sock, POLLRDNORM); genwait_wake_one(sock); mutex_unlock(udp_mutex); @@ -1164,7 +1197,8 @@ net_udp_input, net_udp_getsockopt, net_udp_setsockopt, - net_udp_fcntl + net_udp_fcntl, + net_udp_poll }; int net_udp_init() { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |