Adding select support for basic socket udp receive.
[akaros.git] / kern / src / socket.c
1 /*
2  * Copyright (c) 2011 The Regents of the University of California
3  * David Zhu <yuzhu@cs.berkeley.edu>
4  * See LICENSE for details.
5  * 
6  * Socket layer on top of TCP abstraction. Similar to the BSD implementation.
7  *
8  */
9 #include <ros/common.h>
10 #include <socket.h>
11 #include <vfs.h>
12 #include <time.h>
13 #include <kref.h>
14 #include <syscall.h>
15 #include <sys/uio.h>
16 #include <mbuf.h>
17 #include <ros/errno.h>
18 #include <net.h>
19 #include <net/udp.h>
20 #include <net/pbuf.h>
21 #include <umem.h>
22 #include <kthread.h>
23 #include <bitmask.h>
24 /*
25  *TODO: Figure out which socket.h is used where
26  *There are several socket.h in kern, and a couple more in glibc. Perhaps the glibc ones
27  *should grab from here..
28  */
29
30 struct kmem_cache *sock_kcache;
31 struct kmem_cache *mbuf_kcache;
32 struct kmem_cache *udp_pcb_kcache;
33 // file ops needed to support read/write on socket fd
34 static struct file_operations socket_op = {
35         0,
36         0,//soo_read,
37         0,//soo_write,
38         0,
39         0,
40         0,
41         0,
42         0,
43         0,
44         0,//soo_poll,
45         0,
46         0,
47         0, // sendpage might apply here
48         0,
49 };
50 static struct socket* getsocket(struct proc *p, int fd){
51         /* look up fd -> file */
52         struct file *so_file = get_file_from_fd(&(p->open_files), fd);
53
54         /* get socket and verify its type */
55         if (so_file == NULL){
56                 printd("getsocket() fd -> null file: fd %d\n", fd);
57                 return NULL;
58         }
59         if (so_file->f_op != &socket_op) {
60                 set_errno(ENOTSOCK);
61                 printd("fd %d maps to non-socket file\n");
62                 return NULL;
63         } else
64                 return (struct socket*) so_file->f_privdata;
65 }
66
67 struct socket* alloc_sock(int socket_family, int socket_type, int protocol){
68         struct socket *newsock = kmem_cache_alloc(sock_kcache, 0);
69         assert(newsock);
70
71         newsock->so_family = socket_family;
72         newsock->so_type = socket_type;
73         newsock->so_protocol = protocol;
74         newsock->so_state = SS_ISDISCONNECTED;
75         pbuf_head_init(&newsock->recv_buff);
76         pbuf_head_init(&newsock->send_buff);
77         init_sem(&newsock->sem, 0);
78         spinlock_init(&newsock->waiter_lock);
79         LIST_INIT(&newsock->waiters);
80         if (socket_type == SOCK_DGRAM){
81                 newsock->so_pcb = udp_new();
82                 /* back link */
83                 ((struct udp_pcb*) (newsock->so_pcb))->pcbsock = newsock;
84         }
85         return newsock;
86
87 }
88 // TODO: refactor vfs so we can allocate fd and do the basic initialization
89 struct file *alloc_socket_file(struct socket* sock) {
90         struct file *file = alloc_file();
91         if (file == NULL) return 0;
92
93         // Linux fakes a dentry and an inode for socks, see socket.c : sock_alloc_file
94         file->f_dentry = NULL; // This might break things?
95         file->f_vfsmnt = 0;
96         file->f_flags = 0;
97
98         file->f_mode = S_IRUSR | S_IWUSR; // both read and write for socket files
99
100         file->f_pos = 0;
101         file->f_uid = 0;
102         file->f_gid = 0;
103         file->f_error = 0;
104
105         file->f_op = &socket_op;
106         file->f_privdata = sock;
107         file->f_mapping = 0;
108         return file;
109 }
110
111 void socket_init(){
112         
113         /* allocate buf for socket */
114         sock_kcache = kmem_cache_create("socket", sizeof(struct socket),
115                                                                         __alignof__(struct socket), 0, 0, 0);
116         udp_pcb_kcache = kmem_cache_create("udppcb", sizeof(struct udp_pcb), 
117                                                                         __alignof__(struct udp_pcb), 0, 0, 0);
118
119         pbuf_init();
120
121 }
122
123 intreg_t sys_socket(struct proc *p, int socket_family, int socket_type, int protocol){
124         //check validity of params
125         if (socket_family !=AF_INET && socket_type!=SOCK_DGRAM)
126                 return 0;
127         struct socket *sock = alloc_sock(socket_family, socket_type, protocol);
128         struct file *file = alloc_socket_file(sock);
129         
130         if (file == NULL) return -1;
131         int fd = insert_file(&p->open_files, file, 0);
132         if (fd < 0) {
133                 warn("File insertion for socket open failed");
134                 return -1;
135         }
136         kref_put(&file->f_kref);
137         printk("Socket open, res = %d\n", fd);
138         return fd;
139 }
140 intreg_t send_iov(struct socket* sock, struct iovec* iov, int flags){
141         // COPY_COUNT: for each iov, copy into mbuf, and send
142         // should not copy here, copy in the protocol..
143         // should be esomething like this sock->so_proto->pr_send(sock, iov, flags);
144         // make it datagram specific for now...
145         send_datagram(sock, iov, flags);
146         // finally time to check for validity of UA, in the protocol send
147         return 0;       
148 }
149 /*TODO: iov support currently broken */
150 int send_datagram(struct socket* sock, struct iovec* iov, int flags){
151         // is this a connection oriented protocol? 
152         struct pbuf *prev = NULL;
153         struct pbuf *curr = NULL;
154         if (sock->so_type == SOCK_STREAM){
155                 set_errno(ENOTCONN);
156                 return -1;
157         }
158         
159         // possible sock locks needed
160         if ((sock->so_state & SS_ISCONNECTED) == 0){
161                 set_errno(EINVAL);
162                 return -1;
163         }
164     // pbuf_ref needs to map in the user ref
165         for (int i = 0; i< sizeof(iov) / sizeof (struct iovec); i++){
166                 prev = curr;
167                 curr = pbuf_alloc(PBUF_TRANSPORT, iov[i].iov_len, PBUF_REF);
168                 if (prev!=NULL) pbuf_chain(prev, curr);
169         }
170         // struct pbuf* pb = pbuf_alloc(PBUF_TRANSPORT, PBUF_REF);
171         udp_send(sock->so_pcb, prev);
172         return 0;
173         
174 }
175
176 /* sys_sendto can send SOCK_DGRAM and eventually SOCK_STREAM 
177  * SOCK_DGRAM uses PBUF_REF since UDP does not need to wait for ack
178  * SOCK_STREAM uses PBUF_
179  *
180  */
181 intreg_t sys_sendto(struct proc *p_proc, int fd, const void *buffer, size_t length, 
182                         int flags, const struct sockaddr *dest_addr, socklen_t dest_len){
183         // look up the socket
184         struct socket* sock = getsocket(p_proc, fd);
185         int error;
186         struct sockaddr_in *in_addr;
187         uint16_t r_port;
188         if (sock == NULL) {
189                 set_errno(EBADF);
190                 return -1;      
191         }
192         if (sock->so_type == SOCK_DGRAM){
193                 in_addr = (struct sockaddr_in *)dest_addr;
194                 struct pbuf* buf = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_REF);
195                 if (buf != NULL)
196                         buf->payload = (void*)buffer;
197                 else 
198                         warn("pbuf alloc failed \n");
199                 // potentially unsafe cast to udp_pcb 
200                 return udp_sendto((struct udp_pcb*) sock->so_pcb, buf, &in_addr->sin_addr, in_addr->sin_port);
201         }
202
203         return -1;
204   //TODO: support for sendmsg and iovectors? Let's get the basis working first!
205         #if 0 
206         // use iovector to handle sendmsg calls too, and potentially scatter-gather
207         struct msghdr msg;
208         struct iovec iov;
209         struct uio auio;
210         
211         // checking for permission only when you are sending it
212         // potential bug TOCTOU, especially with async calls
213                 
214     msg.msg_name = dest_addr;
215     msg.msg_namelen = dest_len;
216     msg.msg_iov = &iov;
217     msg.msg_iovlen = 1;
218     msg.msg_control = 0;
219     
220         iov.iov_base = buffer;
221     iov.iov_len = length;
222         
223
224         // this is why we need another function to populate auio
225
226         auio.uio_iov = iov;
227         auio.uio_iovcnt = 1;
228         auio.uio_offset = 0;
229         auio.uio_resid = 0;
230         auio.uio_rw = UIO_WRITE;
231         auio.uio_proc = p;
232
233         // consider changing to send_uaio, since we care about progress.
234     error = send_iov(soc, iov, flags);
235         #endif
236 }
237
238 /* UDP and TCP has different waiting semantics
239  * UDP requires any packet to be available. 
240  * TCP requires accumulation of certain size? 
241  */
242 intreg_t sys_recvfrom(struct proc *p, int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len){
243         struct socket* sock = getsocket(p, socket);     
244         int copied = 0;
245         int returnval = 0;
246         if (sock == NULL) {
247                 set_errno(EBADF);
248                 return -1;
249         }
250         if (sock->so_type == SOCK_DGRAM){
251                 struct pbuf_head *ph = &(sock->recv_buff);
252                 struct pbuf* buf = NULL;
253                 buf = detach_pbuf(ph);
254                 if (!buf){
255                         // about to sleep
256                         sleep_on(&sock->sem);
257                         buf = detach_pbuf(ph);
258                         // Someone woke me up, there should be data..
259                         assert(buf);
260                 } else {
261                         __down_sem(&sock->sem, NULL);
262                 }
263                         copied = buf->len - sizeof(struct udp_hdr);
264                         if (copied > length)
265                                 copied = length;
266                         pbuf_header(buf, -UDP_HDR_SZ);
267                         // copy it to user space
268                         returnval = memcpy_to_user_errno(p, buffer, buf->payload, copied);
269                 }
270         if (returnval < 0) 
271                 return -1;
272         else
273                 return copied;
274 }
275
276 static int selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
277              fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out){
278         return 0;
279 }
280
281 /* TODO: Start respecting the time out value */ 
282 /* TODO: start respecting writefds and exceptfds */
283 intreg_t sys_select(struct proc *p, int nfds, fd_set *readfds, fd_set *writefds,
284                                 fd_set *exceptfds, struct timeval *timeout){
285         /* Create a semaphore */
286         struct semaphore_entry read_sem; 
287
288         init_sem(&(read_sem.sem), 0);
289
290         /* insert into the sem list of a fd / socket */
291         int low_fd = 0;
292         for (int i = low_fd; i< nfds; i++) {
293                 if(FD_ISSET(i, readfds)){
294                   struct socket* sock = getsocket(p, i);
295                         /* if the fd is not open or if the file descriptor is not a socket 
296                          * go to the next in the fd set 
297                          */
298                         if (sock == NULL) continue;
299                         /* for each file that is open, insert this semaphore to be woken up when there
300                         * is data available to be read
301                         */
302                         spin_lock(&sock->waiter_lock);
303                         LIST_INSERT_HEAD(&sock->waiters, &read_sem, link);
304                         spin_unlock(&sock->waiter_lock);
305                 }
306         }
307         /* At this point wait on the semaphore */
308   sleep_on(&(read_sem.sem));
309         /* someone woke me up, so walk through the list of descriptors and find one that is ready */
310         /* remove itself from all the lists that it is waiting on */
311         for (int i = low_fd; i<nfds; i++) {
312                 if (FD_ISSET(i, readfds)){
313                         struct socket* sock = getsocket(p,i);
314                         if (sock == NULL) continue;
315                         spin_lock(&sock->waiter_lock);
316                         LIST_REMOVE(&read_sem, link);
317                         spin_unlock(&sock->waiter_lock);
318                 }
319         }
320         fd_set readout, writeout, exceptout;
321         FD_ZERO(&readout);
322         FD_ZERO(&writeout);
323         FD_ZERO(&exceptout);
324         for (int i = low_fd; i< nfds; i ++){
325                 if (readfds && FD_ISSET(i, readfds)){
326                   struct socket* sock = getsocket(p, i);
327                         if ((sock->recv_buff).qlen > 0){
328                                 FD_SET(i, &readout);
329                         }
330                         /* if the socket is ready, then we can return it */
331                 }
332         }
333         if (readfds)
334                 memcpy(readfds, &readout, sizeof(*readfds));
335         if (writefds)
336                 memcpy(writefds, &writeout, sizeof(*writefds));
337         if (exceptfds)
338                 memcpy(readfds, &readout, sizeof(*readfds));
339
340         /* Sleep on that semaphore */
341         /* Somehow get these file descriptors to wake me up when there is new data */
342         return 0;
343 }