TAS
TCP Acceleration as an OS Service
manage_fd.c
1 /*
2  * Copyright 2019 University of Washington, Max Planck Institute for
3  * Software Systems, and The University of Texas at Austin
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <utils.h>
30 #include <unistd.h>
31 #include <sys/eventfd.h>
32 
33 #include "internal.h"
34 #include <tas_sockets.h>
35 
36 #define MAXSOCK 1024 * 1024
37 
38 enum fh_type {
39  FH_UNUSED,
40  FH_SOCKET,
41  FH_EPOLL,
42 };
43 
44 struct filehandle {
45  union {
46  struct socket *s;
47  struct epoll *e;
48  } data;
49  uint8_t type;
50 };
51 
52 static struct filehandle fhs[MAXSOCK];
53 
54 int flextcp_fd_init(void)
55 {
56  return 0;
57 }
58 
59 
60 int flextcp_fd_salloc(struct socket **ps)
61 {
62  struct socket *s;
63  int fd;
64 
65  if ((s = calloc(1, sizeof(*s))) == NULL) {
66  errno = ENOMEM;
67  return -1;
68  }
69 
70  /* get eventfd so we reserve the FD in the kernel to avoid overlap */
71  if ((fd = eventfd(0, 0)) < 0) {
72  free(s);
73  return -1;
74  }
75 
76  /* no more file handles available */
77  if (fd >= MAXSOCK) {
78  free(s);
79  tas_libc_close(fd);
80  errno = EMFILE;
81  return -1;
82  }
83 
84  s->type = SOCK_SOCKET;
85  s->refcnt = 1;
86  s->sp_lock = 1;
87 
88  fhs[fd].data.s = s;
89  fhs[fd].type = FH_SOCKET;
90 
91  *ps = s;
92 
93  return fd;
94 }
95 
96 int flextcp_fd_slookup(int fd, struct socket **ps)
97 {
98  struct socket *s;
99 
100  if (fd >= MAXSOCK || fhs[fd].type != FH_SOCKET) {
101  errno = EBADF;
102  return -1;
103  }
104 
105  s = fhs[fd].data.s;
106  socket_lock(s);
107  *ps = s;
108  return 0;
109 }
110 
111 int flextcp_fd_ealloc(struct epoll **pe, int fd)
112 {
113  struct epoll *e;
114 
115  /* no more file handles available */
116  if (fd >= MAXSOCK) {
117  errno = EMFILE;
118  return -1;
119  }
120 
121  assert(fhs[fd].type == FH_UNUSED);
122 
123  if ((e = calloc(1, sizeof(*e))) == NULL) {
124  errno = ENOMEM;
125  return -1;
126  }
127 
128  e->refcnt = 1;
129  e->sp_lock = 1;
130 
131  fhs[fd].data.e = e;
132  fhs[fd].type = FH_EPOLL;
133 
134  *pe = e;
135 
136  return fd;
137 }
138 
139 int flextcp_fd_elookup(int fd, struct epoll **pe)
140 {
141  struct epoll *e;
142 
143  if (fd >= MAXSOCK || fhs[fd].type != FH_EPOLL) {
144  errno = EBADF;
145  return -1;
146  }
147 
148  e = fhs[fd].data.e;
149  epoll_lock(e);
150  *pe = e;
151  return 0;
152 }
153 
154 void flextcp_fd_srelease(int fd, struct socket *s)
155 {
156  socket_unlock(s);
157 }
158 
159 void flextcp_fd_erelease(int fd, struct epoll *e)
160 {
161  epoll_unlock(e);
162 }
163 
164 void flextcp_fd_close(int fd)
165 {
166  assert(fhs[fd].type == FH_SOCKET || fhs[fd].type == FH_EPOLL);
167  if (fhs[fd].type == FH_SOCKET) {
168  fhs[fd].data.s->refcnt--;
169  fhs[fd].data.s = NULL;
170  } else if (fhs[fd].type == FH_EPOLL) {
171  fhs[fd].data.e->refcnt--;
172  fhs[fd].data.e = NULL;
173  } else {
174  fprintf(stderr, "flextcp_fd_close: trying to close non-opened tas fd\n");
175  abort();
176  }
177 
178  fhs[fd].type = FH_UNUSED;
179  MEM_BARRIER();
180  tas_libc_close(fd);
181 }
182 
183 /* do the tas-internal part of duping oldfd to newfd, after the linux fds have
184  * already been dup'd */
185 static inline int internal_dup3(int oldfd, int newfd, int flags)
186 {
187  struct socket *s;
188  struct epoll *ep;
189 
190  /* TODO: check flags */
191 
192  if (newfd >= MAXSOCK) {
193  fprintf(stderr, "tas_dup: failed because new fd is larger than MAXSOCK\n");
194  abort();
195  }
196 
197  /* close any previous socket or epoll at newfd */
198  if (fhs[newfd].type == FH_SOCKET) {
199  s = fhs[newfd].data.s;
200 
201  /* close socket */
202  socket_lock(s);
203  s->refcnt--;
204  if (s->refcnt == 0)
205  tas_sock_close(s);
206  else
207  socket_unlock(s);
208 
209  fhs[newfd].data.s = NULL;
210  fhs[newfd].type = FH_UNUSED;
211  } else if (fhs[newfd].type == FH_EPOLL) {
212  ep = fhs[newfd].data.e;
213 
214  /* close epoll */
215  epoll_lock(ep);
216  ep->refcnt--;
217  if (ep->refcnt == 0)
218  flextcp_epoll_destroy(ep);
219  else
220  epoll_unlock(ep);
221 
222  fhs[newfd].data.e = NULL;
223  fhs[newfd].type = FH_UNUSED;
224  }
225 
226  /* next dup the underlying TAS socket and epoll if necessary */
227  if (flextcp_fd_slookup(oldfd, &s) == 0) {
228  /* oldfd is a tas socket */
229  fhs[newfd].type = FH_SOCKET;
230  fhs[newfd].data.s = s;
231 
232  s->refcnt++;
233 
234  flextcp_fd_srelease(oldfd, s);
235  } else if (flextcp_fd_elookup(oldfd, &ep) == 0) {
236  /* oldfd is a tas epoll */
237  fhs[newfd].type = FH_EPOLL;
238  fhs[newfd].data.e = ep;
239 
240  ep->refcnt++;
241 
242  flextcp_fd_srelease(oldfd, s);
243  }
244 
245  return newfd;
246 }
247 
248 int tas_dup(int oldfd)
249 {
250  int newfd;
251 
252  /* either way we want to dup the linux fd */
253  newfd = tas_libc_dup(oldfd);
254  if (newfd < 0)
255  return newfd;
256 
257  return internal_dup3(oldfd, newfd, 0);
258 }
259 
260 int tas_dup2(int oldfd, int newfd)
261 {
262  /* either way we want to dup the linux fd */
263  newfd = tas_libc_dup2(oldfd, newfd);
264  if (newfd < 0)
265  return newfd;
266 
267  /* for dup2 this acts as a nop */
268  if (newfd == oldfd)
269  return newfd;
270 
271  return internal_dup3(oldfd, newfd, 0);
272 }
273 
274 int tas_dup3(int oldfd, int newfd, int flags)
275 {
276  /* either way we want to dup the linux fd */
277  newfd = tas_libc_dup3(oldfd, newfd, flags);
278  if (newfd < 0)
279  return newfd;
280 
281  return internal_dup3(oldfd, newfd, flags);
282 }
TAS sockets emulation.