]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * * Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * * Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * * Neither the name of Intel Corporation nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #include <stdint.h> | |
35 | #include <stdio.h> | |
36 | #include <stdlib.h> | |
37 | #include <sys/socket.h> | |
7c673cae FG |
38 | #include <sys/time.h> |
39 | #include <sys/types.h> | |
40 | #include <unistd.h> | |
11fdf7f2 | 41 | #include <string.h> |
7c673cae FG |
42 | |
43 | #include <rte_common.h> | |
44 | #include <rte_log.h> | |
45 | ||
46 | #include "fd_man.h" | |
47 | ||
11fdf7f2 TL |
48 | #define FDPOLLERR (POLLERR | POLLHUP | POLLNVAL) |
49 | ||
7c673cae | 50 | static int |
11fdf7f2 | 51 | get_last_valid_idx(struct fdset *pfdset, int last_valid_idx) |
7c673cae FG |
52 | { |
53 | int i; | |
54 | ||
11fdf7f2 | 55 | for (i = last_valid_idx; i >= 0 && pfdset->fd[i].fd == -1; i--) |
7c673cae FG |
56 | ; |
57 | ||
11fdf7f2 | 58 | return i; |
7c673cae FG |
59 | } |
60 | ||
11fdf7f2 TL |
61 | static void |
62 | fdset_move(struct fdset *pfdset, int dst, int src) | |
7c673cae | 63 | { |
11fdf7f2 TL |
64 | pfdset->fd[dst] = pfdset->fd[src]; |
65 | pfdset->rwfds[dst] = pfdset->rwfds[src]; | |
7c673cae FG |
66 | } |
67 | ||
11fdf7f2 TL |
68 | static void |
69 | fdset_shrink_nolock(struct fdset *pfdset) | |
7c673cae | 70 | { |
11fdf7f2 TL |
71 | int i; |
72 | int last_valid_idx = get_last_valid_idx(pfdset, pfdset->num - 1); | |
7c673cae | 73 | |
11fdf7f2 TL |
74 | for (i = 0; i < last_valid_idx; i++) { |
75 | if (pfdset->fd[i].fd != -1) | |
76 | continue; | |
7c673cae | 77 | |
11fdf7f2 TL |
78 | fdset_move(pfdset, i, last_valid_idx); |
79 | last_valid_idx = get_last_valid_idx(pfdset, last_valid_idx - 1); | |
80 | } | |
81 | pfdset->num = last_valid_idx + 1; | |
82 | } | |
7c673cae | 83 | |
11fdf7f2 TL |
84 | /* |
85 | * Find deleted fd entries and remove them | |
86 | */ | |
87 | static void | |
88 | fdset_shrink(struct fdset *pfdset) | |
89 | { | |
90 | pthread_mutex_lock(&pfdset->fd_mutex); | |
91 | fdset_shrink_nolock(pfdset); | |
92 | pthread_mutex_unlock(&pfdset->fd_mutex); | |
7c673cae FG |
93 | } |
94 | ||
95 | /** | |
11fdf7f2 | 96 | * Returns the index in the fdset for a given fd. |
7c673cae | 97 | * @return |
11fdf7f2 | 98 | * index for the fd, or -1 if fd isn't in the fdset. |
7c673cae FG |
99 | */ |
100 | static int | |
11fdf7f2 | 101 | fdset_find_fd(struct fdset *pfdset, int fd) |
7c673cae | 102 | { |
11fdf7f2 | 103 | int i; |
7c673cae | 104 | |
11fdf7f2 TL |
105 | for (i = 0; i < pfdset->num && pfdset->fd[i].fd != fd; i++) |
106 | ; | |
7c673cae | 107 | |
11fdf7f2 TL |
108 | return i == pfdset->num ? -1 : i; |
109 | } | |
110 | ||
111 | static void | |
112 | fdset_add_fd(struct fdset *pfdset, int idx, int fd, | |
113 | fd_cb rcb, fd_cb wcb, void *dat) | |
114 | { | |
115 | struct fdentry *pfdentry = &pfdset->fd[idx]; | |
116 | struct pollfd *pfd = &pfdset->rwfds[idx]; | |
117 | ||
118 | pfdentry->fd = fd; | |
119 | pfdentry->rcb = rcb; | |
120 | pfdentry->wcb = wcb; | |
121 | pfdentry->dat = dat; | |
122 | ||
123 | pfd->fd = fd; | |
124 | pfd->events = rcb ? POLLIN : 0; | |
125 | pfd->events |= wcb ? POLLOUT : 0; | |
126 | pfd->revents = 0; | |
7c673cae FG |
127 | } |
128 | ||
129 | void | |
130 | fdset_init(struct fdset *pfdset) | |
131 | { | |
132 | int i; | |
133 | ||
134 | if (pfdset == NULL) | |
135 | return; | |
136 | ||
137 | for (i = 0; i < MAX_FDS; i++) { | |
138 | pfdset->fd[i].fd = -1; | |
139 | pfdset->fd[i].dat = NULL; | |
140 | } | |
141 | pfdset->num = 0; | |
142 | } | |
143 | ||
144 | /** | |
145 | * Register the fd in the fdset with read/write handler and context. | |
146 | */ | |
147 | int | |
148 | fdset_add(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat) | |
149 | { | |
150 | int i; | |
151 | ||
152 | if (pfdset == NULL || fd == -1) | |
153 | return -1; | |
154 | ||
155 | pthread_mutex_lock(&pfdset->fd_mutex); | |
11fdf7f2 TL |
156 | i = pfdset->num < MAX_FDS ? pfdset->num++ : -1; |
157 | if (i == -1) { | |
158 | fdset_shrink_nolock(pfdset); | |
159 | i = pfdset->num < MAX_FDS ? pfdset->num++ : -1; | |
160 | if (i == -1) { | |
161 | pthread_mutex_unlock(&pfdset->fd_mutex); | |
162 | return -2; | |
163 | } | |
7c673cae FG |
164 | } |
165 | ||
11fdf7f2 | 166 | fdset_add_fd(pfdset, i, fd, rcb, wcb, dat); |
7c673cae FG |
167 | pthread_mutex_unlock(&pfdset->fd_mutex); |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | /** | |
173 | * Unregister the fd from the fdset. | |
174 | * Returns context of a given fd or NULL. | |
175 | */ | |
176 | void * | |
177 | fdset_del(struct fdset *pfdset, int fd) | |
178 | { | |
179 | int i; | |
180 | void *dat = NULL; | |
181 | ||
182 | if (pfdset == NULL || fd == -1) | |
183 | return NULL; | |
184 | ||
185 | do { | |
186 | pthread_mutex_lock(&pfdset->fd_mutex); | |
187 | ||
188 | i = fdset_find_fd(pfdset, fd); | |
189 | if (i != -1 && pfdset->fd[i].busy == 0) { | |
190 | /* busy indicates r/wcb is executing! */ | |
191 | dat = pfdset->fd[i].dat; | |
192 | pfdset->fd[i].fd = -1; | |
193 | pfdset->fd[i].rcb = pfdset->fd[i].wcb = NULL; | |
194 | pfdset->fd[i].dat = NULL; | |
7c673cae FG |
195 | i = -1; |
196 | } | |
197 | pthread_mutex_unlock(&pfdset->fd_mutex); | |
198 | } while (i != -1); | |
199 | ||
200 | return dat; | |
201 | } | |
202 | ||
7c673cae FG |
203 | |
204 | /** | |
205 | * This functions runs in infinite blocking loop until there is no fd in | |
206 | * pfdset. It calls corresponding r/w handler if there is event on the fd. | |
207 | * | |
208 | * Before the callback is called, we set the flag to busy status; If other | |
209 | * thread(now rte_vhost_driver_unregister) calls fdset_del concurrently, it | |
210 | * will wait until the flag is reset to zero(which indicates the callback is | |
211 | * finished), then it could free the context after fdset_del. | |
212 | */ | |
11fdf7f2 TL |
213 | void * |
214 | fdset_event_dispatch(void *arg) | |
7c673cae | 215 | { |
11fdf7f2 TL |
216 | int i; |
217 | struct pollfd *pfd; | |
7c673cae | 218 | struct fdentry *pfdentry; |
7c673cae FG |
219 | fd_cb rcb, wcb; |
220 | void *dat; | |
11fdf7f2 | 221 | int fd, numfds; |
7c673cae | 222 | int remove1, remove2; |
11fdf7f2 TL |
223 | int need_shrink; |
224 | struct fdset *pfdset = arg; | |
7c673cae FG |
225 | |
226 | if (pfdset == NULL) | |
11fdf7f2 | 227 | return NULL; |
7c673cae FG |
228 | |
229 | while (1) { | |
7c673cae FG |
230 | |
231 | /* | |
11fdf7f2 | 232 | * When poll is blocked, other threads might unregister |
7c673cae | 233 | * listenfds from and register new listenfds into fdset. |
11fdf7f2 | 234 | * When poll returns, the entries for listenfds in the fdset |
7c673cae FG |
235 | * might have been updated. It is ok if there is unwanted call |
236 | * for new listenfds. | |
237 | */ | |
11fdf7f2 TL |
238 | pthread_mutex_lock(&pfdset->fd_mutex); |
239 | numfds = pfdset->num; | |
240 | pthread_mutex_unlock(&pfdset->fd_mutex); | |
7c673cae | 241 | |
11fdf7f2 TL |
242 | poll(pfdset->rwfds, numfds, 1000 /* millisecs */); |
243 | ||
244 | need_shrink = 0; | |
245 | for (i = 0; i < numfds; i++) { | |
7c673cae | 246 | pthread_mutex_lock(&pfdset->fd_mutex); |
11fdf7f2 | 247 | |
7c673cae FG |
248 | pfdentry = &pfdset->fd[i]; |
249 | fd = pfdentry->fd; | |
11fdf7f2 TL |
250 | pfd = &pfdset->rwfds[i]; |
251 | ||
252 | if (fd < 0) { | |
253 | need_shrink = 1; | |
254 | pthread_mutex_unlock(&pfdset->fd_mutex); | |
255 | continue; | |
256 | } | |
257 | ||
258 | if (!pfd->revents) { | |
259 | pthread_mutex_unlock(&pfdset->fd_mutex); | |
260 | continue; | |
261 | } | |
262 | ||
263 | remove1 = remove2 = 0; | |
264 | ||
7c673cae FG |
265 | rcb = pfdentry->rcb; |
266 | wcb = pfdentry->wcb; | |
267 | dat = pfdentry->dat; | |
268 | pfdentry->busy = 1; | |
11fdf7f2 | 269 | |
7c673cae | 270 | pthread_mutex_unlock(&pfdset->fd_mutex); |
11fdf7f2 TL |
271 | |
272 | if (rcb && pfd->revents & (POLLIN | FDPOLLERR)) | |
7c673cae | 273 | rcb(fd, dat, &remove1); |
11fdf7f2 | 274 | if (wcb && pfd->revents & (POLLOUT | FDPOLLERR)) |
7c673cae FG |
275 | wcb(fd, dat, &remove2); |
276 | pfdentry->busy = 0; | |
277 | /* | |
278 | * fdset_del needs to check busy flag. | |
279 | * We don't allow fdset_del to be called in callback | |
280 | * directly. | |
281 | */ | |
282 | /* | |
283 | * When we are to clean up the fd from fdset, | |
284 | * because the fd is closed in the cb, | |
285 | * the old fd val could be reused by when creates new | |
286 | * listen fd in another thread, we couldn't call | |
287 | * fd_set_del. | |
288 | */ | |
11fdf7f2 TL |
289 | if (remove1 || remove2) { |
290 | pfdentry->fd = -1; | |
291 | need_shrink = 1; | |
292 | } | |
7c673cae | 293 | } |
11fdf7f2 TL |
294 | |
295 | if (need_shrink) | |
296 | fdset_shrink(pfdset); | |
7c673cae | 297 | } |
11fdf7f2 TL |
298 | |
299 | return NULL; | |
7c673cae | 300 | } |