1 /*
2 * include/haproxy/port_range.h
3 * This file defines everything needed to manage port ranges
4 *
5 * Copyright (C) 2000-2020 Willy Tarreau - w@1wt.eu
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation, version 2.1
10 * exclusively.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #ifndef _HAPROXY_PORT_RANGE_H
23 #define _HAPROXY_PORT_RANGE_H
24
25 #include <stdlib.h>
26 #include <haproxy/api.h>
27 #include <haproxy/port_range-t.h>
28
29 #define GET_NEXT_OFF(range, off) ((off) == (range)->size - 1 ? 0 : (off) + 1)
30
31 /* return an available port from range <range>, or zero if none is left */
port_range_alloc_port(struct port_range * range)32 static inline int port_range_alloc_port(struct port_range *range)
33 {
34 int ret;
35 int get;
36 int put;
37
38 get = _HA_ATOMIC_LOAD(&range->get);
39 do {
40 /* barrier ot make sure get is loaded before put */
41 __ha_barrier_atomic_load();
42 put = _HA_ATOMIC_LOAD(&range->put_t);
43 if (unlikely(put == get))
44 return 0;
45 ret = range->ports[get];
46 } while (!(_HA_ATOMIC_CAS(&range->get, &get, GET_NEXT_OFF(range, get))));
47 return ret;
48 }
49
50 /* release port <port> into port range <range>. Does nothing if <port> is zero
51 * nor if <range> is null. The caller is responsible for marking the port
52 * unused by either setting the port to zero or the range to NULL.
53 */
port_range_release_port(struct port_range * range,int port)54 static inline void port_range_release_port(struct port_range *range, int port)
55 {
56 int put;
57
58 if (!port || !range)
59 return;
60
61 put = range->put_h;
62 /* put_h is reserved for producers, so that they can each get a
63 * free slot, put_t is what is used by consumers to know if there's
64 * elements available or not
65 */
66 /* First reserve or slot, we know the ring buffer can't be full,
67 * as we will only ever release port we allocated before
68 */
69 while (!(_HA_ATOMIC_CAS(&range->put_h, &put, GET_NEXT_OFF(range, put))));
70 _HA_ATOMIC_STORE(&range->ports[put], port);
71 /* Barrier to make sure the new port is visible before we change put_t */
72 __ha_barrier_atomic_store();
73 /* Wait until all the threads that got a slot before us are done */
74 while ((volatile int)range->put_t != put)
75 __ha_compiler_barrier();
76 /* Let the world know we're done, and any potential consumer they
77 * can use that port.
78 */
79 _HA_ATOMIC_STORE(&range->put_t, GET_NEXT_OFF(range, put));
80 }
81
82 /* return a new initialized port range of N ports. The ports are not
83 * filled in, it's up to the caller to do it.
84 */
port_range_alloc_range(int n)85 static inline struct port_range *port_range_alloc_range(int n)
86 {
87 struct port_range *ret;
88 ret = calloc(1, sizeof(struct port_range) +
89 (n + 1) * sizeof(((struct port_range *)0)->ports[0]));
90 if (!ret)
91 return NULL;
92 ret->size = n + 1;
93 /* Start at the first free element */
94 ret->put_h = ret->put_t = n;
95 return ret;
96 }
97
98 #endif /* _HAPROXY_PORT_RANGE_H */
99
100 /*
101 * Local variables:
102 * c-indent-level: 8
103 * c-basic-offset: 8
104 * End:
105 */
106