New upstream version 18.02
[deb_dpdk.git] / lib / librte_vhost / fd_man.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/socket.h>
9 #include <sys/time.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <string.h>
13
14 #include <rte_common.h>
15 #include <rte_log.h>
16
17 #include "fd_man.h"
18
19 #define FDPOLLERR (POLLERR | POLLHUP | POLLNVAL)
20
21 static int
22 get_last_valid_idx(struct fdset *pfdset, int last_valid_idx)
23 {
24         int i;
25
26         for (i = last_valid_idx; i >= 0 && pfdset->fd[i].fd == -1; i--)
27                 ;
28
29         return i;
30 }
31
32 static void
33 fdset_move(struct fdset *pfdset, int dst, int src)
34 {
35         pfdset->fd[dst]    = pfdset->fd[src];
36         pfdset->rwfds[dst] = pfdset->rwfds[src];
37 }
38
39 static void
40 fdset_shrink_nolock(struct fdset *pfdset)
41 {
42         int i;
43         int last_valid_idx = get_last_valid_idx(pfdset, pfdset->num - 1);
44
45         for (i = 0; i < last_valid_idx; i++) {
46                 if (pfdset->fd[i].fd != -1)
47                         continue;
48
49                 fdset_move(pfdset, i, last_valid_idx);
50                 last_valid_idx = get_last_valid_idx(pfdset, last_valid_idx - 1);
51         }
52         pfdset->num = last_valid_idx + 1;
53 }
54
55 /*
56  * Find deleted fd entries and remove them
57  */
58 static void
59 fdset_shrink(struct fdset *pfdset)
60 {
61         pthread_mutex_lock(&pfdset->fd_mutex);
62         fdset_shrink_nolock(pfdset);
63         pthread_mutex_unlock(&pfdset->fd_mutex);
64 }
65
66 /**
67  * Returns the index in the fdset for a given fd.
68  * @return
69  *   index for the fd, or -1 if fd isn't in the fdset.
70  */
71 static int
72 fdset_find_fd(struct fdset *pfdset, int fd)
73 {
74         int i;
75
76         for (i = 0; i < pfdset->num && pfdset->fd[i].fd != fd; i++)
77                 ;
78
79         return i == pfdset->num ? -1 : i;
80 }
81
82 static void
83 fdset_add_fd(struct fdset *pfdset, int idx, int fd,
84         fd_cb rcb, fd_cb wcb, void *dat)
85 {
86         struct fdentry *pfdentry = &pfdset->fd[idx];
87         struct pollfd *pfd = &pfdset->rwfds[idx];
88
89         pfdentry->fd  = fd;
90         pfdentry->rcb = rcb;
91         pfdentry->wcb = wcb;
92         pfdentry->dat = dat;
93
94         pfd->fd = fd;
95         pfd->events  = rcb ? POLLIN : 0;
96         pfd->events |= wcb ? POLLOUT : 0;
97         pfd->revents = 0;
98 }
99
100 void
101 fdset_init(struct fdset *pfdset)
102 {
103         int i;
104
105         if (pfdset == NULL)
106                 return;
107
108         for (i = 0; i < MAX_FDS; i++) {
109                 pfdset->fd[i].fd = -1;
110                 pfdset->fd[i].dat = NULL;
111         }
112         pfdset->num = 0;
113 }
114
115 /**
116  * Register the fd in the fdset with read/write handler and context.
117  */
118 int
119 fdset_add(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
120 {
121         int i;
122
123         if (pfdset == NULL || fd == -1)
124                 return -1;
125
126         pthread_mutex_lock(&pfdset->fd_mutex);
127         i = pfdset->num < MAX_FDS ? pfdset->num++ : -1;
128         if (i == -1) {
129                 fdset_shrink_nolock(pfdset);
130                 i = pfdset->num < MAX_FDS ? pfdset->num++ : -1;
131                 if (i == -1) {
132                         pthread_mutex_unlock(&pfdset->fd_mutex);
133                         return -2;
134                 }
135         }
136
137         fdset_add_fd(pfdset, i, fd, rcb, wcb, dat);
138         pthread_mutex_unlock(&pfdset->fd_mutex);
139
140         return 0;
141 }
142
143 /**
144  *  Unregister the fd from the fdset.
145  *  Returns context of a given fd or NULL.
146  */
147 void *
148 fdset_del(struct fdset *pfdset, int fd)
149 {
150         int i;
151         void *dat = NULL;
152
153         if (pfdset == NULL || fd == -1)
154                 return NULL;
155
156         do {
157                 pthread_mutex_lock(&pfdset->fd_mutex);
158
159                 i = fdset_find_fd(pfdset, fd);
160                 if (i != -1 && pfdset->fd[i].busy == 0) {
161                         /* busy indicates r/wcb is executing! */
162                         dat = pfdset->fd[i].dat;
163                         pfdset->fd[i].fd = -1;
164                         pfdset->fd[i].rcb = pfdset->fd[i].wcb = NULL;
165                         pfdset->fd[i].dat = NULL;
166                         i = -1;
167                 }
168                 pthread_mutex_unlock(&pfdset->fd_mutex);
169         } while (i != -1);
170
171         return dat;
172 }
173
174
175 /**
176  * This functions runs in infinite blocking loop until there is no fd in
177  * pfdset. It calls corresponding r/w handler if there is event on the fd.
178  *
179  * Before the callback is called, we set the flag to busy status; If other
180  * thread(now rte_vhost_driver_unregister) calls fdset_del concurrently, it
181  * will wait until the flag is reset to zero(which indicates the callback is
182  * finished), then it could free the context after fdset_del.
183  */
184 void *
185 fdset_event_dispatch(void *arg)
186 {
187         int i;
188         struct pollfd *pfd;
189         struct fdentry *pfdentry;
190         fd_cb rcb, wcb;
191         void *dat;
192         int fd, numfds;
193         int remove1, remove2;
194         int need_shrink;
195         struct fdset *pfdset = arg;
196         int val;
197
198         if (pfdset == NULL)
199                 return NULL;
200
201         while (1) {
202
203                 /*
204                  * When poll is blocked, other threads might unregister
205                  * listenfds from and register new listenfds into fdset.
206                  * When poll returns, the entries for listenfds in the fdset
207                  * might have been updated. It is ok if there is unwanted call
208                  * for new listenfds.
209                  */
210                 pthread_mutex_lock(&pfdset->fd_mutex);
211                 numfds = pfdset->num;
212                 pthread_mutex_unlock(&pfdset->fd_mutex);
213
214                 val = poll(pfdset->rwfds, numfds, 1000 /* millisecs */);
215                 if (val < 0)
216                         continue;
217
218                 need_shrink = 0;
219                 for (i = 0; i < numfds; i++) {
220                         pthread_mutex_lock(&pfdset->fd_mutex);
221
222                         pfdentry = &pfdset->fd[i];
223                         fd = pfdentry->fd;
224                         pfd = &pfdset->rwfds[i];
225
226                         if (fd < 0) {
227                                 need_shrink = 1;
228                                 pthread_mutex_unlock(&pfdset->fd_mutex);
229                                 continue;
230                         }
231
232                         if (!pfd->revents) {
233                                 pthread_mutex_unlock(&pfdset->fd_mutex);
234                                 continue;
235                         }
236
237                         remove1 = remove2 = 0;
238
239                         rcb = pfdentry->rcb;
240                         wcb = pfdentry->wcb;
241                         dat = pfdentry->dat;
242                         pfdentry->busy = 1;
243
244                         pthread_mutex_unlock(&pfdset->fd_mutex);
245
246                         if (rcb && pfd->revents & (POLLIN | FDPOLLERR))
247                                 rcb(fd, dat, &remove1);
248                         if (wcb && pfd->revents & (POLLOUT | FDPOLLERR))
249                                 wcb(fd, dat, &remove2);
250                         pfdentry->busy = 0;
251                         /*
252                          * fdset_del needs to check busy flag.
253                          * We don't allow fdset_del to be called in callback
254                          * directly.
255                          */
256                         /*
257                          * When we are to clean up the fd from fdset,
258                          * because the fd is closed in the cb,
259                          * the old fd val could be reused by when creates new
260                          * listen fd in another thread, we couldn't call
261                          * fd_set_del.
262                          */
263                         if (remove1 || remove2) {
264                                 pfdentry->fd = -1;
265                                 need_shrink = 1;
266                         }
267                 }
268
269                 if (need_shrink)
270                         fdset_shrink(pfdset);
271         }
272
273         return NULL;
274 }